19
19
import org .slf4j .Logger ;
20
20
import org .slf4j .LoggerFactory ;
21
21
22
- import java .util .ArrayList ;
23
- import java .util .List ;
22
+ import java .util .concurrent .ConcurrentLinkedQueue ;
24
23
import java .util .concurrent .CountDownLatch ;
25
24
import java .util .concurrent .TimeUnit ;
26
- import java .util .concurrent .locks .Lock ;
27
- import java .util .concurrent .locks .ReentrantLock ;
25
+ import java .util .concurrent .atomic .AtomicReference ;
28
26
29
27
/**
30
28
* @author Chris Pettitt ([email protected] )
36
34
37
35
private final CountDownLatch _awaitLatch = new CountDownLatch (1 );
38
36
39
- private final Lock _lock = new ReentrantLock ();
40
- private List <PromiseListener <T >> _listeners = new ArrayList <PromiseListener <T >>();
41
- private volatile T _value ;
42
- private volatile Throwable _error ;
43
- private volatile boolean _done ;
37
+ private final AtomicReference <Result <T >> _result = new AtomicReference <Result <T >>();
38
+ private final ConcurrentLinkedQueue <PromiseListener <T >> _listeners = new ConcurrentLinkedQueue <PromiseListener <T >>();
44
39
45
40
@ Override
46
41
public void done (final T value ) throws PromiseResolvedException
@@ -57,30 +52,29 @@ public void fail(final Throwable error) throws PromiseResolvedException
57
52
@ Override
58
53
public T get () throws PromiseException
59
54
{
60
- ensureDone ();
61
- if (_error != null )
55
+ Result < T > result = getResult ();
56
+ if (result . _error != null )
62
57
{
63
- throw new PromiseException (_error );
58
+ throw new PromiseException (result . _error );
64
59
}
65
- return _value ;
60
+ return result . _value ;
66
61
}
67
62
68
63
@ Override
69
64
public Throwable getError () throws PromiseUnresolvedException
70
65
{
71
- ensureDone ();
72
- return _error ;
66
+ return getResult ()._error ;
73
67
}
74
68
75
69
@ Override
76
70
public T getOrDefault (final T defaultValue ) throws PromiseUnresolvedException
77
71
{
78
- ensureDone ();
79
- if (_error != null )
72
+ Result < T > result = getResult ();
73
+ if (result . _error != null )
80
74
{
81
75
return defaultValue ;
82
76
}
83
- return _value ;
77
+ return result . _value ;
84
78
}
85
79
86
80
@ Override
@@ -98,93 +92,92 @@ public boolean await(final long time, final TimeUnit unit) throws InterruptedExc
98
92
@ Override
99
93
public void addListener (final PromiseListener <T > listener )
100
94
{
101
- _lock .lock ();
102
- try
103
- {
104
- if (!isDone ())
105
- {
106
- _listeners .add (listener );
107
- return ;
108
- }
109
- }
110
- finally
95
+ if (isDone ())
111
96
{
112
- _lock .unlock ();
97
+ listener .onResolved (this );
98
+ return ;
113
99
}
114
100
115
- notifyListener (listener );
101
+ _listeners .add (listener );
102
+
103
+ if (isDone ())
104
+ purgeListeners ();
116
105
}
117
106
118
107
@ Override
119
108
public boolean isDone ()
120
109
{
121
- return _done ;
110
+ return _result . get () != null ;
122
111
}
123
112
124
113
@ Override
125
114
public boolean isFailed ()
126
115
{
127
- return isDone () && _error != null ;
116
+ final Result <T > voe = _result .get ();
117
+ return voe != null && voe ._error != null ;
128
118
}
129
119
130
120
private void doFinish (T value , Throwable error ) throws PromiseResolvedException
131
121
{
132
- final List <PromiseListener <T >> listeners ;
133
-
134
- _lock .lock ();
135
- try
136
- {
137
- ensureNotDone ();
138
- _value = value ;
139
- _error = error ;
140
- listeners = _listeners ;
141
- _listeners = null ;
142
- _done = true ;
143
- }
144
- finally
122
+ if (!_result .compareAndSet (null , new Result <T >(value , error )))
145
123
{
146
- _lock . unlock ( );
124
+ throw new PromiseResolvedException ( "Promise has already been satisfied" );
147
125
}
148
126
149
- for (int i = listeners .size () - 1 ; i >= 0 ; i --)
150
- {
151
- notifyListener (listeners .get (i ));
152
- }
127
+ purgeListeners ();
153
128
154
129
_awaitLatch .countDown ();
155
130
}
156
131
157
- private void notifyListener ( final PromiseListener < T > listener )
132
+ private void purgeListeners ( )
158
133
{
159
134
// We intentionally catch Throwable around the listener invocation because
160
135
// it will cause the notifier loop and subsequent count down in doFinish to
161
136
// be skipped, which will certainly lead to bad behavior. It could be argued
162
137
// that the catch should not apply for use of notifyListener from
163
138
// addListener, but it seems better to err on the side of consistency and
164
139
// least surprise.
165
- try
166
- {
167
- listener .onResolved (this );
168
- }
169
- catch (Throwable e )
140
+ PromiseListener <T > listener ;
141
+ while ((listener = _listeners .poll ()) != null )
170
142
{
171
- LOGGER .warn ("An exception was thrown by listener: " + listener .getClass (), e );
143
+ try
144
+ {
145
+ listener .onResolved (this );
146
+ }
147
+ catch (Throwable e )
148
+ {
149
+ LOGGER .warn ("An exception was thrown by listener: " + listener .getClass (), e );
150
+ }
172
151
}
173
152
}
174
153
175
- private void ensureNotDone () throws PromiseResolvedException
154
+ private Result < T > getResult () throws PromiseUnresolvedException
176
155
{
177
- if (isDone ())
156
+ final Result <T > voe = _result .get ();
157
+ if (voe == null )
178
158
{
179
- throw new PromiseResolvedException ("Promise has already been satisfied" );
159
+ throw new PromiseUnresolvedException ("Promise has not yet been satisfied" );
180
160
}
161
+ return voe ;
181
162
}
182
163
183
- private void ensureDone () throws PromiseUnresolvedException
164
+ private static class Result < T >
184
165
{
185
- if (!isDone ())
166
+ private final T _value ;
167
+ private final Throwable _error ;
168
+
169
+ public Result (T value , Throwable error )
186
170
{
187
- throw new PromiseUnresolvedException ("Promise has not yet been satisfied" );
171
+ if (error != null )
172
+ {
173
+ _value = null ;
174
+ _error = error ;
175
+ }
176
+ else
177
+ {
178
+ _value = value ;
179
+ _error = null ;
180
+ }
188
181
}
189
182
}
190
183
}
0 commit comments