├── README.md
├── src
├── test
│ ├── resources
│ │ ├── unbounded.conf
│ │ ├── bounded.conf
│ │ └── disruptor.conf
│ └── java
│ │ └── akka
│ │ └── dispatch
│ │ └── disruptor
│ │ └── benchmark
│ │ └── ThroughputBenchmark.java
└── main
│ └── java
│ └── akka
│ └── dispatch
│ └── disruptor
│ ├── DisruptorMessageQueueSemantics.java
│ ├── DisruptorMailbox.java
│ └── DisruptorMessageQueue.java
├── LICENSE
└── pom.xml
/README.md:
--------------------------------------------------------------------------------
1 | akka-disruptor
2 | ======
3 |
4 | Akka bounded mailbox implementation using LMAX Disruptor
5 |
--------------------------------------------------------------------------------
/src/test/resources/unbounded.conf:
--------------------------------------------------------------------------------
1 | dispatcher {
2 | mailbox = "akka.dispatch.SingleConsumerOnlyUnboundedMailbox"
3 | fork-join-executor {
4 | parallelism-min = 8
5 | parallelism-max = 8
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/test/resources/bounded.conf:
--------------------------------------------------------------------------------
1 | dispatcher1 {
2 | mailbox-requirement = "akka.dispatch.BoundedMessageQueueSemantics"
3 | fork-join-executor {
4 | parallelism-min = 4
5 | parallelism-max = 4
6 | }
7 | }
8 | dispatcher2 {
9 | mailbox = "akka.dispatch.SingleConsumerOnlyUnboundedMailbox"
10 | fork-join-executor {
11 | parallelism-min = 4
12 | parallelism-max = 4
13 | }
14 | }
15 | akka.actor.default-mailbox {
16 | mailbox-capacity = 1024
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/resources/disruptor.conf:
--------------------------------------------------------------------------------
1 | dispatcher1 {
2 | mailbox-requirement = "akka.dispatch.disruptor.DisruptorMessageQueueSemantics"
3 | fork-join-executor {
4 | parallelism-min = 4
5 | parallelism-max = 4
6 | }
7 | }
8 | dispatcher2 {
9 | mailbox = "akka.dispatch.SingleConsumerOnlyUnboundedMailbox"
10 | fork-join-executor {
11 | parallelism-min = 4
12 | parallelism-max = 4
13 | }
14 | }
15 | akka.actor.mailbox.requirements {
16 | "akka.dispatch.disruptor.DisruptorMessageQueueSemantics" = disruptor-mailbox
17 | }
18 | disruptor-mailbox {
19 | mailbox-type = "akka.dispatch.disruptor.DisruptorMailbox"
20 | mailbox-capacity = 1024
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Igor Konev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.github.akka-disruptor
8 | akka-disruptor
9 | 1.0-SNAPSHOT
10 |
11 |
12 |
13 | com.typesafe.akka
14 | akka-actor_2.10
15 | 2.2.3
16 |
17 |
18 | com.lmax
19 | disruptor
20 | 3.2.0
21 |
22 |
23 | junit
24 | junit
25 | 4.11
26 | test
27 |
28 |
29 | com.carrotsearch
30 | junit-benchmarks
31 | 0.7.2
32 | test
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/main/java/akka/dispatch/disruptor/DisruptorMessageQueueSemantics.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2013 Igor Konev
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package akka.dispatch.disruptor;
26 |
27 | public interface DisruptorMessageQueueSemantics {
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/akka/dispatch/disruptor/DisruptorMailbox.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2013 Igor Konev
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package akka.dispatch.disruptor;
26 |
27 | import akka.actor.ActorRef;
28 | import akka.actor.ActorSystem;
29 | import akka.dispatch.MailboxType;
30 | import akka.dispatch.MessageQueue;
31 | import akka.dispatch.ProducesMessageQueue;
32 | import com.lmax.disruptor.SleepingWaitStrategy;
33 | import com.typesafe.config.Config;
34 | import scala.Option;
35 |
36 | import static akka.actor.ActorSystem.Settings;
37 |
38 | public final class DisruptorMailbox implements MailboxType, ProducesMessageQueue {
39 |
40 | private final int bufferSize;
41 |
42 | public DisruptorMailbox(Settings settings, Config config) {
43 | int capacity = config.getInt("mailbox-capacity");
44 | if (capacity < 1) {
45 | throw new IllegalArgumentException("Mailbox capacity must not be less than 1");
46 | }
47 | if (Integer.bitCount(capacity) != 1) {
48 | throw new IllegalArgumentException("Mailbox capacity must be a power of 2");
49 | }
50 | bufferSize = capacity;
51 | }
52 |
53 | @Override
54 | public MessageQueue create(Option owner, Option system) {
55 | return new DisruptorMessageQueue(bufferSize, new SleepingWaitStrategy());
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/akka/dispatch/disruptor/DisruptorMessageQueue.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2013 Igor Konev
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package akka.dispatch.disruptor;
26 |
27 | import akka.actor.ActorRef;
28 | import akka.dispatch.Envelope;
29 | import akka.dispatch.MessageQueue;
30 | import com.lmax.disruptor.AlertException;
31 | import com.lmax.disruptor.EventFactory;
32 | import com.lmax.disruptor.RingBuffer;
33 | import com.lmax.disruptor.Sequence;
34 | import com.lmax.disruptor.SequenceBarrier;
35 | import com.lmax.disruptor.TimeoutException;
36 | import com.lmax.disruptor.WaitStrategy;
37 |
38 | final class DisruptorMessageQueue implements MessageQueue, DisruptorMessageQueueSemantics {
39 |
40 | private static final EventFactory EVENT_FACTORY = new EventFactory() {
41 |
42 | @Override
43 | public ValueEvent newInstance() {
44 | return new ValueEvent();
45 | }
46 | };
47 |
48 | private final Sequence sequence = new Sequence();
49 | private final RingBuffer ringBuffer;
50 | private final SequenceBarrier sequenceBarrier;
51 | private final int mask;
52 | private final Envelope[] buffer;
53 | private int head;
54 | private int tail;
55 |
56 | DisruptorMessageQueue(int bufferSize, WaitStrategy waitStrategy) {
57 | ringBuffer = RingBuffer.createMultiProducer(EVENT_FACTORY, bufferSize, waitStrategy);
58 | ringBuffer.addGatingSequences(sequence);
59 | sequenceBarrier = ringBuffer.newBarrier();
60 | mask = bufferSize - 1;
61 | buffer = new Envelope[bufferSize];
62 | }
63 |
64 | @Override
65 | public void enqueue(ActorRef receiver, Envelope handle) {
66 | long nextSequence = ringBuffer.next();
67 | ringBuffer.get(nextSequence).handle = handle;
68 | ringBuffer.publish(nextSequence);
69 | }
70 |
71 | @Override
72 | public Envelope dequeue() {
73 | fill();
74 | int h = head & mask;
75 | head++;
76 | Envelope handle = buffer[h];
77 | buffer[h] = null;
78 | return handle;
79 | }
80 |
81 | @Override
82 | public int numberOfMessages() {
83 | return 0;
84 | }
85 |
86 | @Override
87 | public boolean hasMessages() {
88 | return head != tail || sequence.get() < sequenceBarrier.getCursor();
89 | }
90 |
91 | @Override
92 | public void cleanUp(ActorRef owner, MessageQueue deadLetters) {
93 | while (hasMessages()) {
94 | deadLetters.enqueue(owner, dequeue());
95 | }
96 | }
97 |
98 | private void fill() {
99 | if (head != tail) {
100 | return;
101 | }
102 |
103 | long nextSequence = sequence.get() + 1L;
104 | boolean interrupted = false;
105 | long availableSequence;
106 | try {
107 | while (true) {
108 | try {
109 | availableSequence = sequenceBarrier.waitFor(nextSequence);
110 | if (nextSequence <= availableSequence) {
111 | break;
112 | }
113 | Thread.yield();
114 | } catch (AlertException ignored) {
115 | interrupted = true;
116 | } catch (InterruptedException ignored) {
117 | interrupted = true;
118 | } catch (TimeoutException ignored) {
119 | }
120 | }
121 | } finally {
122 | if (interrupted) {
123 | Thread.currentThread().interrupt();
124 | }
125 | }
126 |
127 | int t = tail;
128 | do {
129 | buffer[t & mask] = ringBuffer.get(nextSequence).handle;
130 | t++;
131 | nextSequence++;
132 | } while (nextSequence <= availableSequence);
133 |
134 | tail = t;
135 | sequence.set(availableSequence);
136 | }
137 |
138 | private static final class ValueEvent {
139 |
140 | Envelope handle;
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/test/java/akka/dispatch/disruptor/benchmark/ThroughputBenchmark.java:
--------------------------------------------------------------------------------
1 | /*
2 | * The MIT License (MIT)
3 | *
4 | * Copyright (c) 2013 Igor Konev
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | */
24 |
25 | package akka.dispatch.disruptor.benchmark;
26 |
27 | import akka.actor.ActorRef;
28 | import akka.actor.ActorSystem;
29 | import akka.actor.Props;
30 | import akka.actor.UntypedActor;
31 | import com.carrotsearch.junitbenchmarks.AbstractBenchmark;
32 | import com.typesafe.config.Config;
33 | import com.typesafe.config.ConfigFactory;
34 | import java.util.Arrays;
35 | import java.util.List;
36 | import java.util.concurrent.CountDownLatch;
37 | import org.junit.After;
38 | import org.junit.Before;
39 | import org.junit.FixMethodOrder;
40 | import org.junit.Test;
41 | import org.junit.runner.RunWith;
42 | import org.junit.runners.Parameterized;
43 |
44 | import static org.junit.runners.Parameterized.Parameters;
45 |
46 | @RunWith(Parameterized.class)
47 | @FixMethodOrder
48 | public class ThroughputBenchmark extends AbstractBenchmark {
49 |
50 | private final int numberOfClients;
51 | private final long repeats;
52 | private final Config config;
53 | private final String dispatcher1;
54 | private final String dispatcher2;
55 | private ActorSystem system;
56 | private CountDownLatch latch;
57 | private ActorRef[] destinations;
58 | private ActorRef[] clients;
59 |
60 | public ThroughputBenchmark(int numberOfClients, long repeats, Config config, String dispatcher1,
61 | String dispatcher2) {
62 | this.numberOfClients = numberOfClients;
63 | this.repeats = repeats;
64 | this.config = config;
65 | this.dispatcher1 = dispatcher1;
66 | this.dispatcher2 = dispatcher2;
67 | }
68 |
69 | @Before
70 | public void setUp() throws Exception {
71 | system = ActorSystem.create("MySystem", config);
72 | latch = new CountDownLatch(numberOfClients);
73 | destinations = new ActorRef[numberOfClients];
74 | clients = new ActorRef[numberOfClients];
75 |
76 | Props props = Props.create(Destination.class).withDispatcher(dispatcher1);
77 | long repeatsPerClient = repeats / numberOfClients;
78 |
79 | for (int i = 0; i < numberOfClients; i++) {
80 | ActorRef destination = system.actorOf(props);
81 | destinations[i] = destination;
82 | clients[i] = system.actorOf(Props.create(Client.class, destination, latch, repeatsPerClient)
83 | .withDispatcher(dispatcher2));
84 | }
85 | }
86 |
87 | @After
88 | public void tearDown() throws Exception {
89 | for (int i = 0; i < numberOfClients; i++) {
90 | system.stop(clients[i]);
91 | system.stop(destinations[i]);
92 | }
93 | }
94 |
95 | @Test
96 | public void test() throws InterruptedException {
97 | for (int i = 0; i < numberOfClients; i++) {
98 | clients[i].tell(Run.INSTANCE, null);
99 | }
100 | latch.await();
101 | }
102 |
103 | @Parameters
104 | public static List