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