package stario import ( "context" "errors" "time" ) // ERR_TIMEOUT is the legacy timeout sentinel used by WaitUntilTimeout*. var ERR_TIMEOUT = errors.New("TIME OUT") // WaitUntilContext runs fn and returns either its result or the context error, // whichever happens first. func WaitUntilContext(ctx context.Context, fn func(context.Context) error) error { if ctx == nil { ctx = context.Background() } finished := make(chan error, 1) go func() { finished <- fn(ctx) }() select { case err := <-finished: return err case <-ctx.Done(): return ctx.Err() } } // WaitUntilContextFinished is the asynchronous form of WaitUntilContext. func WaitUntilContextFinished(ctx context.Context, fn func(context.Context) error) <-chan error { result := make(chan error, 1) go func() { result <- WaitUntilContext(ctx, fn) close(result) }() return result } // WaitUntilContextDone adapts a done-channel worker to a context-based wait. func WaitUntilContextDone(ctx context.Context, fn func(<-chan struct{}) error) error { if ctx == nil { ctx = context.Background() } return WaitUntilContext(ctx, func(context.Context) error { return fn(ctx.Done()) }) } // WaitUntilContextDoneFinished is the asynchronous form of WaitUntilContextDone. func WaitUntilContextDoneFinished(ctx context.Context, fn func(<-chan struct{}) error) <-chan error { return WaitUntilContextFinished(ctx, func(ctx context.Context) error { return fn(ctx.Done()) }) } // WaitUntilTimeout is a legacy timeout helper kept for compatibility. // // The provided stop channel must be treated as receive-only by callers. New // code should prefer WaitUntilContext or WaitUntilContextDone. func WaitUntilTimeout(tm time.Duration, fn func(chan struct{}) error) error { ctx, cancel := context.WithTimeout(context.Background(), tm) defer cancel() err := WaitUntilContextDone(ctx, func(done <-chan struct{}) error { return fn(bridgeDoneChan(done)) }) if errors.Is(err, context.DeadlineExceeded) { return ERR_TIMEOUT } return err } // WaitUntilFinished runs fn asynchronously and returns its eventual result. func WaitUntilFinished(fn func() error) <-chan error { return WaitUntilContextFinished(context.Background(), func(ctx context.Context) error { return fn() }) } // WaitUntilTimeoutFinished is the asynchronous form of WaitUntilTimeout. // // The provided stop channel must be treated as receive-only by callers. New // code should prefer WaitUntilContextFinished or WaitUntilContextDoneFinished. func WaitUntilTimeoutFinished(tm time.Duration, fn func(chan struct{}) error) <-chan error { result := make(chan error, 1) go func() { ctx, cancel := context.WithTimeout(context.Background(), tm) defer cancel() err := WaitUntilContextDone(ctx, func(done <-chan struct{}) error { return fn(bridgeDoneChan(done)) }) if errors.Is(err, context.DeadlineExceeded) { err = ERR_TIMEOUT } result <- err close(result) }() return result } func bridgeDoneChan(done <-chan struct{}) chan struct{} { stop := make(chan struct{}) if done == nil { return stop } go func() { <-done close(stop) }() return stop }