Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace ExecutionList with linked list of RunnableExecutionPair (#1287)
Patch does CAS loop and XCHG instead of synchronized which should be faster. Patch also reduces per-future memory consumption.
- Loading branch information
1 parent
84d1278
commit 671378f
Showing
4 changed files
with
141 additions
and
174 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 0 additions & 148 deletions
148
client/src/main/java/org/asynchttpclient/future/ExecutionList.java
This file was deleted.
Oops, something went wrong.
70 changes: 70 additions & 0 deletions
70
client/src/main/java/org/asynchttpclient/future/RunnableExecutorPair.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,70 @@ | |||
package org.asynchttpclient.future; | |||
|
|||
import java.util.concurrent.Executor; | |||
import java.util.logging.Level; | |||
import java.util.logging.Logger; | |||
|
|||
import org.asynchttpclient.util.Assertions; | |||
|
|||
/** | |||
* Linked list of runnables with executors. | |||
*/ | |||
final class RunnableExecutorPair { | |||
private static final Logger log = Logger.getLogger(RunnableExecutorPair.class.getPackage().getName()); | |||
|
|||
final Runnable runnable; | |||
final Executor executor; | |||
RunnableExecutorPair next; | |||
|
|||
RunnableExecutorPair() { | |||
runnable = null; | |||
executor = null; | |||
} | |||
|
|||
RunnableExecutorPair(Runnable runnable, Executor executor, RunnableExecutorPair next) { | |||
Assertions.assertNotNull(runnable, "runnable"); | |||
|
|||
this.runnable = runnable; | |||
this.executor = executor; | |||
this.next = next; | |||
} | |||
|
|||
/** | |||
* Submits the given runnable to the given {@link Executor} catching and logging all {@linkplain RuntimeException runtime exceptions} thrown by the executor. | |||
*/ | |||
static void executeListener(Runnable runnable, Executor executor) { | |||
try { | |||
if (executor != null) { | |||
executor.execute(runnable); | |||
} else { | |||
runnable.run(); | |||
} | |||
} catch (RuntimeException e) { | |||
// Log it and keep going, bad runnable and/or executor. Don't punish the other runnables if | |||
// we're given a bad one. We only catch RuntimeException because we want Errors to propagate | |||
// up. | |||
log.log(Level.SEVERE, "RuntimeException while executing runnable " + runnable + " with executor " + executor, e); | |||
} | |||
} | |||
|
|||
static RunnableExecutorPair reverseList(RunnableExecutorPair list) { | |||
// The pairs in the stack are in the opposite order from how they were added | |||
// so we need to reverse the list to fulfill our contract. | |||
// This is somewhat annoying, but turns out to be very fast in practice. Alternatively, we | |||
// could drop the contract on the method that enforces this queue like behavior since depending | |||
// on it is likely to be a bug anyway. | |||
|
|||
// N.B. All writes to the list and the next pointers must have happened before the above | |||
// synchronized block, so we can iterate the list without the lock held here. | |||
RunnableExecutorPair prev = null; | |||
|
|||
while (list != null) { | |||
RunnableExecutorPair next = list.next; | |||
list.next = prev; | |||
prev = list; | |||
list = next; | |||
} | |||
|
|||
return prev; | |||
} | |||
} |
36 changes: 36 additions & 0 deletions
36
client/src/test/java/org/asynchttpclient/future/RunnableExecutorPairTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,36 @@ | |||
package org.asynchttpclient.future; | |||
|
|||
import java.util.ArrayList; | |||
|
|||
import org.testng.Assert; | |||
import org.testng.annotations.Test; | |||
|
|||
/** | |||
* @author Stepan Koltsov | |||
*/ | |||
public class RunnableExecutorPairTest { | |||
|
|||
@Test | |||
public void testReverseList() { | |||
// empty | |||
{ | |||
Assert.assertNull(RunnableExecutorPair.reverseList(null)); | |||
} | |||
|
|||
for (int len = 1; len < 5; ++len) { | |||
ArrayList<RunnableExecutorPair> list = new ArrayList<>(); | |||
for (int i = 0; i < len; ++i) { | |||
RunnableExecutorPair prev = i != 0 ? list.get(i - 1) : null; | |||
list.add(new RunnableExecutorPair(() -> {}, null, prev)); | |||
} | |||
|
|||
RunnableExecutorPair reversed = RunnableExecutorPair.reverseList(list.get(list.size() - 1)); | |||
for (int i = 0; i < len; ++i) { | |||
Assert.assertSame(reversed, list.get(i)); | |||
Assert.assertSame(i != len - 1 ? list.get(i + 1) : null, reversed.next); | |||
reversed = reversed.next; | |||
} | |||
} | |||
} | |||
|
|||
} |