-
Notifications
You must be signed in to change notification settings - Fork 1
/
promise.go
151 lines (140 loc) · 4.06 KB
/
promise.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package sup
import (
"context"
"errors"
"sync"
)
type Promise interface {
Cancel() // cancels the promise, effectively resolving it with nil.
Resolve(interface{}) // sets the value. panics on repeat use.
Get(Context) ResolvedPromise // blocking. waits and returns access to the resolved value.
GetNow() (interface{}, error) // nonblocking. returns (nil,promise.Nonblock) if not yet resolved; error may be context.Canceled or promise.Nonblock or nil if resolved.
Wait(Context) // blocking.
WaitSelectably(chan<- Promise) // nonblocking. cause ourself to be sent to this channel when we become resolved. multiple use panics.
WaitCallback(func(Promise)) // nonblocking. alternative to WaitSelectably which you can use if e.g. you need to send to multiple chans without waiting on each other or otherwise control rejection. multiple use panics.
}
type ResolvedPromise struct {
Value interface{} // the resolved value. if nil, you may also need to check Error() to see if the promise was canceled.
Error error // nil or context.Canceled (or promise.Nonblock if Get was called and its context canceled).
}
var Nonblock = errors.New("promise nonblock")
// NewPromise returns a new unresolved promise.
// You can start waiting on it immediately, and resolve it (or hand it off
// to someone else to resolve) at your leisure.
func NewPromise() Promise {
return &promise{waitCh: make(chan struct{})}
}
// NewDiscardingPromise returns a dummy promise where resolved values are
// discarded and all reader and waiter methods panic.
// (Resolve still has the set-once check but remembers no content.)
func NewDiscardingPromise() Promise {
return &discardPromise{}
}
type promise struct {
ResolvedPromise
mu sync.Mutex
waitCh chan struct{}
afterCh chan<- Promise
afterFn func(Promise)
}
func (p *promise) Cancel() {
p.mu.Lock()
if p.Value != nil || p.Error != nil {
p.mu.Unlock()
return
}
p.Error = context.Canceled
p.notifyAndUnlock()
}
func (p *promise) Resolve(v interface{}) {
p.mu.Lock()
if p.Error != nil {
// i've been raced. drop my effect.
p.mu.Unlock()
return
}
if p.Value != nil {
// i've been misused! rage.
p.mu.Unlock()
panic("multiple Resolve() calls on Promise")
}
p.Value = v
p.notifyAndUnlock()
}
func (p *promise) Get(ctx Context) ResolvedPromise {
select {
case <-p.waitCh:
return p.ResolvedPromise
case <-ctx.Done():
return ResolvedPromise{nil, Nonblock}
}
}
func (p *promise) GetNow() (v interface{}, err error) {
p.mu.Lock()
v, err = p.Value, p.Error
p.mu.Unlock()
if v == nil && err == nil {
err = Nonblock
}
return
}
func (p *promise) Wait(ctx Context) {
select {
case <-p.waitCh:
case <-ctx.Done():
}
}
func (p *promise) WaitSelectably(afterCh chan<- Promise) {
p.mu.Lock()
if p.afterCh != nil {
p.mu.Unlock()
panic("multiple WaitSelectably() calls on Promise")
}
p.afterCh = afterCh
if p.Value != nil || p.Error != nil {
afterCh <- p
}
p.mu.Unlock()
}
func (p *promise) WaitCallback(afterFn func(Promise)) {
p.mu.Lock()
if p.afterFn != nil {
p.mu.Unlock()
panic("multiple WaitCallback() calls on Promise")
}
p.afterFn = afterFn
if p.Value != nil || p.Error != nil {
afterFn(p)
}
p.mu.Unlock()
}
func (p *promise) notifyAndUnlock() {
afterCh, afterFn := p.afterCh, p.afterFn
p.mu.Unlock()
close(p.waitCh)
if afterCh != nil {
afterCh <- p
}
if afterFn != nil {
afterFn(p)
}
}
type discardPromise struct {
mu sync.Mutex
resolved bool
}
func (p *discardPromise) Cancel() {}
func (p *discardPromise) Resolve(interface{}) {
p.mu.Lock()
if p.resolved {
p.mu.Unlock()
panic("multiple Resolve() calls on Promise")
}
p.resolved = true
p.mu.Unlock()
}
func (p discardPromise) Get(Context) ResolvedPromise { panic("discardpromise") }
func (p discardPromise) GetNow() (interface{}, error) { panic("discardpromise") }
func (p discardPromise) Wait(Context) { panic("discardpromise") }
func (p discardPromise) WaitSelectably(chan<- Promise) { panic("discardpromise") }
func (p discardPromise) WaitCallback(func(Promise)) { panic("discardpromise") }