ingredient/locker: Unlock even after cancel

This commit is contained in:
Tom Wiesing 2022-12-05 14:01:53 +01:00
parent 7006277409
commit b86e6294de
No known key found for this signature in database
2 changed files with 73 additions and 1 deletions

67
pkg/cancel/anyways.go Normal file
View file

@ -0,0 +1,67 @@
package cancel
import (
"context"
"time"
)
// 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 := time.NewTimer(timeout)
defer t.Stop()
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()
}