Skip to content

Commit

Permalink
Merge pull request #7 from dogmatiq/context
Browse files Browse the repository at this point in the history
Add functions for working with context deadlines.
  • Loading branch information
jmalloc authored Nov 11, 2019
2 parents bbe5b5e + e623c13 commit 39b1748
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 0 deletions.
50 changes: 50 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package linger

import (
"context"
"time"
)

// FromContextDeadline returns the duration until the deadline of ctx is
// reached.
//
// ok is false if ctx does not have a deadline.
func FromContextDeadline(ctx context.Context) (d time.Duration, ok bool) {
if dl, ok := ctx.Deadline(); ok {
return time.Until(dl), true
}

return 0, false
}

// ContextWithTimeout returns a context with a deadline some duration after the
// current time.
//
// The timeout duration is computed by finding the first of the supplied
// durations that is positive. It uses a zero duration if none of the supplied
// durations are positive.
func ContextWithTimeout(
ctx context.Context,
durations ...time.Duration,
) (context.Context, func()) {
return ContextWithTimeoutX(ctx, Identity, durations...)
}

// ContextWithTimeoutX returns a context with a deadline some duration after the
// current time.
//
// The timeout duration is computed by finding the first of the supplied
// durations that is positive, then applying the transform x. It uses a zero
// duration if none of the supplied durations are positive.
//
// The transform can be used to apply jitter to the timeout duration, for
// example, by using one of the built-in jitter transforms such as FullJitter()
// or ProportionalJitter().
func ContextWithTimeoutX(
ctx context.Context,
x DurationTransform,
durations ...time.Duration,
) (context.Context, func()) {
d, _ := Coalesce(durations...)
return context.WithTimeout(ctx, x(d))
}
62 changes: 62 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package linger_test

import (
"context"
"time"

. "github.com/dogmatiq/linger"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("func FromContextDeadline()", func() {
It("returns the time until the deadline of the context", func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

d, ok := FromContextDeadline(ctx)
Expect(ok).To(BeTrue())
Expect(d).To(BeNumerically("~", 10*time.Second, 1*time.Second))
})

It("returns false if the context does not have a deadline", func() {
_, ok := FromContextDeadline(context.Background())
Expect(ok).To(BeFalse())
})
})

var _ = Describe("func ContextWithTimeout()", func() {
It("sets a timeout for the first positive duration", func() {
expect := time.Now().Add(10 * time.Second)
ctx, cancel := ContextWithTimeout(context.Background(), 0*time.Second, -1*time.Second, 10*time.Second)
defer cancel()

dl, ok := ctx.Deadline()
Expect(ok).To(BeTrue())
Expect(dl).To(BeTemporally("~", expect))
})

It("times out 'immediately' if none of the durations are positive", func() {
ctx, cancel := ContextWithTimeout(context.Background(), 0*time.Second, -1*time.Second)
defer cancel()

err := ctx.Err()
Expect(err).To(Equal(context.DeadlineExceeded))
})
})

var _ = Describe("func ContextWithTimeoutX()", func() {
It("applies the transform", func() {
x := func(t time.Duration) time.Duration {
return t / 2
}

expect := time.Now().Add(10 * time.Second)
ctx, cancel := ContextWithTimeoutX(context.Background(), x, 20*time.Second)
defer cancel()

dl, ok := ctx.Deadline()
Expect(ok).To(BeTrue())
Expect(dl).To(BeTemporally("~", expect))
})
})
File renamed without changes.
File renamed without changes.

0 comments on commit 39b1748

Please sign in to comment.