github.com/haraldrudell/parl@v0.4.176/ptime/on-ticker-thread.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package ptime
     7  
     8  import (
     9  	"context"
    10  	"time"
    11  
    12  	"github.com/haraldrudell/parl/internal/cyclebreaker"
    13  	"github.com/haraldrudell/parl/perrors"
    14  )
    15  
    16  type Go interface {
    17  	Done(err *error)
    18  	Context() (ctx context.Context)
    19  }
    20  
    21  // OnTickerThread is a goroutine that invokes a callback periodically
    22  //   - period must be greater than zero or panic
    23  //   - loc is optional time zone, default time.Local, other time zone is time.UTC
    24  //   - — time zone matters for periods longer than 1 hour or time-zone offiset minutes
    25  //   - g can be nil, in which panics are echoed to standard error.
    26  //     The thread is then not awaitable or cancelable
    27  //   - OnTickerThread is a time.Ticker using on-period multiples that invokes
    28  //     a callback periodically without further action
    29  //   - OnTickerThread eliminates channel receive actions at
    30  //     the cost of an additional OnTickerThread thread
    31  //   - a thread-less on-period ticker is OnTicker
    32  func OnTickerThread(callback func(at time.Time), period time.Duration, loc *time.Location, g Go) {
    33  	var err error
    34  	if g != nil {
    35  		defer g.Done(&err)
    36  		defer cyclebreaker.Recover(func() cyclebreaker.DA { return cyclebreaker.A() }, &err, cyclebreaker.NoOnError)
    37  	} else {
    38  		defer cyclebreaker.Recover(func() cyclebreaker.DA { return cyclebreaker.A() }, &err, cyclebreaker.Infallible)
    39  	}
    40  
    41  	if callback == nil {
    42  		err = perrors.NewPF("callback cannot be nil")
    43  		return
    44  	}
    45  
    46  	var ticker = NewOnTicker(period, loc)
    47  	defer ticker.Stop()
    48  
    49  	C := ticker.C
    50  	var done <-chan struct{}
    51  	if g != nil {
    52  		done = g.Context().Done()
    53  	}
    54  	var t time.Time
    55  	for {
    56  		select {
    57  		case <-done:
    58  			return // context canceled return
    59  		case t = <-C:
    60  		}
    61  
    62  		callback(t)
    63  	}
    64  }