stario/fn.go

112 lines
3.1 KiB
Go
Raw Normal View History

2021-09-01 11:03:02 +08:00
package stario
import (
"context"
2021-09-01 11:03:02 +08:00
"errors"
"time"
)
// 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")
// 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() {
finished <- fn(ctx)
2021-09-01 11:03:02 +08:00
}()
select {
case err := <-finished:
2021-09-01 11:03:02 +08:00
return err
case <-ctx.Done():
return ctx.Err()
2021-09-01 11:03:02 +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() {
result <- WaitUntilContext(ctx, fn)
close(result)
2021-09-01 11:03:02 +08:00
}()
return result
2021-09-01 11:03:02 +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 {
result := make(chan error, 1)
2021-09-01 11:03:02 +08:00
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
2021-09-01 11:03:02 +08:00
}
result <- err
close(result)
2021-09-01 11:03:02 +08:00
}()
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
}