github.com/haraldrudell/parl@v0.4.176/once-waiter.go (about)

     1  /*
     2  © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package parl
     7  
     8  import (
     9  	"context"
    10  
    11  	"github.com/haraldrudell/parl/perrors"
    12  )
    13  
    14  // OnceWaiter allows any number of threads to wait for a single next occurrence.
    15  //   - the occurrence is trigger in any of two ways:
    16  //   - — a parent context may be passed in that on cancel triggers the wait
    17  //   - — the Cancel method is invoked
    18  //   - Ch returns a channel that sends one item on the occurrence
    19  //     but never closes
    20  //   - Done returns a channel that closes on the occurrence happens, similar
    21  //     to a context
    22  //   - Wait awaits the occurrence
    23  //   - a did-occurer object can be obtained that returns true once the cycle
    24  //     trigs.
    25  //   - a context can be obtained that cancels on the next trig
    26  //   - the cycles can be permanently canceled or trigged and rearmed
    27  type OnceWaiter struct {
    28  	ctx context.Context
    29  }
    30  
    31  // NewOnceWaiter returns a channel that will send one item
    32  // when the context cancels or immediately if the context was already canceled.
    33  func NewOnceWaiter(ctx context.Context) (onceReceiver *OnceWaiter) {
    34  	if ctx == nil {
    35  		panic(perrors.NewPF("ctx cannot be nil"))
    36  	}
    37  	return &OnceWaiter{
    38  		ctx: NewCancelContext(ctx),
    39  	}
    40  }
    41  
    42  // Ch returns a channel that will send one item on the occurrence.
    43  //   - the channel will not send anything else
    44  //   - the channel never closes.
    45  func (ow *OnceWaiter) Ch() (ch <-chan struct{}) {
    46  	c := make(chan struct{}, 1)
    47  	go onceWaiterSender(ow.ctx.Done(), c)
    48  	return c
    49  }
    50  
    51  // Done returns a channel that will close on the occurrence.
    52  //   - Done is similar to the Done method of a context
    53  func (ow *OnceWaiter) Done() (done <-chan struct{}) {
    54  	return ow.ctx.Done()
    55  }
    56  
    57  // Wait waits until the ocurrence
    58  func (ow *OnceWaiter) Wait() {
    59  	done := ow.Done()
    60  	<-done
    61  }
    62  
    63  // DidOccur returns true when the occurrence has taken place
    64  func (ow *OnceWaiter) DidOccur() (didOccur bool) {
    65  	return ow.ctx.Err() != nil
    66  }
    67  
    68  // Context returns a context that cancels on the occurrence
    69  func (cw *OnceWaiter) Context() (ctx context.Context) {
    70  	return NewCancelContext(cw.ctx)
    71  }
    72  
    73  // Cancel triggers the occurrence
    74  func (ow *OnceWaiter) Cancel() {
    75  	invokeCancel(ow.ctx)
    76  }
    77  
    78  func onceWaiterSender(done <-chan struct{}, ch chan<- struct{}) {
    79  	Recover(func() DA { return A() }, nil, Infallible) // panic prints to stderr
    80  
    81  	<-done
    82  	ch <- struct{}{}
    83  }