|
28 | 28 |
|
29 | 29 | package org.asynchttpclient.future; |
30 | 30 |
|
| 31 | +import java.util.ArrayList; |
31 | 32 | import java.util.concurrent.Executor; |
| 33 | +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; |
32 | 34 |
|
33 | 35 | import org.asynchttpclient.ListenableFuture; |
34 | 36 |
|
35 | 37 | /** |
36 | | - * An abstract base implementation of the listener support provided by {@link ListenableFuture}. This class uses an {@link ExecutionList} to guarantee that all registered listeners |
37 | | - * will be executed. Listener/Executor pairs are stored in the execution list and executed in the order in which they were added, but because of thread scheduling issues there is |
| 38 | + * An abstract base implementation of the listener support provided by {@link ListenableFuture}. |
| 39 | + * Listener/Executor pairs are stored in the {@link RunnableExecutorPair} linked list in the order in which they were added, but because of thread scheduling issues there is |
38 | 40 | * no guarantee that the JVM will execute them in order. In addition, listeners added after the task is complete will be executed immediately, even if some previously added |
39 | 41 | * listeners have not yet been executed. |
40 | 42 | * |
|
43 | 45 | */ |
44 | 46 | public abstract class AbstractListenableFuture<V> implements ListenableFuture<V> { |
45 | 47 |
|
46 | | - private volatile boolean hasRun; |
47 | | - private volatile boolean executionListInitialized; |
48 | | - private volatile ExecutionList executionList; |
| 48 | + /** |
| 49 | + * Marks that execution is already done, and new runnables |
| 50 | + * should be executed right away instead of begin added to the list. |
| 51 | + */ |
| 52 | + private static final RunnableExecutorPair executedMarker = new RunnableExecutorPair(null, null, null); |
49 | 53 |
|
50 | | - private ExecutionList executionList() { |
51 | | - ExecutionList localExecutionList = executionList; |
52 | | - if (localExecutionList == null) { |
53 | | - synchronized (this) { |
54 | | - localExecutionList = executionList; |
55 | | - if (localExecutionList == null) { |
56 | | - localExecutionList = new ExecutionList(); |
57 | | - executionList = localExecutionList; |
58 | | - executionListInitialized = true; |
59 | | - } |
60 | | - } |
61 | | - } |
62 | | - return localExecutionList; |
63 | | - } |
| 54 | + /** |
| 55 | + * Linked list of executions or a {@link #executedMarker}. |
| 56 | + */ |
| 57 | + private volatile RunnableExecutorPair executionList; |
| 58 | + private static final AtomicReferenceFieldUpdater<AbstractListenableFuture, RunnableExecutorPair> executionListField = |
| 59 | + AtomicReferenceFieldUpdater.newUpdater(AbstractListenableFuture.class, RunnableExecutorPair.class, "executionList"); |
64 | 60 |
|
65 | 61 | @Override |
66 | 62 | public ListenableFuture<V> addListener(Runnable listener, Executor exec) { |
67 | | - executionList().add(listener, exec); |
68 | | - if (hasRun) { |
69 | | - runListeners(); |
| 63 | + for (;;) { |
| 64 | + RunnableExecutorPair executionListLocal = this.executionList; |
| 65 | + if (executionListLocal == executedMarker) { |
| 66 | + RunnableExecutorPair.executeListener(listener, exec); |
| 67 | + return this; |
| 68 | + } |
| 69 | + |
| 70 | + RunnableExecutorPair pair = new RunnableExecutorPair(listener, exec, executionListLocal); |
| 71 | + if (executionListField.compareAndSet(this, executionListLocal, pair)) { |
| 72 | + return this; |
| 73 | + } |
70 | 74 | } |
71 | | - return this; |
72 | 75 | } |
73 | 76 |
|
74 | 77 | /** |
75 | 78 | * Execute the execution list. |
76 | 79 | */ |
77 | 80 | protected void runListeners() { |
78 | | - hasRun = true; |
79 | | - if (executionListInitialized) { |
80 | | - executionList().execute(); |
| 81 | + RunnableExecutorPair execution = executionListField.getAndSet(this, executedMarker); |
| 82 | + if (execution == executedMarker) { |
| 83 | + return; |
| 84 | + } |
| 85 | + |
| 86 | + RunnableExecutorPair reversedList = RunnableExecutorPair.reverseList(execution); |
| 87 | + |
| 88 | + while (reversedList != null) { |
| 89 | + RunnableExecutorPair.executeListener(reversedList.runnable, reversedList.executor); |
81 | 90 | } |
82 | 91 | } |
83 | 92 | } |
0 commit comments