wisski-cloud-distillery/pkg/cancel/anyways.go
2023-01-31 12:51:54 +01:00

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()
}