2021-09-01 11:03:02 +08:00
|
|
|
package stario
|
|
|
|
|
|
|
|
|
|
import (
|
2026-04-15 14:35:19 +08:00
|
|
|
"context"
|
2021-09-01 11:03:02 +08:00
|
|
|
"errors"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-15 14:35:19 +08:00
|
|
|
// ERR_TIMEOUT is the legacy timeout sentinel used by WaitUntilTimeout*.
|
2021-09-01 11:03:02 +08:00
|
|
|
var ERR_TIMEOUT = errors.New("TIME OUT")
|
|
|
|
|
|
2026-04-15 14:35:19 +08:00
|
|
|
// 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)
|
2021-09-01 11:03:02 +08:00
|
|
|
go func() {
|
2026-04-15 14:35:19 +08:00
|
|
|
finished <- fn(ctx)
|
2021-09-01 11:03:02 +08:00
|
|
|
}()
|
|
|
|
|
select {
|
2026-04-15 14:35:19 +08:00
|
|
|
case err := <-finished:
|
2021-09-01 11:03:02 +08:00
|
|
|
return err
|
2026-04-15 14:35:19 +08:00
|
|
|
case <-ctx.Done():
|
|
|
|
|
return ctx.Err()
|
2021-09-01 11:03:02 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-15 14:35:19 +08:00
|
|
|
// WaitUntilContextFinished is the asynchronous form of WaitUntilContext.
|
|
|
|
|
func WaitUntilContextFinished(ctx context.Context, fn func(context.Context) error) <-chan error {
|
|
|
|
|
result := make(chan error, 1)
|
2021-09-01 11:03:02 +08:00
|
|
|
go func() {
|
2026-04-15 14:35:19 +08:00
|
|
|
result <- WaitUntilContext(ctx, fn)
|
|
|
|
|
close(result)
|
2021-09-01 11:03:02 +08:00
|
|
|
}()
|
2026-04-15 14:35:19 +08:00
|
|
|
return result
|
2021-09-01 11:03:02 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-15 14:35:19 +08:00
|
|
|
// 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.
|
2021-11-12 16:01:35 +08:00
|
|
|
func WaitUntilTimeoutFinished(tm time.Duration, fn func(chan struct{}) error) <-chan error {
|
2026-04-15 14:35:19 +08:00
|
|
|
result := make(chan error, 1)
|
2021-09-01 11:03:02 +08:00
|
|
|
go func() {
|
2026-04-15 14:35:19 +08:00
|
|
|
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
|
2021-09-01 11:03:02 +08:00
|
|
|
}
|
2026-04-15 14:35:19 +08:00
|
|
|
result <- err
|
|
|
|
|
close(result)
|
2021-09-01 11:03:02 +08:00
|
|
|
}()
|
|
|
|
|
return result
|
|
|
|
|
}
|
2026-04-15 14:35:19 +08:00
|
|
|
|
|
|
|
|
func bridgeDoneChan(done <-chan struct{}) chan struct{} {
|
|
|
|
|
stop := make(chan struct{})
|
|
|
|
|
if done == nil {
|
|
|
|
|
return stop
|
|
|
|
|
}
|
|
|
|
|
go func() {
|
|
|
|
|
<-done
|
|
|
|
|
close(stop)
|
|
|
|
|
}()
|
|
|
|
|
return stop
|
|
|
|
|
}
|