70 lines
1.3 KiB
Go
70 lines
1.3 KiB
Go
package cancel
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/FAU-CDI/wisski-distillery/pkg/timex"
|
|
)
|
|
|
|
// Anyways behaves like context.WithTimeout, except that if the Done() channel of ctx is closed before Anyways is called, the returned context's Done() channel is only closed after timeout.
|
|
func Anyways(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) {
|
|
// context is not yet cancelled => return as-is
|
|
if err := ctx.Err(); err == nil {
|
|
return context.WithTimeout(ctx, timeout)
|
|
}
|
|
|
|
// create a new anyways
|
|
any := &anyways{
|
|
done: make(chan struct{}),
|
|
parent: ctx,
|
|
deadline: time.Now().Add(timeout),
|
|
}
|
|
|
|
// start waiting for the timer (or the cancel to be called)
|
|
finish := make(chan struct{})
|
|
go func() {
|
|
t := timex.NewTimer()
|
|
t.Reset(timeout)
|
|
defer timex.ReleaseTimer(t)
|
|
|
|
defer close(any.done)
|
|
|
|
select {
|
|
case <-t.C:
|
|
case <-finish:
|
|
}
|
|
}()
|
|
|
|
return any, func() {
|
|
close(finish)
|
|
}
|
|
|
|
}
|
|
|
|
type anyways struct {
|
|
done chan struct{}
|
|
|
|
parent context.Context
|
|
deadline time.Time
|
|
}
|
|
|
|
func (a anyways) Deadline() (deadline time.Time, ok bool) {
|
|
return a.deadline, true
|
|
}
|
|
|
|
func (a anyways) Done() <-chan struct{} {
|
|
return a.done
|
|
}
|
|
func (a anyways) Err() error {
|
|
select {
|
|
case <-a.done:
|
|
return context.DeadlineExceeded
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (a anyways) Value(key any) any {
|
|
return a.parent.Done()
|
|
}
|