github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/xact/demand.go (about)

     1  // Package xact provides core functionality for the AIStore eXtended Actions (xactions).
     2  /*
     3   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package xact
     6  
     7  import (
     8  	"time"
     9  
    10  	"github.com/NVIDIA/aistore/cmn"
    11  	"github.com/NVIDIA/aistore/cmn/atomic"
    12  	"github.com/NVIDIA/aistore/cmn/cos"
    13  	"github.com/NVIDIA/aistore/cmn/debug"
    14  	"github.com/NVIDIA/aistore/cmn/mono"
    15  	"github.com/NVIDIA/aistore/core"
    16  	"github.com/NVIDIA/aistore/core/meta"
    17  	"github.com/NVIDIA/aistore/hk"
    18  )
    19  
    20  const (
    21  	IdleDefault = time.Minute // hk -> idle tick
    22  )
    23  
    24  type (
    25  	// xaction that self-terminates after staying idle for a while
    26  	// with an added capability to renew itself and ref-count its pending work
    27  	Demand interface {
    28  		core.Xact
    29  		IdleTimer() <-chan struct{}
    30  		IncPending()
    31  		DecPending()
    32  		SubPending(n int)
    33  	}
    34  	DemandBase struct {
    35  		hkName string
    36  		idle   struct {
    37  			ticks cos.StopCh
    38  			d     time.Duration // hk idle
    39  			last  atomic.Int64  // mono.NanoTime
    40  		}
    41  
    42  		Base
    43  
    44  		pending atomic.Int64
    45  		hkReg   atomic.Bool // mono.NanoTime
    46  	}
    47  )
    48  
    49  ////////////////
    50  // DemandBase //
    51  ////////////////
    52  
    53  // NOTE: override `Base.IsIdle`
    54  func (r *DemandBase) IsIdle() bool {
    55  	last := r.idle.last.Load()
    56  	return last != 0 && mono.Since(last) >= max(cmn.Rom.MaxKeepalive(), 2*time.Second)
    57  }
    58  
    59  func (r *DemandBase) Init(uuid, kind string, bck *meta.Bck, idleDur time.Duration) {
    60  	r.hkName = kind + "/" + uuid
    61  	r.idle.d = IdleDefault
    62  	if idleDur > 0 {
    63  		r.idle.d = idleDur
    64  	}
    65  	r.idle.ticks.Init()
    66  	r.InitBase(uuid, kind, bck)
    67  
    68  	r.idle.last.Store(mono.NanoTime())
    69  	r.hkReg.Store(true)
    70  	hk.Reg(r.hkName+hk.NameSuffix, r.hkcb, 0 /*time.Duration*/)
    71  }
    72  
    73  // (e.g. usage: listed last page)
    74  func (r *DemandBase) Reset(idleTime time.Duration) { r.idle.d = idleTime }
    75  
    76  func (r *DemandBase) hkcb() time.Duration {
    77  	last := r.idle.last.Load()
    78  	if last != 0 && mono.Since(last) >= r.idle.d {
    79  		// signal parent xaction to finish and exit (via `IdleTimer` chan)
    80  		r.idle.ticks.Close()
    81  	}
    82  	return r.idle.d
    83  }
    84  
    85  func (r *DemandBase) IdleTimer() <-chan struct{} { return r.idle.ticks.Listen() }
    86  func (r *DemandBase) Pending() (cnt int64)       { return r.pending.Load() }
    87  func (r *DemandBase) DecPending()                { r.SubPending(1) }
    88  
    89  func (r *DemandBase) IncPending() {
    90  	debug.Assert(r.hkReg.Load())
    91  	r.pending.Inc()
    92  	r.idle.last.Store(0)
    93  }
    94  
    95  func (r *DemandBase) SubPending(n int) {
    96  	if n == 0 {
    97  		return
    98  	}
    99  	pending := r.pending.Sub(int64(n))
   100  	debug.Assert(pending >= 0)
   101  	if pending == 0 {
   102  		r.idle.last.Store(mono.NanoTime())
   103  	}
   104  }
   105  
   106  func (r *DemandBase) Stop() {
   107  	hk.Unreg(r.hkName + hk.NameSuffix)
   108  	r.idle.ticks.Close()
   109  }
   110  
   111  func (r *DemandBase) Abort(err error) (ok bool) {
   112  	if err == nil && !r.IsIdle() {
   113  		err = cmn.NewErrAborted(r.Name(), "aborting non-idle", nil)
   114  	}
   115  	if ok = r.Base.Abort(err); ok {
   116  		r.Finish()
   117  	}
   118  	return
   119  }