Skip to content
This repository has been archived by the owner on Nov 8, 2022. It is now read-only.

Commit

Permalink
Added Promise::IsError().
Browse files Browse the repository at this point in the history
  • Loading branch information
ConnorDoyle committed Mar 2, 2016
1 parent 6bb07bf commit cff79e4
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 22 deletions.
58 changes: 36 additions & 22 deletions pkg/promise/promise.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,36 @@ import (
// Functions that operate on this type (IsComplete, Complete,
// Await, AwaitUntil) are idempotent and thread-safe.
type Promise interface {
// Returns whether this promise is complete yet, without blocking.
IsComplete() bool

// Returns whether this promise completed with errors, without blocking.
IsError() bool

// Unblock all goroutines awaiting promise completion.
Complete(errors []error)

// Blocks the caller until the promise is marked complete. This function
// is equivalent to invoking AwaitUntil() with a zero-length duration.
// To avoid blocking the caller indefinitely, use AwaitUntil() with a
// non-zero duration instead.
Await() []error

// Blocks the caller until the promise is marked complete, or the supplied
// duration has elapsed. If the promise has not been completed before the
// await times out, this function returns with nonempty errors. If the
// supplied duration has zero length, this await will never time out.
AwaitUntil(timeout time.Duration) []error

// Invokes the supplied function after this promise completes. This function
// is equivalent to invoking AndThenUntil() with a zero-length duration.
// To avoid blocking a goroutine indefinitely, use AndThenUntil() with a
// non-zero duration instead.
AndThen(f func([]error))

// Invokes the supplied function after this promise completes or times out
// after the supplied duration. If the supplied duration has zero length,
// the deferred execution will never time out.
AndThenUntil(timeout time.Duration, f func([]error))
}

Expand All @@ -36,12 +61,14 @@ type promise struct {
completeChan chan struct{}
}

// Returns whether this promise is complete yet, without blocking.
func (p *promise) IsComplete() bool {
return p.complete
}

// Unblock all goroutines awaiting promise completion.
func (p *promise) IsError() bool {
return p.IsComplete() && len(p.errors) > 0
}

func (p *promise) Complete(errors []error) {
p.Lock()
defer p.Unlock()
Expand All @@ -53,18 +80,10 @@ func (p *promise) Complete(errors []error) {
}
}

// Blocks the caller until the promise is marked complete. This function
// is equivalent to invoking AwaitUntil() with a zero-length duration.
// To avoid blocking the caller indefinitely, use AwaitUntil() with a
// non-zero duration instead.
func (p *promise) Await() []error {
return p.AwaitUntil(0 * time.Second)
}

// Blocks the caller until the promise is marked complete, or the supplied
// duration has elapsed. If the promise has not been completed before the
// await times out, this function returns with nonempty errors. If the
// supplied duration has zero length, this await will never time out.
func (p *promise) AwaitUntil(duration time.Duration) []error {
var timeoutChan <-chan time.Time
if duration.Nanoseconds() > 0 {
Expand All @@ -79,17 +98,10 @@ func (p *promise) AwaitUntil(duration time.Duration) []error {
}
}

// Invokes the supplied function after this promise completes. This function
// is equivalent to invoking AndThenUntil() with a zero-length duration.
// To avoid blocking a goroutine indefinitely, use AndThenUntil() with a
// non-zero duration instead.
func (p *promise) AndThen(f func([]error)) {
p.AndThenUntil(0*time.Nanosecond, f)
}

// Invokes the supplied function after this promise completes or times out
// after the supplied duration. If the supplied duration has zero length,
// the deferred execution will never time out.
func (p *promise) AndThenUntil(d time.Duration, f func([]error)) {
go func() {
f(p.AwaitUntil(d))
Expand All @@ -99,8 +111,15 @@ func (p *promise) AndThenUntil(d time.Duration, f func([]error)) {
// A reciprocal promise that makes it easy for two coordinating routines
// A and B to wait on each other before proceeding.
type RendezVous interface {
// Returns whether this rendez-vous is complete yet, without blocking.
IsComplete() bool

// Complete process A's half of the rendez-vous, and block until process
// B has done the same.
A()

// Complete process B's half of the rendez-vous, and block until process
// A has done the same.
B()
}

Expand All @@ -116,20 +135,15 @@ type rendezVous struct {
b Promise
}

// Returns whether this rendez-vous is complete yet, without blocking.
func (r *rendezVous) IsComplete() bool {
return r.a.IsComplete() && r.b.IsComplete()
}

// Complete process A's half of the rendez-vous, and block until process
// B has done the same.
func (r *rendezVous) A() {
r.a.Complete([]error{})
r.b.Await()
}

// Complete process B's half of the rendez-vous, and block until process
// A has done the same.
func (r *rendezVous) B() {
r.b.Complete([]error{})
r.a.Await()
Expand Down
17 changes: 17 additions & 0 deletions pkg/promise/promise_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package promise

import (
"errors"
"sync"
"testing"
"time"
Expand All @@ -17,6 +18,22 @@ func TestPromise(t *testing.T) {
So(p.IsComplete(), ShouldBeTrue)
})
})
Convey("IsError()", t, func() {
Convey("it should return the error status", func() {
Convey("after completing without errors, IsError() returns false", func() {
p := NewPromise()
So(p.IsError(), ShouldBeFalse)
p.Complete([]error{})
So(p.IsError(), ShouldBeFalse)
})
Convey("after completing with errors, IsError() returns true", func() {
p := NewPromise()
So(p.IsError(), ShouldBeFalse)
p.Complete([]error{errors.New("ERROR")})
So(p.IsError(), ShouldBeTrue)
})
})
})
Convey("Complete()", t, func() {
Convey("it should unblock any waiting goroutines", func() {
p := NewPromise()
Expand Down

0 comments on commit cff79e4

Please sign in to comment.