├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
├── src
└── PhpDisruptor
│ ├── AbstractSequencer.php
│ ├── AggregateEventHandler.php
│ ├── Collections
│ └── Histogram.php
│ ├── CursoredInterface.php
│ ├── DataInterface.php
│ ├── DataProviderInterface.php
│ ├── Dsl
│ ├── ConsumerInfoInterface.php
│ ├── ConsumerRepository.php
│ ├── Disruptor.php
│ ├── EventHandlerGroup.php
│ ├── EventProcessorInfo.php
│ ├── ExceptionHandlerSetting.php
│ ├── ProducerType.php
│ └── WorkerPoolInfo.php
│ ├── EventClassCapableInterface.php
│ ├── EventFactoryInterface.php
│ ├── EventHandlerInterface.php
│ ├── EventProcessor
│ ├── AbstractEventProcessor.php
│ ├── BatchEventProcessor.php
│ ├── NoOpEventProcessor.php
│ └── WorkProcessor.php
│ ├── EventTranslatorInterface.php
│ ├── Exception
│ ├── AlertException.php
│ ├── ExceptionInterface.php
│ ├── ExtensionNotLoadedException.php
│ ├── InsufficientCapacityException.php
│ ├── InterruptedException.php
│ ├── InvalidArgumentException.php
│ ├── RuntimeException.php
│ ├── TimeoutException.php
│ └── UnsupportedMethodCallException.php
│ ├── ExceptionHandler
│ ├── AbstractExceptionHandler.php
│ ├── ExceptionHandlerInterface.php
│ ├── FatalExceptionHandler.php
│ └── IgnoreExceptionHandler.php
│ ├── FixedSequenceGroup.php
│ ├── LifecycleAwareInterface.php
│ ├── Lists
│ ├── EventHandlerList.php
│ ├── EventProcessorList.php
│ ├── EventTranslatorList.php
│ ├── SequenceList.php
│ └── WorkHandlerList.php
│ ├── MultiProducerSequencer.php
│ ├── ProcessingSequenceBarrier.php
│ ├── RingBuffer.php
│ ├── Sequence.php
│ ├── SequenceAggregateInterface.php
│ ├── SequenceBarrierInterface.php
│ ├── SequenceGroup.php
│ ├── SequenceGroups.php
│ ├── SequenceReportingEventHandlerInterface.php
│ ├── SequencerFollowingSequence.php
│ ├── SequencerInterface.php
│ ├── SingleProducerSequencer.php
│ ├── TimeoutHandlerInterface.php
│ ├── Util
│ └── Util.php
│ ├── WaitStrategy
│ ├── BlockingWaitStrategy.php
│ ├── BusySpinWaitStrategy.php
│ ├── SleepingWaitStrategy.php
│ ├── TimeoutBlockingWaitStrategy.php
│ ├── WaitStrategyInterface.php
│ └── YieldingWaitStrategy.php
│ ├── WorkHandlerInterface.php
│ └── WorkerPool.php
└── tests
├── Bootstrap.php
└── PhpDisruptorTest
├── AggregateEventHandler
├── AggregateEventHandlerTest.php
└── TestAsset
│ ├── EventHandler.php
│ └── ResultCounter.php
├── Dsl
├── ConsumerRepository
│ └── ConsumerRepositoryTest.php
└── Disruptor
│ ├── DisruptorTest.php
│ └── TestAsset
│ ├── DelayedEventHandler.php
│ └── TestWorkHandler.php
├── EventProcessor
└── BatchEventProcessor
│ ├── BatchEventProcessorTest.php
│ └── TestAsset
│ ├── EventHandler.php
│ ├── ExEventHandler.php
│ └── TestExceptionHandler.php
├── EventPublisherTest.php
├── EventTranslatorTest.php
├── ExceptionHandler
├── FatalExceptionHandlerTest.php
└── IgnoreExceptionHandlerTest.php
├── FixedSequenceGroupTest.php
├── LifecycleAwareInterface
├── LifecylceAwareInterfaceTest.php
└── TestAsset
│ └── LifecycleAwareEventHandler.php
├── MultiProducerSequencerTest.php
├── RingBufferTest.php
├── SequenceBarrierTest.php
├── SequenceGroupTest.php
├── SequenceReportingCallbackTest.php
├── TestAsset
├── ArrayEventTranslator.php
├── ArrayFactory.php
├── CountDownLatchSequence.php
├── EventTranslator.php
├── ExampleEventTranslator.php
├── LongEvent.php
├── LongEventFactory.php
├── LongWorkHandler.php
├── RingBufferThread.php
├── RingBufferThread2.php
├── SequenceBarrierThread.php
├── SleepingEventHandler.php
├── StubEvent.php
├── StubEventFactory.php
├── StubEventProcessor.php
├── StubEventProcessorThread.php
├── StubEventTranslator.php
├── TestEvent.php
├── TestEventFactory.php
├── TestEventProcessor.php
├── TestEventProcessor2.php
└── TestSequenceReportingEventHandler.php
├── Util
└── UtilTest.php
└── WaitStrategy
├── AbstractWaitStrategyTestCase.php
├── BusySpinWaitStrategyTest.php
├── SleepingWaitStrategyTest.php
├── TestAsset
├── DummySequenceBarrier.php
└── SequenceUpdater.php
└── YieldingWaitStrategyTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | vendor
3 | composer.lock
4 | composer.phar
5 | phpunit.xml
6 | phpmd.xml
7 | phpdox.xml
8 | .project
9 | nbproject
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 |
8 | before_script:
9 | - git clone https://github.com/krakjoe/pthreads lib-pthreads
10 | - cd lib-pthreads
11 | - phpize
12 | - ./configure
13 | - make
14 | - sudo make install
15 | - cd ..
16 | - echo "extension = pthreads.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
17 | - composer self-update
18 | - composer update --prefer-source --dev
19 |
20 | script:
21 | - ./vendor/bin/phpunit
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Sascha-Oliver Prolic
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PHP Disruptor
2 | =========================
3 |
4 | [](https://travis-ci.org/prolic/PhpDisruptor)
5 | [](https://packagist.org/packages/prolic/php-disruptor)
6 | [](https://packagist.org/packages/prolic/php-disruptor)
7 | [](https://packagist.org/packages/prolic/php-disruptor)
8 | [](https://www.versioneye.com/php/prolic:php-disruptor)
9 |
10 | A High Performance Inter-Thread Messaging Library
11 |
12 | Port of the [LMAX Disruptor](https://github.com/LMAX-Exchange/disruptor)
13 |
14 | Info
15 | ----
16 | This project will is discontinued and archived. PHP's pthreads extensions has been archived already.
17 |
18 |
19 | Requirements
20 | ------------
21 |
22 | - PHP 5.4 ZTS
23 | - ext-pthreads 0.0.45
24 | - ext-uuid 1.0.3
25 | - Concurrent-PHP-Utils
26 | - MabeEnum
27 |
28 | Under construction...
29 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "prolic/php-disruptor",
3 | "type": "library",
4 | "description": "A High Performance Inter-Thread Messaging Library",
5 | "keywords": ["disruptor" , "php"],
6 | "homepage": "https://github.com/prolic/PhpDisruptor",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "Sascha-Oliver Prolic",
11 | "email": "saschaprolic@googlemail.com"
12 | }
13 | ],
14 | "require": {
15 | "php": ">=5.4.0",
16 | "ext-pthreads": ">=2.0.8",
17 | "marc-mabe/php-enum": "1.2.*",
18 | "prolic/concurrent-php-utils": "dev-master"
19 | },
20 | "require-dev": {
21 | "phpunit/phpunit": ">=3.7"
22 | },
23 | "autoload": {
24 | "psr-0": {
25 | "PhpDisruptor\\": "src"
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 | A
must
160 | * process events before the worker pool with handlers B, C
:
161 | *
162 | *
dw.after(A).handleEventsWithWorkerPool(B, C);
163 | *
164 | * @param WorkHandlerList $handlers the work handlers that will process events.
165 | * Each work handler instance will provide an extra thread in the worker pool.
166 | * @return EventHandlerGroup that can be used to set up a event processor barrier over the created event processors.
167 | */
168 | public function handleEventsWithWorkerPool(WorkHandlerList $handlers)
169 | {
170 | return $this->disruptor->createWorkerPool($this->sequences, $handlers);
171 | }
172 |
173 | /**
174 | * Create a dependency barrier for the processors in this group.
175 | * This allows custom event processors to have dependencies on
176 | * BatchEventProcessors created by the disruptor.
177 | *
178 | * @return SequenceBarrierInterface including all the processors in this group.
179 | */
180 | public function asSequenceBarrier()
181 | {
182 | return $this->disruptor->getRingBuffer()->newBarrier($this->sequences);
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/Dsl/EventProcessorInfo.php:
--------------------------------------------------------------------------------
1 | eventProcessor = $eventProcessor;
47 | $this->handler = $handler;
48 | $this->barrier = $barrier;
49 | $this->endOfChain = true;
50 | }
51 |
52 | /**
53 | * @return AbstractEventProcessor
54 | */
55 | public function getEventProcessor()
56 | {
57 | return $this->eventProcessor;
58 | }
59 |
60 | /**
61 | * @return Sequence[]
62 | */
63 | public function getSequences()
64 | {
65 | $sequence = $this->eventProcessor->getSequence();
66 | $sequences = new SequenceList($sequence);
67 | return $sequences;
68 | }
69 |
70 | /**
71 | * @return EventHandlerInterface
72 | */
73 | public function getHandler()
74 | {
75 | return $this->handler;
76 | }
77 |
78 | /**
79 | * @return SequenceBarrierInterface
80 | */
81 | public function getBarrier()
82 | {
83 | return $this->barrier;
84 | }
85 |
86 | /**
87 | * @return bool
88 | */
89 | public function isEndOfChain()
90 | {
91 | return $this->endOfChain;
92 | }
93 |
94 | public function start()
95 | {
96 | $this->eventProcessor->start();
97 | }
98 |
99 | /**
100 | * @return void
101 | */
102 | public function shutdown()
103 | {
104 | $this->eventProcessor->shutdown();
105 | }
106 |
107 | /**
108 | * @return void
109 | */
110 | public function markAsUsedInBarrier()
111 | {
112 | $this->endOfChain = false;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/Dsl/ExceptionHandlerSetting.php:
--------------------------------------------------------------------------------
1 | getEventClass() != $consumerRepository->getEventClass()) {
38 | throw new Exception\InvalidArgumentException(
39 | '$consumerRepository uses event class ' . $consumerRepository->getEventClass()
40 | . ' but $eventHandler uses event class ' . $eventHandler->getEventClass()
41 | );
42 | }
43 | $this->eventClass = $eventHandler->getEventClass();
44 | $this->eventHandler = $eventHandler;
45 | $this->consumerRepository = $consumerRepository;
46 | }
47 |
48 | /**
49 | * @inheritdoc
50 | */
51 | public function getEventClass()
52 | {
53 | return $this->eventClass;
54 | }
55 |
56 | /**
57 | * Specify the ExceptionHandler to use with the event handler
58 | *
59 | * @param ExceptionHandlerInterface $exceptionHandler
60 | * @return void
61 | */
62 | public function with(ExceptionHandlerInterface $exceptionHandler)
63 | {
64 | $this->consumerRepository->getEventProcessorFor($this->eventHandler)->setExceptionHandler($exceptionHandler);
65 | $this->consumerRepository->getBarrierFor($this->eventHandler)->alert();
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/Dsl/ProducerType.php:
--------------------------------------------------------------------------------
1 | workerPool = $workerPool;
42 | $this->eventClass = $workerPool->getEventClass();
43 | $this->sequenceBarrier = $sequenceBarrier;
44 | $this->endOfChain = true;
45 | }
46 |
47 | /**
48 | * @return string
49 | */
50 | public function getEventClass()
51 | {
52 | return $this->eventClass;
53 | }
54 |
55 | /**
56 | * @return Sequence[]
57 | */
58 | public function getSequences()
59 | {
60 | return $this->workerPool->getWorkerSequences();
61 | }
62 |
63 | /**
64 | * @return SequenceBarrierInterface
65 | */
66 | public function getBarrier()
67 | {
68 | return $this->sequenceBarrier;
69 | }
70 |
71 | /**
72 | * @return bool
73 | */
74 | public function isEndOfChain()
75 | {
76 | return $this->endOfChain;
77 | }
78 |
79 | public function run()
80 | {
81 | $this->workerPool->start();
82 | }
83 |
84 | /**
85 | * @return void
86 | */
87 | public function halt()
88 | {
89 | $this->workerPool->halt();
90 | }
91 |
92 | /**
93 | * @return void
94 | */
95 | public function markAsUsedInBarrier()
96 | {
97 | $this->endOfChain = false;
98 | }
99 |
100 | /**
101 | * @return bool
102 | */
103 | public function isRunning()
104 | {
105 | return $this->workerPool->isRunning();
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/EventClassCapableInterface.php:
--------------------------------------------------------------------------------
1 | getEventClass() != $eventClass) {
90 | throw new Exception\InvalidArgumentException(
91 | 'invalid data provider given, must use the event class: "' . $eventClass . '"'
92 | );
93 | }
94 | if ($eventHandler->getEventClass() != $eventClass) {
95 | throw new Exception\InvalidArgumentException(
96 | 'invalid event handler given, must use the event class: "' . $eventClass . '"'
97 | );
98 | }
99 |
100 | $this->eventClass = $eventClass;
101 | $this->dataProvider = $dataProvider;
102 | $this->sequencerBarrier = $sequenceBarrier;
103 | $this->sequence = new Sequence(SequencerInterface::INITIAL_CURSOR_VALUE);
104 |
105 | if ($eventHandler instanceof SequenceReportingEventHandlerInterface) {
106 | $eventHandler->setSequenceCallback($this->sequence);
107 | }
108 | $this->eventHandler = $eventHandler;
109 |
110 | $this->exceptionHandler = new FatalExceptionHandler('/tmp/disruptor-batchevents');
111 | $this->timeoutHandler = ($eventHandler instanceof TimeoutHandlerInterface) ? $eventHandler : null;
112 | $this->running = false;
113 | }
114 |
115 | /**
116 | * @return Sequence
117 | */
118 | public function getSequence()
119 | {
120 | return $this->sequence;
121 | }
122 |
123 | /**
124 | * @inheritdoc
125 | */
126 | public function halt()
127 | {
128 | $this->running = false;
129 | $this->sequencerBarrier->alert();
130 | }
131 |
132 | /**
133 | * @param ExceptionHandlerInterface $exceptionHandler
134 | */
135 | public function setExceptionHandler(ExceptionHandlerInterface $exceptionHandler)
136 | {
137 | $this->exceptionHandler = $exceptionHandler;
138 | }
139 |
140 | public function run()
141 | {
142 | if (!$this->casMember('running', false, true)) {
143 | throw new Exception\RuntimeException(
144 | 'Thread is already running'
145 | );
146 | }
147 |
148 | $this->sequencerBarrier->clearAlert();
149 |
150 | // notify start
151 | if ($this->eventHandler instanceof LifecycleAwareInterface) {
152 | try {
153 | $this->eventHandler->onStart();
154 | } catch (\Exception $e) {
155 | $this->exceptionHandler->handleOnStartException($e);
156 | }
157 | }
158 |
159 | $nextSequence = $this->getSequence()->get() + 1;
160 | while (true) {
161 | try {
162 | $availableSequence = $this->sequencerBarrier->waitFor($nextSequence);
163 | while ($nextSequence <= $availableSequence) {
164 | $event = $this->dataProvider->get($nextSequence);
165 | $this->eventHandler->onEvent($event, $nextSequence, $nextSequence == $availableSequence);
166 | $nextSequence++;
167 | }
168 | $this->getSequence()->set($availableSequence);
169 | } catch (Exception\TimeoutException $e) {
170 | // notify timeout
171 | $availableSequence = $this->getSequence()->get();
172 | try {
173 | if (null !== $this->timeoutHandler) {
174 | $this->timeoutHandler->onTimeout($availableSequence);
175 | }
176 | } catch (\Exception $e) {
177 | $this->exceptionHandler->handleEventException($e, $availableSequence, null);
178 | }
179 | } catch (Exception\AlertException $e) {
180 | if (!$this->running) {
181 | break;
182 | }
183 | } catch (\Exception $e) {
184 | $event = isset($event) ? $event : '';
185 | $this->exceptionHandler->handleEventException($e, $nextSequence, $event);
186 | $this->getSequence()->set($nextSequence);
187 | $nextSequence++;
188 | }
189 | }
190 |
191 | // notify shutdown
192 | if ($this->eventHandler instanceof LifecycleAwareInterface) {
193 | try {
194 | $this->eventHandler->onShutdown();
195 | } catch (\Exception $e) {
196 | $this->exceptionHandler->handleOnShutdownException($e);
197 | }
198 | }
199 | $this->running = false;
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/EventProcessor/NoOpEventProcessor.php:
--------------------------------------------------------------------------------
1 | sequence = new SequencerFollowingSequence($sequencer);
29 | }
30 |
31 | /**
32 | * @return SequencerFollowingSequence
33 | */
34 | public function getSequence()
35 | {
36 | return $this->sequence;
37 | }
38 |
39 | /**
40 | * @return void
41 | */
42 | public function halt()
43 | {
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/EventProcessor/WorkProcessor.php:
--------------------------------------------------------------------------------
1 | getEventClass() != $this->ringBuffer->getEventClass()) {
63 | throw new Exception\InvalidArgumentException(
64 | 'All work handlers must use the event class as the ring buffer, buffer has "'
65 | . $this->ringBuffer->getEventClass() . '" and current handler has "'
66 | . $workHandler->getEventClass() . '"'
67 | );
68 | }
69 | $this->sequence = new Sequence();
70 | $this->ringBuffer = $ringBuffer;
71 | $this->sequenceBarrier = $sequenceBarrier;
72 | $this->workHandler = $workHandler;
73 | $this->exceptionHandler = $exceptionHandler;
74 | $this->workSequence = $workSequence;
75 | }
76 |
77 | /**
78 | * Get a reference to the Sequence being used by this EventProcessor.
79 | *
80 | * @return Sequence reference to the Sequence for this EventProcessor
81 | */
82 | public function getSequence()
83 | {
84 | return $this->sequence;
85 | }
86 |
87 | /**
88 | * Signal that this EventProcessor should stop when it has finished consuming at the next clean break.
89 | * It will call {@link SequenceBarrierInterface#alert()} to notify the thread to check status.
90 | *
91 | * @return void
92 | * @throws Exception\RuntimeException
93 | */
94 | public function halt()
95 | {
96 | $this->sequenceBarrier->clearAlert();
97 | $this->_notifyStart();
98 |
99 | $processedSequence = true;
100 | $cachedAvailableSequence = - PHP_INT_MAX - 1;
101 | $nextSequence = $this->sequence->get();
102 | $event = null;
103 | while (true) {
104 | try {
105 | // if previous sequence was processed - fetch the next sequence and set
106 | // that we have successfully processed the previous sequence
107 | // typically, this will be true
108 | // this prevents the sequence getting too far forward if an exception
109 | // is thrown from the WorkHandler
110 | if ($processedSequence) {
111 | $processedSequence = false;
112 | $nextSequence = $this->workSequence->incrementAndGet();
113 | $this->sequence->set($nextSequence - 1);
114 | }
115 | if ($cachedAvailableSequence >= $nextSequence) {
116 | $event = $this->ringBuffer->get($nextSequence);
117 | $this->workHandler->onEvent($event);
118 | $processedSequence = true;
119 | } else {
120 | $cachedAvailableSequence = $this->sequenceBarrier->waitFor($nextSequence);
121 | }
122 | } catch (Exception\AlertException $e) {
123 | if (!$this->isRunning()) {
124 | break;
125 | }
126 | } catch (\Exception $e) {
127 | $this->exceptionHandler->handleEventException($e, $nextSequence, $event);
128 | $processedSequence = true;
129 | }
130 | }
131 |
132 | $this->_notifyShutdown();
133 | $this->shutdown();
134 | }
135 |
136 | /**
137 | * @return void
138 | * @throws Exception\RuntimeException
139 | */
140 | public function run()
141 | {
142 | $this->sequenceBarrier->clearAlert();
143 | $this->_notifyStart();
144 |
145 | $processedSequence = true;
146 | $cachedAvailableSequence = - PHP_INT_MAX - 1;
147 | $nextSequence = $this->sequence->get();
148 | $event = null;
149 |
150 | while (true) {
151 | try {
152 | // if previous sequence was processed - fetch the next sequence and set
153 | // that we have successfully processed the previous sequence
154 | // typically, this will be true
155 | // this prevents the sequence getting too far forward if an exception
156 | // is thrown from the WorkHandler
157 | if ($processedSequence) {
158 | $processedSequence = false;
159 | $nextSequence = $this->workSequence->incrementAndGet();
160 | $this->sequence->set($nextSequence - 1);
161 | }
162 |
163 | if ($cachedAvailableSequence >= $nextSequence) {
164 | $event = $this->ringBuffer->get($nextSequence);
165 | $this->workHandler->onEvent($event);
166 | $processedSequence = true;
167 | } else {
168 | $cachedAvailableSequence = $this->sequenceBarrier->waitFor($nextSequence);
169 | }
170 | } catch (Exception\AlertException $e) {
171 | if (!$this->isRunning()) {
172 | break;
173 | }
174 | } catch (\Exception $e) {
175 | $this->exceptionHandler->handleEventException($e, $nextSequence, $event);
176 | $processedSequence = true;
177 | }
178 | }
179 |
180 | $this->_notifyShutdown();
181 | $this->running = false;
182 | }
183 |
184 | /**
185 | * @return void
186 | */
187 | public function _notifyStart() // private !! only public for pthreads reasons
188 | {
189 | if ($this->workHandler instanceof LifecycleAwareInterface) {
190 | try {
191 | $this->workHandler->onStart();
192 | } catch (\Exception $e) {
193 | $this->exceptionHandler->handleOnStartException($e);
194 | }
195 | }
196 | }
197 |
198 | /**
199 | * @return void
200 | */
201 | public function _notifyShutdown() // private !! only public for pthreads reasons
202 | {
203 | if ($this->workHandler instanceof LifecycleAwareInterface) {
204 | try {
205 | $this->workHandler->onShutdown();
206 | } catch (\Exception $e) {
207 | $this->exceptionHandler->handleOnShutdownException($e);
208 | }
209 | }
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/EventTranslatorInterface.php:
--------------------------------------------------------------------------------
1 | fh = fopen($file, 'a+b');
30 | }
31 |
32 | public function __destruct()
33 | {
34 | fclose($this->fh);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/ExceptionHandler/ExceptionHandlerInterface.php:
--------------------------------------------------------------------------------
1 | fh, 'ERR: Exception processing: ' . $sequence . ' ' . $event);
15 | throw new Exception\RuntimeException('', 0, $ex);
16 | }
17 |
18 | /**
19 | * @inheritdoc
20 | */
21 | public function handleOnStartException(\Exception $ex)
22 | {
23 | fwrite($this->fh, 'ERR: Exception during onStart()');
24 | }
25 |
26 | /**
27 | * @inheritdoc
28 | */
29 | public function handleOnShutdownException(\Exception $ex)
30 | {
31 | fwrite($this->fh, 'ERR: Exception during onShutdown()');
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/ExceptionHandler/IgnoreExceptionHandler.php:
--------------------------------------------------------------------------------
1 | fh, 'INFO: Exception processing: ' . $sequence . ' ' . $event);
13 | }
14 |
15 | /**
16 | * @inheritdoc
17 | */
18 | public function handleOnStartException(\Exception $ex)
19 | {
20 | fwrite($this->fh, 'INFO: Exception during onStart()');
21 | }
22 |
23 | /**
24 | * @inheritdoc
25 | */
26 | public function handleOnShutdownException(\Exception $ex)
27 | {
28 | fwrite($this->fh, 'INFO: Exception during onShutdown()');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/FixedSequenceGroup.php:
--------------------------------------------------------------------------------
1 | sequences = $sequences;
30 | }
31 |
32 | /**
33 | * Get the minimum sequence value for the group.
34 | *
35 | * @return int the minimum sequence value for the group.
36 | */
37 | public function get()
38 | {
39 | return Util::getMinimumSequence($this->sequences);
40 | }
41 |
42 | /**
43 | * Not supported.
44 | *
45 | * @throws Exception\UnsupportedMethodCallException
46 | */
47 | public function set($value)
48 | {
49 | throw new Exception\UnsupportedMethodCallException('not supported');
50 | }
51 |
52 | /**
53 | * Not supported.
54 | *
55 | * @throws Exception\UnsupportedMethodCallException
56 | */
57 | public function compareAndSwap($oldValue, $newValue)
58 | {
59 | throw new Exception\UnsupportedMethodCallException('not supported');
60 | }
61 |
62 | /**
63 | * Not supported.
64 | *
65 | * @throws Exception\UnsupportedMethodCallException
66 | */
67 | public function incrementAndGet()
68 | {
69 | throw new Exception\UnsupportedMethodCallException('not supported');
70 | }
71 |
72 | /**
73 | * Not supported.
74 | *
75 | * @throws Exception\UnsupportedMethodCallException
76 | */
77 | public function addAndGet($increment)
78 | {
79 | throw new Exception\UnsupportedMethodCallException('not supported');
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/LifecycleAwareInterface.php:
--------------------------------------------------------------------------------
1 | add($entities);
22 | } else if (is_array($entities) || $entities instanceof Traversable) {
23 | foreach ($entities as $entity) {
24 | $this->add($entity);
25 | }
26 | } else if (null !== $entities) {
27 | throw new Exception\InvalidArgumentException(sprintf(
28 | 'Parameter provided to %s must be an %s, %s or %s',
29 | __METHOD__, 'array', 'Traversable', 'PhpDisruptor\EventHandlerInterface'
30 | ));
31 | }
32 | }
33 |
34 | /**
35 | * @param EventHandlerInterface $entity
36 | */
37 | public function add(EventHandlerInterface $entity)
38 | {
39 | $this[] = $entity;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/Lists/EventProcessorList.php:
--------------------------------------------------------------------------------
1 | add($entities);
22 | } else if (is_array($entities) || $entities instanceof Traversable) {
23 | foreach ($entities as $entity) {
24 | $this->add($entity);
25 | }
26 | } else if (null !== $entities) {
27 | throw new Exception\InvalidArgumentException(sprintf(
28 | 'Parameter provided to %s must be an %s, %s or %s',
29 | __METHOD__, 'array', 'Traversable', 'PhpDisruptor\EventProcessor\AbstractEventProcessor'
30 | ));
31 | }
32 | }
33 |
34 | /**
35 | * @param AbstractEventProcessor $entity
36 | */
37 | public function add(AbstractEventProcessor $entity)
38 | {
39 | $this[] = $entity;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/Lists/EventTranslatorList.php:
--------------------------------------------------------------------------------
1 | add($entities);
22 | } else if (is_array($entities) || $entities instanceof Traversable) {
23 | foreach ($entities as $entity) {
24 | $this->add($entity);
25 | }
26 | } else if (null !== $entities) {
27 | throw new Exception\InvalidArgumentException(sprintf(
28 | 'Parameter provided to %s must be an %s, %s or %s',
29 | __METHOD__, 'array', 'Traversable', 'PhpDisruptor\EventTranslatorInterface'
30 | ));
31 | }
32 | }
33 |
34 | /**
35 | * @param EventTranslatorInterface $entity
36 | */
37 | public function add(EventTranslatorInterface $entity)
38 | {
39 | $this[] = $entity;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/Lists/SequenceList.php:
--------------------------------------------------------------------------------
1 | add($entities);
22 | } else if (is_array($entities) || $entities instanceof Traversable) {
23 | foreach ($entities as $entity) {
24 | $this->add($entity);
25 | }
26 | } else if (null !== $entities) {
27 | throw new Exception\InvalidArgumentException(sprintf(
28 | 'Parameter provided to %s must be an %s, %s or %s',
29 | __METHOD__, 'array', 'Traversable', 'PhpDisruptor\Sequence'
30 | ));
31 | }
32 | }
33 |
34 | /**
35 | * @param Sequence $entity
36 | */
37 | public function add(Sequence $entity)
38 | {
39 | $this[] = $entity;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/Lists/WorkHandlerList.php:
--------------------------------------------------------------------------------
1 | add($entities);
22 | } else if (is_array($entities) || $entities instanceof Traversable) {
23 | foreach ($entities as $entity) {
24 | $this->add($entity);
25 | }
26 | } else if (null !== $entities) {
27 | throw new Exception\InvalidArgumentException(sprintf(
28 | 'Parameter provided to %s must be an %s, %s or %s',
29 | __METHOD__, 'array', 'Traversable', 'PhpDisruptor\WorkHandlerInterface'
30 | ));
31 | }
32 | }
33 |
34 | /**
35 | * @param WorkHandlerInterface $entity
36 | */
37 | public function add(WorkHandlerInterface $entity)
38 | {
39 | $this[] = $entity;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/MultiProducerSequencer.php:
--------------------------------------------------------------------------------
1 | gatingSequenceCache = new Sequence();
41 |
42 | $this->indexMask = $bufferSize - 1;
43 | $this->indexShift = Util::log2($bufferSize);
44 |
45 | $this->availableBuffer = new Threaded();
46 | for ($i = 0; $i < $bufferSize; $i++) {
47 | $this->availableBuffer[$i] = -1;
48 | }
49 | }
50 |
51 | /**
52 | * @param int $requiredCapacity
53 | * @return bool
54 | * @throws Exception\InvalidArgumentException
55 | */
56 | public function hasAvailableCapacity($requiredCapacity)
57 | {
58 | return $this->_internalHasAvailableCapacity($this->getSequences(), $requiredCapacity, $this->cursor->get());
59 | }
60 |
61 | /**
62 | * @param SequenceList $gatingSequences
63 | * @param int $requiredCapacity
64 | * @param int $cursorValue
65 | * @return bool
66 | */
67 | public function _internalHasAvailableCapacity(SequenceList $gatingSequences, $requiredCapacity, $cursorValue) // private !! only public for pthreads reasons
68 | {
69 | $wrapPoint = ($cursorValue + $requiredCapacity) - $this->bufferSize;
70 | $cachedGatingSequence = $this->gatingSequenceCache->get();
71 |
72 | if ($wrapPoint > $cachedGatingSequence || $cachedGatingSequence > $cursorValue) {
73 | $minSequence = Util::getMinimumSequence($gatingSequences, $cursorValue);
74 | $this->gatingSequenceCache->set($minSequence);
75 | if ($wrapPoint > $minSequence) {
76 | return false;
77 | }
78 | }
79 | return true;
80 | }
81 |
82 | /**
83 | * @inheritdoc
84 | */
85 | public function claim($sequence)
86 | {
87 | $this->cursor->set($sequence);
88 | }
89 |
90 | /**
91 | * @inheritdoc
92 | */
93 | public function next($n = 1)
94 | {
95 | if ($n < 1) {
96 | throw new Exception\InvalidArgumentException('$n must be > 0');
97 | }
98 |
99 | do {
100 | $current = $this->cursor->get();
101 | $next = $current + $n;
102 |
103 | $wrapPoint = $next - $this->bufferSize;
104 | $cachedGatingSequence = $this->gatingSequenceCache->get();
105 |
106 | if ($wrapPoint > $cachedGatingSequence || $cachedGatingSequence > $current) {
107 | $gatingSequence = Util::getMinimumSequence($this->getSequences(), $current);
108 |
109 | if ($wrapPoint > $gatingSequence) {
110 | $this->wait(1);
111 | continue;
112 | }
113 |
114 | $this->gatingSequenceCache->set($gatingSequence);
115 | } elseif ($this->cursor->compareAndSwap($current, $next)) {
116 | break;
117 | }
118 | } while (true);
119 |
120 | return $next;
121 | }
122 |
123 | /**
124 | * @inheritdoc
125 | */
126 | public function tryNext($n = 1)
127 | {
128 | if ($n < 1) {
129 | throw new Exception\InvalidArgumentException('$n must be > 0');
130 | }
131 |
132 | do {
133 | $current = $this->cursor->get();
134 | $next = $current + $n;
135 |
136 | if (!$this->_internalHasAvailableCapacity($this->getSequences(), $n, $current)) {
137 | throw new Exception\InsufficientCapacityException('insufficient capacity');
138 | }
139 | } while (!$this->cursor->compareAndSwap($current, $next));
140 |
141 | return $next;
142 | }
143 |
144 | /**
145 | * @inheritdoc
146 | */
147 | public function remainingCapacity()
148 | {
149 | $consumed = Util::getMinimumSequence($this->getSequences(), $this->cursor->get());
150 | $produced = $this->cursor->get();
151 | return $this->getBufferSize() - ($produced - $consumed);
152 | }
153 |
154 | /**
155 | * @inheritdoc
156 | */
157 | public function publish($low, $high = null)
158 | {
159 | if (null === $high) {
160 | $this->_setAvailable($low);
161 | } else {
162 | for ($l = $low; $l <= $high; $l++) {
163 | $this->_setAvailable($l);
164 | }
165 | }
166 | $this->waitStrategy->signalAllWhenBlocking();
167 | }
168 |
169 | /**
170 | * The below methods work on the availableBuffer flag.
171 | *
172 | * The prime reason is to avoid a shared sequence object between publisher threads.
173 | * (Keeping single pointers tracking start and end would require coordination
174 | * between the threads).
175 | *
176 | * -- Firstly we have the constraint that the delta between the cursor and minimum
177 | * gating sequence will never be larger than the buffer size (the code in
178 | * next/tryNext in the Sequence takes care of that).
179 | * -- Given that; take the sequence value and mask off the lower portion of the
180 | * sequence as the index into the buffer (indexMask). (aka modulo operator)
181 | * -- The upper portion of the sequence becomes the value to check for availability.
182 | * ie: it tells us how many times around the ring buffer we've been (aka division)
183 | * -- Because we can't wrap without the gating sequences moving forward (i.e. the
184 | * minimum gating sequence is effectively our last available position in the
185 | * buffer), when we have new data and successfully claimed a slot we can simply
186 | * write over the top.
187 | *
188 | * @param int $sequence
189 | * @return void
190 | */
191 | public function _setAvailable($sequence) // private !! only public for pthreads reasons
192 | {
193 | $this->_setAvailableBufferValue($this->_calculateIndex($sequence), $this->_calculateAvailabilityFlag($sequence));
194 | }
195 |
196 | /**
197 | * @param int $index
198 | * @param int $flag
199 | * @return void
200 | */
201 | public function _setAvailableBufferValue($index, $flag) // private !! only public for pthreads reasons
202 | {
203 | do {
204 | $oldAvailableBuffer = $this->availableBuffer;
205 | $newAvailableBuffer = new Threaded();
206 | $newAvailableBuffer->merge($oldAvailableBuffer);
207 | $newAvailableBuffer[$index] = $flag;
208 | } while (!$this->casMember('availableBuffer', $oldAvailableBuffer, $newAvailableBuffer));
209 | }
210 |
211 | /**
212 | * @inheritdoc
213 | */
214 | public function isAvailable($sequence)
215 | {
216 | $index = $this->_calculateIndex($sequence);
217 | $flag = $this->_calculateAvailabilityFlag($sequence);
218 |
219 | return $this->availableBuffer[$index] == $flag;
220 | }
221 |
222 | /**
223 | * @param int $lowerBound
224 | * @param int $availableSequence
225 | * @return int
226 | */
227 | public function getHighestPublishedSequence($lowerBound, $availableSequence)
228 | {
229 | for ($sequence = $lowerBound; $sequence <= $availableSequence; $sequence++) {
230 | if (!$this->isAvailable($sequence)) {
231 | return $sequence - 1 ;
232 | }
233 | }
234 | return $availableSequence;
235 | }
236 |
237 | /**
238 | * @param int $sequence
239 | * @return int
240 | */
241 | public function _calculateAvailabilityFlag($sequence) // private !! only public for pthreads reasons
242 | {
243 | return (int) ($sequence >> $this->indexShift);
244 | }
245 |
246 | /**
247 | * @param int $sequence
248 | * @return int
249 | */
250 | public function _calculateIndex($sequence) // private !! only public for pthreads reasons
251 | {
252 | return (int) ($sequence & $this->indexMask);
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/ProcessingSequenceBarrier.php:
--------------------------------------------------------------------------------
1 | sequencer = $sequencer;
60 | $this->waitStrategy = $waitStrategy;
61 | $this->cursorSequence = $cursorSequence;
62 | $this->alerted = false;
63 |
64 | if (0 == count($dependentSequences)) {
65 | $this->dependentSequence = $cursorSequence;
66 | } else {
67 | $this->dependentSequence = new FixedSequenceGroup($dependentSequences);
68 | }
69 | }
70 |
71 | /**
72 | * Wait for the given sequence to be available for consumption.
73 | *
74 | * @param int $sequence to wait for
75 | * @return int the sequence up to which is available
76 | * @throws Exception\AlertException if a status change has occurred for the Disruptor
77 | * @throws Exception\InterruptedException if the thread needs awaking on a condition variable.
78 | * @throws Exception\TimeoutException
79 | */
80 | public function waitFor($sequence)
81 | {
82 | $this->checkAlert();
83 |
84 | $availableSequence = $this->waitStrategy->waitFor(
85 | $sequence,
86 | $this->cursorSequence,
87 | $this->dependentSequence,
88 | $this
89 | );
90 |
91 | if ($availableSequence < $sequence) {
92 | return $availableSequence;
93 | }
94 |
95 | return $this->sequencer->getHighestPublishedSequence($sequence, $availableSequence);
96 | }
97 |
98 | /**
99 | * Get the current cursor value that can be read.
100 | *
101 | * @return int value of the cursor for entries that have been published.
102 | */
103 | public function getCursor()
104 | {
105 | return $this->dependentSequence->get();
106 | }
107 |
108 | /**
109 | * The current alert status for the barrier.
110 | *
111 | * @return bool true if in alert otherwise false.
112 | */
113 | public function isAlerted()
114 | {
115 | return $this->alerted;
116 | }
117 |
118 | /**
119 | * Alert the EventProcessors of a status change and stay in this status until cleared.
120 | *
121 | * @return void
122 | */
123 | public function alert()
124 | {
125 | $this->alerted = true;
126 | $this->waitStrategy->signalAllWhenBlocking();
127 | }
128 |
129 | /**
130 | * Clear the current alert status.
131 | *
132 | * @return void
133 | */
134 | public function clearAlert()
135 | {
136 | $this->alerted = false;
137 | }
138 |
139 | /**
140 | * Check if an alert has been raised and throw an AlertException if it has.
141 | *
142 | * @return void
143 | * @throws Exception\AlertException if alert has been raised.
144 | */
145 | public function checkAlert()
146 | {
147 | if (true === $this->alerted) {
148 | throw new Exception\AlertException('alert');
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/Sequence.php:
--------------------------------------------------------------------------------
1 | set($initialValue);
28 | }
29 |
30 | /**
31 | * Perform a volatile read of this sequence's value.
32 | *
33 | * @return int The current value of the sequence.
34 | */
35 | public function get()
36 | {
37 | return $this->value;
38 | }
39 |
40 | /**
41 | * Perform an ordered write of this sequence. The intent is
42 | * a Store/Store barrier between this write and any previous
43 | * store.
44 | *
45 | * @param int $value The new value for the sequence.
46 | * @return void
47 | */
48 | public function set($value)
49 | {
50 | $this->value = $value;
51 | }
52 |
53 | /**
54 | * Perform a compare and swap operation on the sequence.
55 | *
56 | * @param int $oldValue The expected current value.
57 | * @param int $newValue The value to update to.
58 | * @return bool true if the operation succeeds, false otherwise.
59 | */
60 | public function compareAndSwap($oldValue, $newValue)
61 | {
62 | return $this->casMember('value', $oldValue, $newValue);
63 | }
64 |
65 | /**
66 | * Atomically increment the sequence by one.
67 | *
68 | * @return int The value after the increment
69 | */
70 | public function incrementAndGet()
71 | {
72 | return $this->addAndGet(1);
73 | }
74 |
75 | /**
76 | * Atomically add the supplied value.
77 | *
78 | * @param int $increment The value to add to the sequence.
79 | * @return int The value after the increment.
80 | * @throws Exception\InvalidArgumentException
81 | */
82 | public function addAndGet($increment)
83 | {
84 | do {
85 | $currentValue = $this->get();
86 | $newValue = $currentValue + $increment;
87 | } while (!$this->compareAndSwap($currentValue, $newValue));
88 |
89 | return $newValue;
90 | }
91 |
92 | /**
93 | * @return string
94 | */
95 | public function __toString()
96 | {
97 | return (string) $this->get();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/SequenceAggregateInterface.php:
--------------------------------------------------------------------------------
1 | value = self::INITIAL_VALUE;
19 | $this->sequences = new SequenceList();
20 | }
21 |
22 | /**
23 | * Get the sequences
24 | *
25 | * @return Sequence[]
26 | */
27 | public function getSequences()
28 | {
29 | return $this->sequences;
30 | }
31 |
32 | /**
33 | * Get the minimum sequence value for the group.
34 | *
35 | * @return int the minimum sequence value for the group.
36 | */
37 | public function get()
38 | {
39 | return Util::getMinimumSequence($this->sequences);
40 | }
41 |
42 | /**
43 | * Set all Sequences in the group to a given value.
44 | *
45 | * @param int $value to set the group of sequences to.
46 | * @return void
47 | */
48 | public function set($value)
49 | {
50 | foreach ($this->sequences as $sequence) {
51 | $sequence->set($value);
52 | }
53 | }
54 |
55 | /**
56 | * Add a Sequence into this aggregate. This should only be used during
57 | * initialisation. Use {@link SequenceGroup#addWhileRunning(Cursored, Sequence)}
58 | *
59 | * @param Sequence $sequence to be added to the aggregate.
60 | * @return void
61 | */
62 | public function add(Sequence $sequence)
63 | {
64 | $this->sequences[] = $sequence;
65 | }
66 |
67 | /**
68 | * Remove the first occurrence of the Sequence from this aggregate.
69 | *
70 | * @param Sequence $sequence to be removed from this aggregate.
71 | * @return bool true if the sequence was removed otherwise false.
72 | */
73 | public function remove(Sequence $sequence)
74 | {
75 | return SequenceGroups::removeSequence($this, $sequence);
76 | }
77 |
78 | /**
79 | * Get the size of the group.
80 | *
81 | * @return int the size of the group.
82 | */
83 | public function count()
84 | {
85 | return count($this->sequences);
86 | }
87 |
88 | /**
89 | * Adds a sequence to the sequence group after threads have started to publish to
90 | * the Disruptor. It will set the sequences to cursor value of the ringBuffer
91 | * just after adding them. This should prevent any nasty rewind/wrapping effects.
92 | *
93 | * @param CursoredInterface $cursored The data structure that the owner of this sequence group will
94 | * be pulling it's events from.
95 | * @param Sequence $sequence The sequence to add.
96 | * @return void
97 | */
98 | public function addWhileRunning(CursoredInterface $cursored, Sequence $sequence)
99 | {
100 | $sequencesToAdd = new SequenceList();
101 | $sequencesToAdd[] = $sequence;
102 | SequenceGroups::addSequences($this, $cursored, $sequencesToAdd);
103 | }
104 |
105 | /**
106 | * @inheritdoc
107 | */
108 | public function casSequences(SequenceList $oldSequences, SequenceList $newSequences)
109 | {
110 | return Util::casSequences($this, $oldSequences, $newSequences);
111 | }
112 |
113 | /**
114 | * @param SequenceList $sequences
115 | * @return void
116 | */
117 | public function setSequences(SequenceList $sequences)
118 | {
119 | $this->sequences = $sequences;
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/SequenceGroups.php:
--------------------------------------------------------------------------------
1 | getSequences();
23 | $updatedSequences = new SequenceList();
24 | $updatedSequences->merge($currentSequences);
25 | $cursorSequence = $cursor->getCursor();
26 |
27 | foreach ($sequencesToAdd as $sequence) {
28 | $sequence->set($cursorSequence);
29 | $updatedSequences[] = $sequence;
30 | }
31 |
32 | } while (!$sequenceAggregate->casSequences($currentSequences, $updatedSequences));
33 |
34 | $cursorSequence = $cursor->getCursor();
35 | foreach ($sequencesToAdd as $sequence) {
36 | $sequence->set($cursorSequence);
37 | }
38 | }
39 |
40 | /**
41 | * @param SequenceAggregateInterface $sequenceAggregate
42 | * @param Sequence $sequence
43 | * @return bool
44 | */
45 | public static function removeSequence(SequenceAggregateInterface $sequenceAggregate, Sequence $sequence)
46 | {
47 | do {
48 | $oldSequences = $sequenceAggregate->getSequences();
49 | $numToRemove = self::countMatching($oldSequences, $sequence);
50 | if (0 == $numToRemove) {
51 | break;
52 | }
53 |
54 | $oldSize = count($oldSequences);
55 |
56 | $newSequences = new SequenceList();
57 | for ($i = 0, $pos = 0; $i < $oldSize; $i++) {
58 | $testSequence = $oldSequences[$i];
59 | if ($testSequence !== $sequence) {
60 | $newSequences[$pos++] = $testSequence;
61 | }
62 | }
63 | } while (!$sequenceAggregate->casSequences($oldSequences, $newSequences));
64 | return $numToRemove != 0;
65 | }
66 |
67 | /**
68 | * @param SequenceList $sequences
69 | * @param Sequence $sequence
70 | * @return int
71 | */
72 | public static function countMatching(SequenceList $sequences, Sequence $sequence)
73 | {
74 | $numToRemove = 0;
75 | foreach ($sequences as $sequenceToTest) {
76 | if ($sequenceToTest == $sequence) {
77 | $numToRemove++;
78 | }
79 | }
80 | return $numToRemove;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/SequenceReportingEventHandlerInterface.php:
--------------------------------------------------------------------------------
1 | sequencer = $sequencer;
21 | }
22 |
23 | /**
24 | * @return int
25 | */
26 | public function get()
27 | {
28 | return $this->sequencer->getCursor();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/SequencerInterface.php:
--------------------------------------------------------------------------------
1 | requiredCapacity slots
38 | * available.
39 | *
40 | * @param int|null $n
41 | * @return int the claimed sequence value
42 | * @throws Exception\InsufficientCapacityException
43 | */
44 | public function tryNext($n = null);
45 |
46 | /**
47 | * Get the remaining capacity for this sequencer.
48 | *
49 | * @return int The number of slots remaining.
50 | */
51 | public function remainingCapacity();
52 |
53 | /**
54 | * Claim a specific sequence. Only used if initialising the ring buffer to
55 | * a specific value.
56 | *
57 | * @param int $sequence The sequence to initialise too.
58 | * @return void
59 | */
60 | public function claim($sequence);
61 |
62 | /**
63 | * Publishes a sequence. Call when the event has been filled.
64 | *
65 | * @param int $low first sequence number to publish
66 | * @param int|null $high last sequence number to publish (optional)
67 | * @return void
68 | */
69 | public function publish($low, $high = null);
70 |
71 | /**
72 | * Confirms if a sequence is published and the event is available for use; non-blocking.
73 | *
74 | * @param int $sequence of the buffer to check
75 | * @return bool true if the sequence is available for use, false if not
76 | */
77 | public function isAvailable($sequence);
78 |
79 | /**
80 | * Add the specified gating sequences to this instance of the Disruptor. They will
81 | * safely and atomically added to the list of gating sequences.
82 | *
83 | * @param SequenceList $gatingSequences The sequences to add.
84 | * @return void
85 | * @throws Exception\InvalidArgumentException
86 | */
87 | public function addGatingSequences(SequenceList $gatingSequences);
88 |
89 | /**
90 | * Remove the specified sequence from this sequencer.
91 | *
92 | * @param Sequence $sequence to be removed.
93 | * @return bool true if this sequence was found, false otherwise.
94 | */
95 | public function removeGatingSequence(Sequence $sequence);
96 |
97 | /**
98 | * Create a new SequenceBarrier to be used by an EventProcessor to track which messages
99 | * are available to be read from the ring buffer given a list of sequences to track.
100 | *
101 | * @see SequenceBarrierInterface
102 | * @param SequenceList $sequencesToTrack
103 | * @return SequenceBarrierInterface A sequence barrier that will track the specified sequences.
104 | */
105 | public function newBarrier(SequenceList $sequencesToTrack = null);
106 |
107 | /**
108 | * Get the minimum sequence value from all of the gating sequences
109 | * added to this ringBuffer.
110 | *
111 | * @return int The minimum gating sequence or the cursor sequence if
112 | * no sequences have been added.
113 | */
114 | public function getMinimumSequence();
115 |
116 | /**
117 | * Get the highest sequence value from all the gatting sequences
118 | * added to this ringBuffer.
119 | *
120 | * @param int $sequence
121 | * @param int $availableSequence
122 | * @return int The highest gating sequence or the cursor sequence if
123 | * no sequences have been added.
124 | */
125 | public function getHighestPublishedSequence($sequence, $availableSequence);
126 | }
127 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/SingleProducerSequencer.php:
--------------------------------------------------------------------------------
1 | nextValue = Sequence::INITIAL_VALUE;
33 | $this->cachedValue = Sequence::INITIAL_VALUE;
34 | }
35 |
36 | /**
37 | * @param int $requiredCapacity
38 | * @return bool
39 | */
40 | public function hasAvailableCapacity($requiredCapacity)
41 | {
42 | $nextValue = $this->nextValue;
43 |
44 | $wrapPoint = ($nextValue + $requiredCapacity) - $this->bufferSize;
45 | $cachedGatingSequence = $this->cachedValue;
46 |
47 | if ($wrapPoint > $cachedGatingSequence || $cachedGatingSequence > $nextValue) {
48 | $minSequence = Util::getMinimumSequence($this->getSequences(), $nextValue);
49 | $this->cachedValue = $minSequence;
50 |
51 | if ($wrapPoint > $minSequence) {
52 | return false;
53 | }
54 | }
55 |
56 | return true;
57 | }
58 |
59 | /**
60 | * @param int $n
61 | * @return int
62 | * @throws Exception\InvalidArgumentException
63 | */
64 | public function next($n = 1)
65 | {
66 | if ($n < 1) {
67 | throw new Exception\InvalidArgumentException('$n must be > 0');
68 | }
69 |
70 | $nextValue = $this->nextValue;
71 |
72 | $nextSequence = $nextValue + $n;
73 | $wrapPoint = $nextSequence - $this->bufferSize;
74 | $cachedGatingSequence = $this->cachedValue;
75 |
76 | if ($wrapPoint > $cachedGatingSequence || $cachedGatingSequence > $nextValue) {
77 |
78 | while ($wrapPoint > ($minSequence = Util::getMinimumSequence($this->getSequences(), $nextValue))) {
79 | $this->wait(1);
80 | }
81 | $this->cachedValue = $minSequence;
82 | }
83 |
84 | $this->nextValue = $nextSequence;
85 |
86 | return $nextSequence;
87 | }
88 |
89 | /**
90 | * @param int $n
91 | * @return int
92 | * @throws Exception\InvalidArgumentException
93 | * @throws Exception\InsufficientCapacityException
94 | */
95 | public function tryNext($n = 1)
96 | {
97 | if ($n < 1) {
98 | throw new Exception\InvalidArgumentException('$n must be > 0');
99 | }
100 |
101 | if (!$this->hasAvailableCapacity($n)) {
102 | throw new Exception\InsufficientCapacityException('insufficient capacity');
103 | }
104 |
105 | $nextSequence = $this->nextValue += $n;
106 | return $nextSequence;
107 | }
108 |
109 | /**
110 | * @return int
111 | */
112 | public function remainingCapacity()
113 | {
114 | $nextValue = $this->nextValue;
115 |
116 | $consumed = Util::getMinimumSequence($this->getSequences(), $nextValue);
117 | $produced = $nextValue;
118 |
119 | return $this->getBufferSize() - ($produced - $consumed);
120 | }
121 |
122 | /**
123 | * @param int
124 | * @return void
125 | */
126 | public function claim($sequence)
127 | {
128 | $this->nextValue = $sequence;
129 | }
130 |
131 | /**
132 | * @param int $low
133 | * @param int|null $high will get ignored in this implementation
134 | * @return void
135 | */
136 | public function publish($low, $high = null)
137 | {
138 | $this->cursor->set($low);
139 | $this->waitStrategy->signalAllWhenBlocking();
140 | }
141 |
142 | /**
143 | * @param int $sequence
144 | * @return bool
145 | */
146 | public function isAvailable($sequence)
147 | {
148 | return $sequence <= $this->cursor->get();
149 | }
150 |
151 | /**
152 | * @param int $lowerBound
153 | * @param int $availableSequence
154 | * @return int
155 | */
156 | public function getHighestPublishedSequence($lowerBound, $availableSequence)
157 | {
158 | return $availableSequence;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/TimeoutHandlerInterface.php:
--------------------------------------------------------------------------------
1 |
17 | * From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
18 | *
19 | * @param int $x Value to round up
20 | * @return int The next power of 2 from x inclusive
21 | * @throws Exception\InvalidArgumentException
22 | */
23 | public static function ceilingNextPowerOfTwo($x)
24 | {
25 | $size = PHP_INT_SIZE * 8;
26 | $binary = str_pad(decbin($x -1), $size, 0, STR_PAD_LEFT);
27 | $numberOfLeadingZeros = strpos($binary, '1');
28 |
29 | return 1 << ($size - $numberOfLeadingZeros);
30 | }
31 |
32 | /**
33 | * Get the minimum sequence from an SequenceList
34 | *
35 | * @param SequenceList $sequences to compare
36 | * @param int|null $minimum
37 | * @return int the minimum sequence found or PHP_INT_MAX if the sequence list is empty
38 | * @throws Exception\InvalidArgumentException
39 | */
40 | public static function getMinimumSequence(SequenceList $sequences, $minimum = PHP_INT_MAX)
41 | {
42 | foreach ($sequences as $sequence) {
43 | $value = $sequence->get();
44 | $minimum = min($minimum, $value);
45 | }
46 | return $minimum;
47 | }
48 |
49 | /**
50 | * Get a SequenceList of Sequences for the passed EventProcessors
51 | *
52 | * @param EventProcessorList $processors for which to get the sequences
53 | * @return SequenceList of Sequences
54 | */
55 | public static function getSequencesFor(EventProcessorList $processors)
56 | {
57 | $sequences = new SequenceList();
58 | foreach ($processors as $eventProcessor) {
59 | $sequences->add($eventProcessor->getSequence());
60 | }
61 | return $sequences;
62 | }
63 |
64 | /**
65 | * @param SequenceAggregateInterface $sequenceAggregate
66 | * @param SequenceList $oldSequences
67 | * @param SequenceList $newSequences
68 | * @return bool
69 | */
70 | public static function casSequences(
71 | SequenceAggregateInterface $sequenceAggregate,
72 | SequenceList $oldSequences,
73 | SequenceList $newSequences
74 | ) {
75 | $set = false;
76 | $sequenceAggregate->lock();
77 | if ($sequenceAggregate->getSequences() == $oldSequences) {
78 | $sequenceAggregate->setSequences($newSequences);
79 | $set = true;
80 | }
81 | $sequenceAggregate->unlock();
82 | return $set;
83 | }
84 |
85 | /**
86 | * Calculate the log base 2 of the supplied integer, essentially reports the location
87 | * of the highest bit.
88 | *
89 | * @param int $i Value to calculate log2 for.
90 | * @return int The log2 value
91 | * @throws Exception\InvalidArgumentException
92 | */
93 | public static function log2($i)
94 | {
95 | $r = 0;
96 | while (($i >>= 1) != 0) {
97 | ++$r;
98 | }
99 | return $r;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/WaitStrategy/BlockingWaitStrategy.php:
--------------------------------------------------------------------------------
1 | mutex = Mutex::create(false);
32 | $this->cond = Cond::create();
33 | }
34 |
35 | /**
36 | * @inheritdoc
37 | */
38 | public function waitFor(
39 | $sequence,
40 | Sequence $cursor,
41 | Sequence $dependentSequence,
42 | SequenceBarrierInterface $barrier
43 | ) {
44 | if (($availableSequence = $cursor->get()) < $sequence) {
45 | Mutex::lock($this->mutex);
46 | try {
47 | while (($availableSequence = $cursor->get()) < $sequence) {
48 | $barrier->checkAlert();
49 | Cond::wait($this->cond, $this->mutex);
50 | }
51 | } catch (\Exception $e) {
52 | Mutex::unlock($this->mutex);
53 | }
54 | Mutex::unlock($this->mutex);
55 | }
56 |
57 | while (($availableSequence = $cursor->get()) < $sequence) {
58 | $barrier->checkAlert();
59 | }
60 |
61 | return $availableSequence;
62 | }
63 |
64 | /**
65 | * @inheritdoc
66 | */
67 | public function signalAllWhenBlocking()
68 | {
69 | Mutex::lock($this->mutex);
70 | Cond::broadcast($this->cond);
71 | Mutex::unlock($this->mutex);
72 | }
73 |
74 | /**
75 | * Destroy the mutex
76 | */
77 | public function __destruct()
78 | {
79 | Cond::destroy($this->cond);
80 | Mutex::destroy($this->mutex);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/WaitStrategy/BusySpinWaitStrategy.php:
--------------------------------------------------------------------------------
1 | get()) < $sequence) {
34 | $barrier->checkAlert();
35 | }
36 | return $availableSequence;
37 | }
38 |
39 | /**
40 | * @inheritdoc
41 | */
42 | public function signalAllWhenBlocking()
43 | {
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/WaitStrategy/SleepingWaitStrategy.php:
--------------------------------------------------------------------------------
1 | get()) < $sequence) {
31 |
32 | $barrier->checkAlert();
33 |
34 | if ($counter > 100) {
35 | --$counter;
36 | } else if ($counter > 0) {
37 | --$counter;
38 | $this->wait(1);
39 | } else {
40 | time_nanosleep(0, 1);
41 | }
42 | }
43 |
44 | return $availableSequence;
45 | }
46 |
47 | public function signalAllWhenBlocking()
48 | {
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/WaitStrategy/TimeoutBlockingWaitStrategy.php:
--------------------------------------------------------------------------------
1 | timeoutMicros = $timeUnit->toMicros($timeout);
39 | $this->mutex = Mutex::create(false);
40 | $this->cond = Cond::create();
41 | }
42 |
43 | /**
44 | * Wait for the given sequence to be available. It is possible for this method to return a value
45 | * less than the sequence number supplied depending on the implementation of the WaitStrategy. A common
46 | * use for this is to signal a timeout. Any EventProcessor that is using a WaitStragegy to get notifications
47 | * about message becoming available should remember to handle this case. The BatchEventProcessor explicitly
48 | * handles this case and will signal a timeout if required.
49 | *
50 | * @param int $sequence to be waited on.
51 | * @param Sequence $cursor the main sequence from ringbuffer. Wait/notify strategies will
52 | * need this as it's the only sequence that is also notified upon update.
53 | * @param Sequence $dependentSequence on which to wait.
54 | * @param SequenceBarrierInterface $barrier the processor is waiting on.
55 | * @return int the sequence that is available which may be greater than the requested sequence.
56 | * @throws Exception\AlertException if the status of the Disruptor has changed.
57 | * @throws Exception\InterruptedException if the thread is interrupted.
58 | * @throws Exception\TimeoutException
59 | */
60 | public function waitFor(
61 | $sequence,
62 | Sequence $cursor,
63 | Sequence $dependentSequence,
64 | SequenceBarrierInterface $barrier
65 | ) {
66 | $micros = $this->timeoutMicros;
67 |
68 | if (($availableSequence = $cursor->get()) < $sequence) {
69 | Mutex::lock($this->mutex);
70 | while (($availableSequence = $cursor->get()) < $sequence) {
71 | $barrier->checkAlert();
72 | $s = microtime();
73 | Cond::wait($this->cond, $this->mutex, $micros);
74 | $micros = (microtime() - $s) * 1000000 - $micros;
75 | if ($micros <= 0) {
76 | Mutex::unlock($this->mutex);
77 | throw new Exception\TimeoutException();
78 | }
79 | }
80 | Mutex::unlock($this->mutex);
81 | }
82 |
83 | while (($availableSequence = $dependentSequence->get()) < $sequence) {
84 | $barrier->checkAlert();
85 | }
86 |
87 | return $availableSequence;
88 | }
89 |
90 | /**
91 | * Implementations should signal the waiting EventProcessors that the cursor has advanced.
92 | *
93 | * @return void
94 | */
95 | public function signalAllWhenBlocking()
96 | {
97 | Mutex::lock($this->mutex);
98 | Cond::broadcoast($this->cond);
99 | Mutex::unlock($this->mutex);
100 | }
101 |
102 | public function __destruct()
103 | {
104 | Cond::destroy($this->cond);
105 | Mutex::destroy($this->mutex);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/WaitStrategy/WaitStrategyInterface.php:
--------------------------------------------------------------------------------
1 | get()) < $sequence) {
49 |
50 | $barrier->checkAlert();
51 |
52 | if (0 == $counter) {
53 | $this->wait(1);
54 | } else {
55 | --$counter;
56 | }
57 | }
58 |
59 | return $availableSequence;
60 | }
61 |
62 | /**
63 | * Implementations should signal the waiting EventProcessors that the cursor has advanced.
64 | *
65 | * @return void
66 | */
67 | public function signalAllWhenBlocking()
68 | {
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/PhpDisruptor/WorkHandlerInterface.php:
--------------------------------------------------------------------------------
1 | started = false;
57 | $this->workSequence = new Sequence(SequencerInterface::INITIAL_CURSOR_VALUE);
58 | $this->ringBuffer = $ringBuffer;
59 | $this->eventClass = $ringBuffer->getEventClass();
60 | $this->workProcessors = new Threaded();
61 | foreach ($workHandlers as $workHandler) {
62 | $this->workProcessors[] = new WorkProcessor(
63 | $ringBuffer,
64 | $sequenceBarrier,
65 | $workHandler,
66 | $exceptionHandler,
67 | $this->workSequence
68 | );
69 | }
70 | }
71 |
72 | /**
73 | * @return string
74 | */
75 | public function getEventClass()
76 | {
77 | return $this->eventClass;
78 | }
79 |
80 | /**
81 | * Create worker pool from ring buffer
82 | *
83 | * @param RingBuffer $ringBuffer
84 | * @param SequenceBarrierInterface $sequenceBarrier
85 | * @param ExceptionHandlerInterface $exceptionHandler
86 | * @param WorkHandlerList $workHandlers
87 | * @return WorkerPool
88 | */
89 | public static function createFromRingBuffer(
90 | RingBuffer $ringBuffer,
91 | SequenceBarrierInterface $sequenceBarrier,
92 | ExceptionHandlerInterface $exceptionHandler,
93 | WorkHandlerList $workHandlers
94 | ) {
95 | return new self($ringBuffer, $sequenceBarrier, $exceptionHandler, $workHandlers);
96 | }
97 |
98 | /**
99 | * Constructor
100 | *
101 | * @param EventFactoryInterface $eventFactory
102 | * @param ExceptionHandlerInterface $exceptionHandler
103 | * @param WorkHandlerList $workHandlers
104 | * @return WorkerPool
105 | */
106 | public static function createFromEventFactory(
107 | EventFactoryInterface $eventFactory,
108 | ExceptionHandlerInterface $exceptionHandler,
109 | WorkHandlerList $workHandlers
110 | ) {
111 | $ringBuffer = RingBuffer::createMultiProducer($eventFactory, 1024);
112 | $sequenceBarrier = $ringBuffer->newBarrier();
113 |
114 | $workerPool = new self($ringBuffer, $sequenceBarrier, $exceptionHandler, $workHandlers);
115 |
116 | $ringBuffer->addGatingSequences($workerPool->getWorkerSequences());
117 |
118 | return $workerPool;
119 | }
120 |
121 | /**
122 | * @return Sequence[]
123 | */
124 | public function getWorkerSequences()
125 | {
126 | $sequences = new SequenceList();
127 | foreach ($this->workProcessors as $workProcessor) {
128 | $sequences[] = $workProcessor->getSequence();
129 | }
130 | return $sequences;
131 | }
132 |
133 | /**
134 | * Start the worker pool processing events in sequence
135 | *
136 | * @return RingBuffer
137 | * @throws Exception\InvalidArgumentException
138 | */
139 | public function start()
140 | {
141 | if (!$this->casMember('started', false, true)) {
142 | throw new Exception\InvalidArgumentException(
143 | 'WorkerPool has already been started and cannot be restarted until halted'
144 | );
145 | }
146 |
147 | $cursor = $this->ringBuffer->getCursor();
148 | $this->workSequence->set($cursor);
149 |
150 | foreach ($this->workProcessors as $workProcessor) {
151 | $workProcessor->getSequence()->set($cursor);
152 | $workProcessor-start();
153 | }
154 |
155 | return $this->ringBuffer;
156 | }
157 |
158 | /**
159 | * Wait for the RingBuffer to drain of published events then halt the workers
160 | *
161 | * @return void
162 | */
163 | public function drainAndHalt()
164 | {
165 | $workerSequences = $this->getWorkerSequences();
166 |
167 | while ($this->ringBuffer->getCursor() > Util::getMinimumSequence($workerSequences)) {
168 | $this->wait(1);
169 | }
170 |
171 | $this->halt();
172 | }
173 |
174 | /**
175 | * Halt all workers immediately at the end of their current cycle
176 | *
177 | * @return void
178 | */
179 | public function halt()
180 | {
181 | foreach ($this->workProcessors as $workProcessor) {
182 | $workProcessor->halt();
183 | }
184 | $this->started = false;
185 | }
186 |
187 | /**
188 | * @return bool
189 | */
190 | public function isRunning()
191 | {
192 | return $this->started;
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/tests/Bootstrap.php:
--------------------------------------------------------------------------------
1 | add('PhpDisruptorTest\\', __DIR__);
37 | $loader->add('\\', __DIR__);
38 |
39 | unset($files, $file, $loader);
40 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/AggregateEventHandler/AggregateEventHandlerTest.php:
--------------------------------------------------------------------------------
1 | result = $result = new ResultCounter();
36 | $this->eventHandlerOne = new EventHandler('stdClass', $result);
37 | $this->eventHandlerTwo = new EventHandler('stdClass', $result);
38 | $this->eventHandlerThree = new EventHandler('stdClass', $result);
39 | }
40 |
41 | public function testShouldCallOnEventInSequence()
42 | {
43 | $event = new \stdClass();
44 | $sequence = 3;
45 | $endOfBatch = true;
46 |
47 | $aggregateEventHandler = $this->prepareAggregateEventHandler();
48 |
49 | $aggregateEventHandler->onEvent($event, $sequence, $endOfBatch);
50 |
51 | $this->assertEquals('123', $this->result->getResult());
52 | }
53 |
54 | public function testShouldCallOnStartInSequence()
55 | {
56 | $aggregateEventHandler = $this->prepareAggregateEventHandler();
57 |
58 | $aggregateEventHandler->onShutdown();
59 |
60 | $this->assertEquals('123', $this->result->getResult());
61 | }
62 |
63 | public function testShouldHandleEmptyListOfEventHandlers()
64 | {
65 | $handlers = new EventHandlerList();
66 | $aggregateEventHandler = new AggregateEventHandler('stdClass', $handlers);
67 | $event = new \stdClass();
68 | $aggregateEventHandler->onEvent($event, 0, true);
69 | $aggregateEventHandler->onStart();
70 | $aggregateEventHandler->onShutdown();
71 | }
72 |
73 | private function prepareAggregateEventHandler()
74 | {
75 | $handlers = new EventHandlerList(array(
76 | $this->eventHandlerOne,
77 | $this->eventHandlerTwo,
78 | $this->eventHandlerThree
79 | ));
80 |
81 | $aggregateEventHandler = new AggregateEventHandler(
82 | 'stdClass',
83 | $handlers
84 | );
85 | return $aggregateEventHandler;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/AggregateEventHandler/TestAsset/EventHandler.php:
--------------------------------------------------------------------------------
1 | eventClass = $eventClass;
31 | $this->result = $result;
32 | }
33 |
34 | /**
35 | * Return the used event class name
36 | *
37 | * @return string
38 | */
39 | public function getEventClass()
40 | {
41 | return $this->eventClass;
42 | }
43 |
44 | /**
45 | * Called when a publisher has published an event to the RingBuffer
46 | *
47 | * @param object $event published to the RingBuffer
48 | * @param int $sequence of the event being processed
49 | * @param bool $endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
50 | * @return void
51 | * @throws \Exception if the EventHandler would like the exception handled further up the chain.
52 | */
53 | public function onEvent($event, $sequence, $endOfBatch)
54 | {
55 | $this->result->appendToResult();
56 | }
57 |
58 | /**
59 | * Called once on thread start before first event is available.
60 | *
61 | * @return void
62 | */
63 | public function onStart()
64 | {
65 | $this->result->appendToResult();
66 | }
67 |
68 | /**
69 | * Called once just before the thread is shutdown.
70 | *
71 | * Sequence event processing will already have stopped before this method is called. No events will
72 | * be processed after this message.
73 | *
74 | * @return void
75 | */
76 | public function onShutdown()
77 | {
78 | $this->result->appendToResult();
79 | }
80 |
81 |
82 | /**
83 | * @return array
84 | */
85 | public function getResult()
86 | {
87 | return $this->result;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/AggregateEventHandler/TestAsset/ResultCounter.php:
--------------------------------------------------------------------------------
1 | count = 0;
22 | }
23 |
24 | public function incrementAndGet()
25 | {
26 | return ++$this->count;
27 | }
28 |
29 | public function appendToResult()
30 | {
31 | $this->result .= $this->incrementAndGet();
32 | }
33 |
34 | public function getResult()
35 | {
36 | return $this->result;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/Dsl/ConsumerRepository/ConsumerRepositoryTest.php:
--------------------------------------------------------------------------------
1 | eventFactory = new TestEventFactory();
61 | $this->consumerRepository = new ConsumerRepository($this->eventFactory);
62 |
63 | $sequence1 = new Sequence();
64 | $sequence2 = new Sequence();
65 | $this->eventProcessor1 = new TestEventProcessor($sequence1);
66 | $this->eventProcessor2 = new TestEventProcessor($sequence2);
67 |
68 | $this->handler1 = new SleepingEventHandler($this->eventFactory->getEventClass());
69 | $this->handler2 = new SleepingEventHandler($this->eventFactory->getEventClass());
70 |
71 | $ringBuffer = RingBuffer::createMultiProducer($this->eventFactory, 64);
72 |
73 | $this->barrier1 = $ringBuffer->newBarrier();
74 | $this->barrier2 = $ringBuffer->newBarrier();
75 | }
76 |
77 | public function testShouldGetBarrierByHandler()
78 | {
79 | $this->consumerRepository->addEventProcessor($this->eventProcessor1, $this->handler1, $this->barrier1);
80 | $this->assertSame($this->barrier1, $this->consumerRepository->getBarrierFor($this->handler1));
81 | }
82 |
83 | public function testShouldReturnNullForBarrierWhenHandlerIsNotRegistered()
84 | {
85 | $this->assertNull($this->consumerRepository->getBarrierFor($this->handler1));
86 | }
87 |
88 | public function testShouldGetLastEventProcessorsInChain()
89 | {
90 | $this->consumerRepository->addEventProcessor($this->eventProcessor1, $this->handler1, $this->barrier1);
91 | $this->consumerRepository->addEventProcessor($this->eventProcessor2, $this->handler2, $this->barrier2);
92 |
93 | $sequences = new SequenceList();
94 | $sequences[] = $this->eventProcessor2->getSequence();
95 | $this->consumerRepository->unMarkEventProcessorsAsEndOfChain($sequences);
96 |
97 | $lastEventProcessorsInChain = $this->consumerRepository->getLastSequenceInChain(true);
98 | $this->assertEquals(1, count($lastEventProcessorsInChain));
99 | $this->assertTrue($this->eventProcessor1->getSequence() === $lastEventProcessorsInChain[0]);
100 | }
101 |
102 | public function testShouldRetrieveEventProcessorForHandler()
103 | {
104 | $this->consumerRepository->addEventProcessor($this->eventProcessor1, $this->handler1, $this->barrier1);
105 | $this->assertTrue($this->consumerRepository->getEventProcessorFor($this->handler1) === $this->eventProcessor1);
106 | }
107 |
108 | /**
109 | * @expectedException PhpDisruptor\Exception\InvalidArgumentException
110 | * @expectedExceptionMessage The given event handler is not processing events
111 | */
112 | public function testShouldThrowExceptionWhenHandlerIsNotRegistered()
113 | {
114 | $eventFactory = new TestEventFactory();
115 | $eventHandler = new SleepingEventHandler($eventFactory->getEventClass());
116 | $this->consumerRepository->getEventProcessorFor($eventHandler);
117 | }
118 |
119 | public function testShouldIterateAllEventProcessors()
120 | {
121 | $this->consumerRepository->addEventProcessor($this->eventProcessor1, $this->handler1, $this->barrier1);
122 | $this->consumerRepository->addEventProcessor($this->eventProcessor2, $this->handler2, $this->barrier2);
123 |
124 | $seen1 = false;
125 | $seen2 = false;
126 |
127 | foreach($this->consumerRepository->getConsumerInfos() as $info) {
128 | if (!$seen1
129 | && $info->getEventProcessor() === $this->eventProcessor1
130 | && $info->getHandler() === $this->handler1
131 | ) {
132 | $seen1 = true;
133 | } else if (!$seen2
134 | && $info->getEventProcessor() === $this->eventProcessor2
135 | && $info->getHandler() === $this->handler2
136 | ) {
137 | $seen2 = true;
138 | } else {
139 | $this->fail('Unexpected event processor info');
140 | }
141 | }
142 |
143 | $this->assertTrue($seen1);
144 | $this->assertTrue($seen2);
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/Dsl/Disruptor/DisruptorTest.php:
--------------------------------------------------------------------------------
1 | eventClass = $eventFactory->getEventClass();
52 | $producerType = ProducerType::SINGLE();
53 | $this->disruptor = new Disruptor($eventFactory, 4, $producerType);
54 | $this->delayedEventHandlers = new EventHandlerList();
55 | $this->testWorkHandlers = new WorkHandlerList();
56 | }
57 |
58 | protected function tearDown()
59 | {
60 | foreach ($this->delayedEventHandlers as $delayedEventHandler) {
61 | $delayedEventHandler->stopWaiting();
62 | }
63 |
64 | foreach ($this->testWorkHandlers as $testWorkHandler) {
65 | $testWorkHandler->stopWaiting();
66 | }
67 |
68 | $this->disruptor->halt();
69 | }
70 |
71 | public function testShouldCreateEventProcessorGroupForFirstEventProcessors()
72 | {
73 | $eventHandler1 = new SleepingEventHandler($this->eventClass);
74 | $eventHandler2 = new SleepingEventHandler($this->eventClass);
75 |
76 | $eventHandlerList = new EventHandlerList(array($eventHandler1, $eventHandler2));
77 |
78 | $eventHandlerGroup = $this->disruptor->handleEventsWithEventHandlers($eventHandlerList);
79 | $this->disruptor->start();
80 |
81 | $this->assertNotNull($eventHandlerGroup);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/Dsl/Disruptor/TestAsset/DelayedEventHandler.php:
--------------------------------------------------------------------------------
1 | readyToProcessEvent = false;
33 | $this->stopped = false;
34 | if (null === $barrier) {
35 | $barrier = new CyclicBarrier(2);
36 | }
37 | $this->cyclicBarrier = $barrier;
38 | }
39 |
40 | /**
41 | * Return the used event class name
42 | *
43 | * @return string
44 | */
45 | public function getEventClass()
46 | {
47 | return 'PhpDisruptorTest\TestAsset\TestEvent';
48 | }
49 |
50 | /**
51 | * Called when a publisher has published an event to the RingBuffer
52 | *
53 | * @param object $event published to the RingBuffer
54 | * @param int $sequence of the event being processed
55 | * @param bool $endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
56 | * @return void
57 | * @throws \Exception if the EventHandler would like the exception handled further up the chain.
58 | */
59 | public function onEvent($event, $sequence, $endOfBatch)
60 | {
61 | $this->waitForAndSetFlag(false);
62 | }
63 |
64 | public function processEvent()
65 | {
66 | $this->waitForAndSetFlag(true);
67 | }
68 |
69 | public function stopWaiting()
70 | {
71 | $this->stopped = true;
72 | }
73 |
74 | public function waitForAndSetFlag($bool)
75 | {
76 | while(!$this->casMember('readyToProcessEvent', !$bool, $bool)) {
77 | $this->wait(1);
78 | }
79 | }
80 |
81 | public function onStart()
82 | {
83 | try
84 | {
85 | $this->cyclicBarrier->await();
86 | } catch (BrokenBarrierException $e) {
87 | throw new \RuntimeException($e);
88 | }
89 | }
90 |
91 | public function onShutdown()
92 | {
93 | }
94 |
95 | public function awaitStart()
96 | {
97 | $this->cyclicBarrier->await();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/Dsl/Disruptor/TestAsset/TestWorkHandler.php:
--------------------------------------------------------------------------------
1 | stopped = false;
21 | $this->readyToProcessEvent = false;
22 | }
23 |
24 | /**
25 | * Return the used event class name
26 | *
27 | * @return string
28 | */
29 | public function getEventClass()
30 | {
31 | return 'PhpDisruptorTest\TestAsset\TestEvent';
32 | }
33 |
34 | /**
35 | * @param object $event
36 | * @return void
37 | * @throws Exception
38 | */
39 | public function onEvent($event)
40 | {
41 | $this->waitForAndSetFlag(false);
42 | }
43 |
44 | public function processEvent($event)
45 | {
46 | $this->waitForAndSetFlag(true);
47 | }
48 |
49 | public function stopWaiting()
50 | {
51 | $this->stopped = true;
52 | }
53 |
54 | public function waitForAndSetFlag($bool)
55 | {
56 | while (!$this->casMember('readyToProcessEvent', !$bool, $bool)) {
57 | $this->wait(1);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/EventProcessor/BatchEventProcessor/BatchEventProcessorTest.php:
--------------------------------------------------------------------------------
1 | ringBuffer = RingBuffer::createMultiProducer($factory, 16);
36 | $this->sequenceBarrier = $this->ringBuffer->newBarrier();
37 | $this->latch = new CountDownLatch(1);
38 | }
39 |
40 | public function testShouldCallMethodsInLifecycleOrder()
41 | {
42 | $eventHandler = new EventHandler('PhpDisruptorTest\TestAsset\StubEvent', $this->latch);
43 | $batchEventProcessor = new BatchEventProcessor(
44 | 'PhpDisruptorTest\TestAsset\StubEvent',
45 | $this->ringBuffer,
46 | $this->sequenceBarrier,
47 | $eventHandler
48 | );
49 |
50 | $batchEventProcessor->start();
51 |
52 | $this->assertEquals(-1, $batchEventProcessor->getSequence()->get());
53 | $this->ringBuffer->publish($this->ringBuffer->next());
54 |
55 | $this->latch->await();
56 |
57 | $batchEventProcessor->halt();
58 | $batchEventProcessor->join();
59 |
60 | $expectedResult = new Threaded();
61 | $expectedResult[] = 'onStart';
62 | $expectedResult[] = 'PhpDisruptorTest\TestAsset\StubEvent-0-1';
63 | $expectedResult[] = 'onShutdown';
64 |
65 | $this->assertEquals($expectedResult, $eventHandler->getResult());
66 | }
67 |
68 | public function testShouldCallMethodsInLifecycleOrderForBatch()
69 | {
70 | $eventHandler = new EventHandler('PhpDisruptorTest\TestAsset\StubEvent', $this->latch);
71 | $batchEventProcessor = new BatchEventProcessor(
72 | 'PhpDisruptorTest\TestAsset\StubEvent',
73 | $this->ringBuffer,
74 | $this->sequenceBarrier,
75 | $eventHandler
76 | );
77 |
78 | $this->ringBuffer->publish($this->ringBuffer->next());
79 | $this->ringBuffer->publish($this->ringBuffer->next());
80 | $this->ringBuffer->publish($this->ringBuffer->next());
81 |
82 | $batchEventProcessor->start();
83 |
84 | $this->latch->await();
85 |
86 | $batchEventProcessor->halt();
87 | $batchEventProcessor->join();
88 |
89 | $expectedResult = new Threaded();
90 | $expectedResult[] = 'onStart';
91 | $expectedResult[] = 'PhpDisruptorTest\TestAsset\StubEvent-0-0';
92 | $expectedResult[] = 'PhpDisruptorTest\TestAsset\StubEvent-1-0';
93 | $expectedResult[] = 'PhpDisruptorTest\TestAsset\StubEvent-2-1';
94 | $expectedResult[] = 'onShutdown';
95 |
96 | $this->assertEquals($expectedResult, $eventHandler->getResult());
97 | }
98 |
99 | public function testShouldCallExceptionHandlerOnUncaughtException()
100 | {
101 | $exceptionHandler = new TestExceptionHandler();
102 | $eventHandler = new ExEventHandler('PhpDisruptorTest\TestAsset\StubEvent', $this->latch);
103 | $batchEventProcessor = new BatchEventProcessor(
104 | 'PhpDisruptorTest\TestAsset\StubEvent',
105 | $this->ringBuffer,
106 | $this->sequenceBarrier,
107 | $eventHandler
108 | );
109 | $batchEventProcessor->setExceptionHandler($exceptionHandler);
110 |
111 | $batchEventProcessor->start();
112 |
113 | $this->ringBuffer->publish($this->ringBuffer->next());
114 |
115 | $this->latch->await();
116 |
117 | $batchEventProcessor->halt();
118 | $batchEventProcessor->join();
119 |
120 | $expectedResult = new Threaded();
121 | $expectedResult[] = 'onStart';
122 | $expectedResult[] = 'onShutdown';
123 |
124 | $this->assertEquals($expectedResult, $eventHandler->getResult());
125 |
126 | $expectedException = new Threaded();
127 | $expectedException[] = 'PhpDisruptorTest\EventProcessor\BatchEventProcessor\TestAsset\TestExceptionHandler'
128 | . '::handleEventExceptionException-0-PhpDisruptorTest\TestAsset\StubEvent';
129 |
130 | $this->assertEquals($expectedException, $exceptionHandler->getResult());
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/EventProcessor/BatchEventProcessor/TestAsset/EventHandler.php:
--------------------------------------------------------------------------------
1 | eventClass = $eventClass;
37 | $this->result = new Threaded();
38 | $this->latch = $latch;
39 | }
40 |
41 | /**
42 | * Return the used event class name
43 | *
44 | * @return string
45 | */
46 | public function getEventClass()
47 | {
48 | return $this->eventClass;
49 | }
50 |
51 | /**
52 | * Called when a publisher has published an event to the RingBuffer
53 | *
54 | * @param object $event published to the RingBuffer
55 | * @param int $sequence of the event being processed
56 | * @param bool $endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
57 | * @return void
58 | * @throws \Exception if the EventHandler would like the exception handled further up the chain.
59 | */
60 | public function onEvent($event, $sequence, $endOfBatch)
61 | {
62 | $this->result[] = get_class($event) . '-' . $sequence . '-' . (string) (int) $endOfBatch;
63 | $this->latch->countDown();
64 | }
65 |
66 | /**
67 | * Called once on thread start before first event is available.
68 | *
69 | * @return void
70 | */
71 | public function onStart()
72 | {
73 | $this->result[] = 'onStart';
74 | }
75 |
76 | /**
77 | * Called once just before the thread is shutdown.
78 | *
79 | * Sequence event processing will already have stopped before this method is called. No events will
80 | * be processed after this message.
81 | *
82 | * @return void
83 | */
84 | public function onShutdown()
85 | {
86 | $this->result[] = 'onShutdown';
87 | }
88 |
89 | /**
90 | * @return array
91 | */
92 | public function getResult()
93 | {
94 | return $this->result;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/EventProcessor/BatchEventProcessor/TestAsset/ExEventHandler.php:
--------------------------------------------------------------------------------
1 | latch->countDown();
19 | throw new \Exception('Throws exception');
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/EventProcessor/BatchEventProcessor/TestAsset/TestExceptionHandler.php:
--------------------------------------------------------------------------------
1 | result = new Threaded();
22 | }
23 |
24 | /**
25 | * Strategy for handling uncaught exceptions when processing an event.
26 | *
27 | * If the strategy wishes to terminate further processing by the BatchEventProcessor
28 | * then it should throw a RuntimeException.
29 | *
30 | * @param Exception $ex the exception that propagated from the EventHandler.
31 | * @param int $sequence of the event which cause the exception.
32 | * @param object $event being processed when the exception occurred. This can be null.
33 | * @return void
34 | */
35 | public function handleEventException(Exception $ex, $sequence, $event)
36 | {
37 | $this->result[] = __METHOD__ . get_class($ex) . '-' . $sequence . '-' . get_class($event);
38 | }
39 |
40 | /**
41 | * Callback to notify of an exception during LifecycleAware#onStart()
42 | *
43 | * @param Exception $ex throw during the starting process.
44 | * @return void
45 | */
46 | public function handleOnStartException(Exception $ex)
47 | {
48 | $this->result[] = __METHOD__ . get_class($ex);
49 | }
50 |
51 | /**
52 | * Callback to notify of an exception during LifecycleAware#onShutdown()
53 | *
54 | * @param Exception $ex throw during the shutdown process.
55 | */
56 | public function handleOnShutdownException(Exception $ex)
57 | {
58 | $this->result[] = __METHOD__ . get_class($ex);
59 | }
60 |
61 | /**
62 | * @return Threaded
63 | */
64 | public function getResult()
65 | {
66 | return $this->result;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/EventPublisherTest.php:
--------------------------------------------------------------------------------
1 | eventFactory = new LongEventFactory;
31 | $this->ringBuffer = RingBuffer::createMultiProducer($this->eventFactory, self::RINGBUFFER_SIZE);
32 | }
33 |
34 | public function testShouldPublishEvent()
35 | {
36 | $eventProcessor = new NoOpEventProcessor($this->ringBuffer);
37 | $sequences = new SequenceList($eventProcessor->getSequence());
38 |
39 | $this->ringBuffer->addGatingSequences($sequences);
40 |
41 | $this->ringBuffer->publishEvent($this);
42 | $this->ringBuffer->publishEvent($this);
43 |
44 | $this->assertEquals($this->ringBuffer->get(0)->get(), 29);
45 | $this->assertEquals($this->ringBuffer->get(1)->get(), 30);
46 | }
47 |
48 | public function testShouldTryPublishEvent()
49 | {
50 | $sequence = new Sequence();
51 | $sequences = new SequenceList($sequence);
52 | $this->ringBuffer->addGatingSequences($sequences);
53 |
54 | for ($i = 0; $i < self::RINGBUFFER_SIZE; $i++) {
55 | $this->assertTrue($this->ringBuffer->tryPublishEvent($this));
56 | }
57 |
58 | for ($i = 0; $i < self::RINGBUFFER_SIZE; $i++) {
59 | $this->assertEquals($this->ringBuffer->get($i)->get(), $i + 29);
60 | }
61 |
62 | $this->assertFalse($this->ringBuffer->tryPublishEvent($this));
63 | }
64 |
65 | /**
66 | * Return the used event class name
67 | *
68 | * @return string
69 | */
70 | public function getEventClass()
71 | {
72 | return __NAMESPACE__ . '\TestAsset\LongEvent';
73 | }
74 |
75 | /**
76 | * Translate a data representation into fields set in given event
77 | *
78 | * @param object $event into which the data should be translated.
79 | * @param int $sequence that is assigned to event.
80 | * @param Threaded $args
81 | * @return void
82 | */
83 | public function translateTo($event, $sequence, Threaded $args = null)
84 | {
85 | $event->set($sequence + 29);
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/EventTranslatorTest.php:
--------------------------------------------------------------------------------
1 | newInstance();
16 |
17 | $eventTranslator = new ExampleEventTranslator(self::TEST_VALUE);
18 | $eventTranslator->translateTo($event, 0);
19 |
20 | $this->assertEquals(self::TEST_VALUE, $event->getTestString());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/ExceptionHandler/FatalExceptionHandlerTest.php:
--------------------------------------------------------------------------------
1 | handleEventException($ex, 0, $event);
23 | } catch (RuntimeException $e) {
24 | $this->assertEquals($ex, $e->getPrevious());
25 | }
26 |
27 | $res = file_get_contents(sys_get_temp_dir() . '/fatallog');
28 | $this->assertEquals('ERR: Exception processing: 0 Test Event', $res);
29 |
30 | if (file_exists(sys_get_temp_dir() . '/fatallog')) {
31 | unlink(sys_get_temp_dir() . '/fatallog');
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/ExceptionHandler/IgnoreExceptionHandlerTest.php:
--------------------------------------------------------------------------------
1 | handleEventException($ex, 0, $event);
21 |
22 | $res = file_get_contents(sys_get_temp_dir() . '/ignorelog');
23 | $this->assertEquals('INFO: Exception processing: 0 Test Event', $res);
24 |
25 | if (file_exists(sys_get_temp_dir() . '/ignorelog')) {
26 | unlink(sys_get_temp_dir() . '/ignorelog');
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/FixedSequenceGroupTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(34, $sequenceGroup->get());
24 | $sequence1->set(35);
25 | $this->assertEquals(35, $sequenceGroup->get());
26 | $sequence1->set(48);
27 | $this->assertEquals(47, $sequenceGroup->get());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/LifecycleAwareInterface/LifecylceAwareInterfaceTest.php:
--------------------------------------------------------------------------------
1 | newBarrier();
22 |
23 | $handler = new LifecycleAwareEventHandler($startLatch, $shutdownLatch);
24 |
25 | $batchEventProcessor = new BatchEventProcessor(
26 | $eventFactory->getEventClass(),
27 | $ringBuffer,
28 | $sequenceBarrier,
29 | $handler
30 | );
31 |
32 | $batchEventProcessor->start();
33 |
34 | $startLatch->await();
35 | $batchEventProcessor->halt();
36 |
37 | $shutdownLatch->await();
38 |
39 | $this->assertSame(1, $handler->startCounter);
40 | $this->assertSame(1, $handler->shutdownCounter);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/LifecycleAwareInterface/TestAsset/LifecycleAwareEventHandler.php:
--------------------------------------------------------------------------------
1 | startCounter = 0;
29 | $this->shutdownCounter = 0;
30 |
31 | $this->startLatch = $startLatch;
32 | $this->shutdownLatch = $shutdownLatch;
33 | }
34 |
35 | public function getEventClass()
36 | {
37 | return 'PhpDisruptorTest\TestAsset\StubEvent';
38 | }
39 |
40 | public function onEvent($event, $sequence, $endOfBatch)
41 | {
42 | }
43 |
44 | public function onStart()
45 | {
46 | ++$this->startCounter;
47 | $this->startLatch->countDown();
48 | }
49 |
50 | public function onShutdown()
51 | {
52 | ++$this->shutdownCounter;
53 | $this->shutdownLatch->countDown();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/MultiProducerSequencerTest.php:
--------------------------------------------------------------------------------
1 | publish(3);
15 | $publisher->publish(5);
16 |
17 | $this->assertFalse($publisher->isAvailable(0));
18 | $this->assertFalse($publisher->isAvailable(1));
19 | $this->assertFalse($publisher->isAvailable(2));
20 | $this->assertTrue($publisher->isAvailable(3));
21 | $this->assertFalse($publisher->isAvailable(4));
22 | $this->assertTrue($publisher->isAvailable(5));
23 | $this->assertFalse($publisher->isAvailable(6));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/SequenceBarrierTest.php:
--------------------------------------------------------------------------------
1 | eventFactory = new StubEventFactory();
50 | $this->eventProcessor1 = new StubEventProcessor();
51 | $this->eventProcessor2 = new StubEventProcessor();
52 | $this->eventProcessor3 = new StubEventProcessor();
53 | $this->ringBuffer = RingBuffer::createMultiProducer($this->eventFactory, 64);
54 | $eventProcessor = new NoOpEventProcessor($this->ringBuffer);
55 | $sequences = new SequenceList($eventProcessor->getSequence());
56 | $this->ringBuffer->addGatingSequences($sequences);
57 | }
58 |
59 | public function testShouldWaitForWorkCompleteWhereCompleteWorkThresholdIsAhead()
60 | {
61 | $expectedNumberMessages = 10;
62 | $expectedWorkSequence = 9;
63 | $this->fillRingBuffer($expectedNumberMessages);
64 |
65 | $this->eventProcessor1->setSequence($expectedNumberMessages);
66 | $this->eventProcessor2->setSequence($expectedWorkSequence);
67 | $this->eventProcessor3->setSequence($expectedNumberMessages);
68 |
69 | $seqs = array(
70 | $this->eventProcessor1->getSequence(),
71 | $this->eventProcessor2->getSequence(),
72 | $this->eventProcessor3->getSequence()
73 | );
74 | $sequences = new SequenceList($seqs);
75 |
76 | $sequenceBarrier = $this->ringBuffer->newBarrier($sequences);
77 |
78 | $completedWorkSequence = $sequenceBarrier->waitFor($expectedWorkSequence);
79 | $this->assertTrue($completedWorkSequence >= $expectedWorkSequence);
80 | }
81 |
82 | public function testShouldWaitForWorkCompleteWhereAllWorkersAreBlockedOnRingBuffer()
83 | {
84 | $expectedNumberMessages = 10;
85 | $this->fillRingBuffer($expectedNumberMessages);
86 |
87 | $workers = new EventProcessorList();
88 | for ($i = 0; $i < 3; $i++) {
89 | $worker = new StubEventProcessor();
90 | $worker->setSequence($expectedNumberMessages - 1);
91 | $workers->add($worker);
92 | }
93 |
94 | $sequenceBarrier = $this->ringBuffer->newBarrier(Util::getSequencesFor($workers));
95 | $thread = new RingBufferThread($this->ringBuffer, $workers);
96 | $thread->start();
97 |
98 |
99 | $expectedWorkSequence = $expectedNumberMessages;
100 | $completedWorkSequence = $sequenceBarrier->waitFor($expectedNumberMessages);
101 | $this->assertTrue($completedWorkSequence >= $expectedWorkSequence);
102 |
103 | $thread->join();
104 | }
105 |
106 | public function testShouldInterruptDuringBusySpin()
107 | {
108 | $expectedNumberMessages = 10;
109 | $this->fillRingBuffer($expectedNumberMessages);
110 |
111 | $countDownLatch = new CountDownLatch(3);
112 | $sequence1 = new CountDownLatchSequence(8, $countDownLatch);
113 | $sequence2 = new CountDownLatchSequence(8, $countDownLatch);
114 | $sequence3 = new CountDownLatchSequence(8, $countDownLatch);
115 |
116 | $this->eventProcessor1->setSequenceObject($sequence1);
117 | $this->eventProcessor2->setSequenceObject($sequence2);
118 | $this->eventProcessor3->setSequenceObject($sequence3);
119 |
120 | $processors = new EventProcessorList();
121 | $processors->add($this->eventProcessor1);
122 | $processors->add($this->eventProcessor2);
123 | $processors->add($this->eventProcessor3);
124 |
125 | $sequencesToTrack = Util::getSequencesFor($processors);
126 | $sequenceBarrier = $this->ringBuffer->newBarrier($sequencesToTrack);
127 |
128 | $alerted = new Threaded();
129 | $alerted[0] = false;
130 |
131 | $thread = new SequenceBarrierThread($sequenceBarrier, $expectedNumberMessages, $alerted);
132 | $thread->start();
133 | $countDownLatch->await(3000000); // 3sec
134 | $sequenceBarrier->alert();
135 | $thread->join();
136 |
137 | $this->assertTrue($alerted[0], 'Thread was not interrupted');
138 | }
139 |
140 | public function testShouldWaitForWorkCompleteWhereCompleteWorkThresholdIsBehind()
141 | {
142 | $expectedNumberMessages = 10;
143 | $this->fillRingBuffer($expectedNumberMessages);
144 |
145 | $eventProcessors = new EventProcessorList();
146 | for ($i = 0; $i < 3; $i++) {
147 | $eventProcessors[$i] = new StubEventProcessor();
148 | $eventProcessors[$i]->setSequence($expectedNumberMessages - 2);
149 | }
150 |
151 | $sequenceBarrier = $this->ringBuffer->newBarrier(Util::getSequencesFor($eventProcessors));
152 |
153 | $thread = new StubEventProcessorThread($eventProcessors);
154 | $thread->start();
155 | $thread->join();
156 |
157 | $expectedWorkSequence = $expectedNumberMessages - 1;
158 | $completedWorkSequence = $sequenceBarrier->waitFor($expectedWorkSequence);
159 | $this->assertTrue($completedWorkSequence >= $expectedWorkSequence);
160 | }
161 |
162 | public function testShouldSetAndClearAlertStatus()
163 | {
164 | $sequenceBarrier = $this->ringBuffer->newBarrier();
165 | $this->assertFalse($sequenceBarrier->isAlerted());
166 |
167 | $sequenceBarrier->alert();
168 | $this->assertTrue($sequenceBarrier->isAlerted());
169 |
170 | $sequenceBarrier->clearAlert();
171 | $this->assertFalse($sequenceBarrier->isAlerted());
172 | }
173 |
174 | protected function fillRingBuffer($expectedNumberMessages)
175 | {
176 | for ($i = 0; $i < $expectedNumberMessages; $i++) {
177 | $sequence = $this->ringBuffer->next();
178 | $event = $this->ringBuffer->get($sequence);
179 | $event->setValue($i);
180 | $this->ringBuffer->publish($sequence);
181 | }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/SequenceGroupTest.php:
--------------------------------------------------------------------------------
1 | get();
18 | $this->assertEquals(PHP_INT_MAX, $result);
19 | }
20 |
21 | public function testShouldAddOneSequenceToGroup()
22 | {
23 | $sequence = new Sequence(7);
24 | $sequenceGroup = new SequenceGroup();
25 | $sequenceGroup->add($sequence);
26 |
27 | $this->assertEquals($sequence->get(), $sequenceGroup->get());
28 | }
29 |
30 | public function testShouldNotFailIfTryingToRemoveNotExistingSequence()
31 | {
32 | $sequenceGroup = new SequenceGroup();
33 | $sequence1 = new Sequence();
34 | $sequence2 = new Sequence();
35 | $sequence3 = new Sequence();
36 | $sequenceGroup->add($sequence1);
37 | $sequenceGroup->add($sequence2);
38 | $sequenceGroup->remove($sequence3);
39 | }
40 |
41 | public function testShouldReportTheMinimumSequenceForGroupOfTwo()
42 | {
43 | $sequenceThree = new Sequence(3);
44 | $sequenceSeven = new Sequence(7);
45 | $sequenceGroup = new SequenceGroup();
46 |
47 | $sequenceGroup->add($sequenceSeven);
48 | $sequenceGroup->add($sequenceThree);
49 |
50 | $this->assertEquals($sequenceThree->get(), $sequenceGroup->get());
51 | }
52 |
53 | public function testShouldReportSizeOfGroup()
54 | {
55 | $sequenceGroup = new SequenceGroup();
56 | $sequence1 = new Sequence();
57 | $sequence2 = new Sequence();
58 | $sequence3 = new Sequence();
59 | $sequenceGroup->add($sequence1);
60 | $sequenceGroup->add($sequence2);
61 | $sequenceGroup->add($sequence3);
62 | $this->assertEquals(3, $sequenceGroup->count());
63 | }
64 |
65 | public function testShouldRemoveSequenceFromGroup()
66 | {
67 | $sequenceThree = new Sequence(3);
68 | $sequenceSeven = new Sequence(7);
69 | $sequenceGroup = new SequenceGroup();
70 |
71 | $sequenceGroup->add($sequenceThree);
72 | $sequenceGroup->add($sequenceSeven);
73 | $this->assertEquals($sequenceThree->get(), $sequenceGroup->get());
74 | $this->assertTrue($sequenceGroup->remove($sequenceThree));
75 | $this->assertEquals($sequenceSeven->get(), $sequenceGroup->get());
76 | $this->assertEquals(1, $sequenceGroup->count());
77 | }
78 |
79 | public function testShouldDoNothingWhenRemovingNotContainingSequence()
80 | {
81 | $sequenceThree = new Sequence(3);
82 | $sequenceGroup = new SequenceGroup();
83 |
84 | $this->assertFalse($sequenceGroup->remove($sequenceThree));
85 | }
86 |
87 | public function testShouldRemoveSequenceFromGroupWhereItBeenAddedMultipleTimes()
88 | {
89 | $sequenceThree = new Sequence(3);
90 | $sequenceSeven = new Sequence(7);
91 | $sequenceGroup = new SequenceGroup();
92 |
93 | $sequenceGroup->add($sequenceThree);
94 | $sequenceGroup->add($sequenceSeven);
95 | $sequenceGroup->add($sequenceThree);
96 |
97 | $this->assertEquals($sequenceThree->get(), $sequenceGroup->get());
98 |
99 | $this->assertTrue($sequenceGroup->remove($sequenceThree));
100 | $this->assertEquals($sequenceSeven->get(), $sequenceGroup->get());
101 | $this->assertEquals(1, $sequenceGroup->count());
102 | }
103 |
104 | public function testShouldSetGroupSequenceToSameValue()
105 | {
106 | $sequenceThree = new Sequence(3);
107 | $sequenceSeven = new Sequence(7);
108 | $sequenceGroup = new SequenceGroup();
109 |
110 | $sequenceGroup->add($sequenceSeven);
111 | $sequenceGroup->add($sequenceThree);
112 |
113 | $expectedSequence = 11;
114 | $sequenceGroup->set($expectedSequence);
115 |
116 | $this->assertEquals($expectedSequence, $sequenceThree->get());
117 | $this->assertEquals($expectedSequence, $sequenceSeven->get());
118 | }
119 |
120 | public function testObjectEqualityThroughThreads()
121 | {
122 | $sequence = new Sequence();
123 | $sequenceGroup = new SequenceGroup();
124 | $sequenceGroup->add($sequence);
125 | $sequences = $sequenceGroup->getSequences();
126 | $groupSequence = $sequences[0];
127 |
128 | $this->assertSame($sequence, $groupSequence);
129 | $this->assertNotSame($sequence, $sequenceGroup);
130 | $this->assertSame($sequence, $groupSequence);
131 | }
132 |
133 |
134 | public function testShouldAddWhileRunning()
135 | {
136 | $eventFactory = new TestEventFactory();
137 | $ringBuffer = RingBuffer::createSingleProducer($eventFactory, 32);
138 | $sequenceThree = new Sequence(3);
139 | $sequenceSeven = new Sequence(7);
140 | $sequenceGroup = new SequenceGroup();
141 | $sequenceGroup->add($sequenceSeven);
142 |
143 | for ($i = 0; $i < 11; $i++)
144 | {
145 | $ringBuffer->publish($ringBuffer->next());
146 | }
147 |
148 | $sequenceGroup->addWhileRunning($ringBuffer, $sequenceThree);
149 | $this->assertEquals(10, $sequenceThree->get());
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/SequenceReportingCallbackTest.php:
--------------------------------------------------------------------------------
1 | newBarrier();
22 | $handler = new TestSequenceReportingEventHandler($callbackLatch);
23 | $batchEventProcessor = new BatchEventProcessor($eventFactory->getEventClass(), $ringBuffer, $sequenceBarrier, $handler);
24 |
25 | $sequenceList = new SequenceList($batchEventProcessor->getSequence());
26 | $ringBuffer->addGatingSequences($sequenceList);
27 |
28 | $batchEventProcessor->start();
29 |
30 | $this->assertSame(-1, $batchEventProcessor->getSequence()->get());
31 | $ringBuffer->publish($ringBuffer->next());
32 |
33 | $callbackLatch->await();
34 | $this->assertSame(0, $batchEventProcessor->getSequence()->get());
35 |
36 | $onEndOfBatchLatch->countDown();
37 | $this->assertSame(0, $batchEventProcessor->getSequence()->get());
38 |
39 | $batchEventProcessor->halt();
40 | $batchEventProcessor->join();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/ArrayEventTranslator.php:
--------------------------------------------------------------------------------
1 | size = $size;
15 | }
16 |
17 | /**
18 | * Return the used event class name
19 | *
20 | * @return string
21 | */
22 | public function getEventClass()
23 | {
24 | return 'Threaded';
25 | }
26 |
27 | public function newInstance()
28 | {
29 | $array = new Threaded();
30 | for ($i = 0; $i < $this->size; $i++) {
31 | $array[] = null;
32 | }
33 | return $array;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/CountDownLatchSequence.php:
--------------------------------------------------------------------------------
1 | latch = $latch;
23 | }
24 |
25 | /**
26 | * @return int
27 | */
28 | public function get()
29 | {
30 | $this->latch->countDown();
31 | return parent::get();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/EventTranslator.php:
--------------------------------------------------------------------------------
1 | value = $value;
15 | }
16 |
17 | /**
18 | * Return the used event class name
19 | *
20 | * @return string
21 | */
22 | public function getEventClass()
23 | {
24 | return 'PhpDisruptorTest\TestAsset\StubEvent';
25 | }
26 |
27 | /**
28 | * Translate a data representation into fields set in given event
29 | *
30 | * @param object $event into which the data should be translated.
31 | * @param int $sequence that is assigned to event.
32 | * @param Threaded|null $args
33 | * @return void
34 | */
35 | public function translateTo($event, $sequence, Threaded $args = null)
36 | {
37 | $event->setTestString($this->value);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/LongEvent.php:
--------------------------------------------------------------------------------
1 | long = 0;
14 | }
15 |
16 | public function set($long)
17 | {
18 | $this->long = $long;
19 | }
20 |
21 | public function get()
22 | {
23 | return $this->long;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/LongEventFactory.php:
--------------------------------------------------------------------------------
1 | ringBuffer = $ringBuffer;
17 | $this->workers = $workers;
18 | }
19 |
20 | public function run()
21 | {
22 | $sequence = $this->ringBuffer->next();
23 | $event = $this->ringBuffer->get($sequence);
24 | $event->setValue($sequence);
25 | $this->ringBuffer->publish($sequence);
26 | foreach ($this->workers as $worker) {
27 | $worker->setSequence($sequence);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/RingBufferThread2.php:
--------------------------------------------------------------------------------
1 | ringBuffer = $ringBuffer;
21 | $this->latch = $latch;
22 | $this->publisherComplete = $publisherComplete;
23 | }
24 |
25 | public function run()
26 | {
27 | $ringBuffer = $this->ringBuffer;
28 | for ($i = 0; $i <= $ringBuffer->getBufferSize(); $i++) {
29 | $sequence = $ringBuffer->next();
30 | $event = $ringBuffer->get($sequence);
31 | /* @var $event StubEvent */
32 | $event->setValue($i);
33 | $ringBuffer->publish($sequence);
34 | $this->latch->countDown();
35 | }
36 | $this->publisherComplete[0] = true;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/SequenceBarrierThread.php:
--------------------------------------------------------------------------------
1 | barrier = $barrier;
20 | $this->expectedNumberOfMessages = $expectedNumberOfMessages;
21 | $this->alerted = $alerted;
22 | }
23 |
24 | public function run()
25 | {
26 | try {
27 | $this->barrier->waitFor($this->expectedNumberOfMessages - 1);
28 | } catch (AlertException $e) {
29 | $this->alerted[0] = true;
30 | } catch (\Exception $e) {
31 | // ignore
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/SleepingEventHandler.php:
--------------------------------------------------------------------------------
1 | eventClass = $eventClass;
24 | }
25 |
26 | /**
27 | * Return the used event class name
28 | *
29 | * @return string
30 | */
31 | public function getEventClass()
32 | {
33 | return $this->eventClass;
34 | }
35 |
36 | /**
37 | * Called when a publisher has published an event to the RingBuffer
38 | *
39 | * @param object $event published to the RingBuffer
40 | * @param int $sequence of the event being processed
41 | * @param bool $endOfBatch flag to indicate if this is the last event in a batch from the RingBuffer
42 | * @return void
43 | * @throws Exception\ExceptionInterface if the EventHandler would like the exception handled further up the chain.
44 | */
45 | public function onEvent($event, $sequence, $endOfBatch)
46 | {
47 | $this->wait(1);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/StubEvent.php:
--------------------------------------------------------------------------------
1 | value = $value;
27 | }
28 |
29 | public function copy(self $event)
30 | {
31 | $this->value = $event->value;
32 | }
33 |
34 | public function getValue()
35 | {
36 | return $this->value;
37 | }
38 |
39 | public function setValue($value)
40 | {
41 | $this->value = $value;
42 | }
43 |
44 | public function getTestString()
45 | {
46 | return $this->testString;
47 | }
48 |
49 | public function setTestString($testString)
50 | {
51 | $this->testString = $testString;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/StubEventFactory.php:
--------------------------------------------------------------------------------
1 | sequence = new Sequence();
18 | }
19 |
20 | public function setSequence($sequence)
21 | {
22 | $this->sequence->set($sequence);
23 | }
24 |
25 | public function setSequenceObject(Sequence $se)
26 | {
27 | $this->sequence = $se;
28 | }
29 |
30 | /**
31 | * Get a reference to the Sequence being used by this EventProcessor.
32 | *
33 | * @return Sequence reference to the Sequence for this EventProcessor
34 | */
35 | public function getSequence()
36 | {
37 | return $this->sequence;
38 | }
39 |
40 | /**
41 | * @return void
42 | */
43 | public function halt()
44 | {
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/StubEventProcessorThread.php:
--------------------------------------------------------------------------------
1 | eventProcessors = $eventProcessors;
14 | }
15 |
16 | public function run()
17 | {
18 | foreach ($this->eventProcessors as $stubWorker) {
19 | $stubWorker->setSequence($stubWorker->getSequence()->get() + 1);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/StubEventTranslator.php:
--------------------------------------------------------------------------------
1 | setValue($args[0]);
36 | $event->setTestString($args[1]);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/TestEvent.php:
--------------------------------------------------------------------------------
1 | sequence = $sequence;
15 | }
16 |
17 | /**
18 | * Get a reference to the Sequence being used by this EventProcessor.
19 | *
20 | * @return Sequence reference to the Sequence for this EventProcessor
21 | */
22 | public function getSequence()
23 | {
24 | return $this->sequence;
25 | }
26 |
27 | public function halt()
28 | {
29 | }
30 | }
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/TestEventProcessor2.php:
--------------------------------------------------------------------------------
1 | sequenceBarrier = $sequenceBarrier;
18 | $this->sequence = new Sequence();
19 | }
20 |
21 | /**
22 | * Get a reference to the Sequence being used by this EventProcessor.
23 | *
24 | * @return Sequence reference to the Sequence for this EventProcessor
25 | */
26 | public function getSequence()
27 | {
28 | return $this->sequence;
29 | }
30 |
31 | /**
32 | * @return void
33 | */
34 | public function halt()
35 | {
36 | }
37 |
38 | public function run()
39 | {
40 | try {
41 | $this->sequenceBarrier->waitFor(0);
42 | } catch (\Exception $e) {
43 | throw new \RuntimeException('', 0, $e);
44 | }
45 |
46 | $newSequence = $this->sequence->get() + 1;
47 | $this->sequence->set($newSequence);
48 | }
49 | }
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/TestAsset/TestSequenceReportingEventHandler.php:
--------------------------------------------------------------------------------
1 | latch = $latch;
24 | }
25 |
26 | public function getEventClass()
27 | {
28 | return __NAMESPACE__ . '\StubEvent';
29 | }
30 |
31 | public function setSequenceCallback(Sequence $sequenceTrackerCallback)
32 | {
33 | $this->sequenceCallback = $sequenceTrackerCallback;
34 | }
35 |
36 | public function onEvent($event, $sequence, $endOfBatch)
37 | {
38 | $this->sequenceCallback->set($sequence);
39 | $this->latch->countDown();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/Util/UtilTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(1024, $powerOfTwo);
17 | }
18 |
19 | public function testShouldReturnExactPowerOfTwo()
20 | {
21 | $powerOfTwo = Util::ceilingNextPowerOfTwo(1024);
22 | $this->assertEquals(1024, $powerOfTwo);
23 | }
24 |
25 | public function testLog2Of23()
26 | {
27 | $log2 = Util::log2(23);
28 | $this->assertEquals(4, $log2);
29 | }
30 |
31 | public function testLog2Of1000()
32 | {
33 | $log2 = Util::log2(1000);
34 | $this->assertEquals(9, $log2);
35 | }
36 |
37 | public function dataProvider()
38 | {
39 | $sequences1 = array(
40 | new Sequence(3),
41 | new Sequence(5),
42 | new Sequence(7)
43 | );
44 | $one = new SequenceList($sequences1);
45 |
46 | $sequences2 = array(
47 | new Sequence(7),
48 | new Sequence(5),
49 | new Sequence(3)
50 | );
51 | $two = new SequenceList($sequences2);
52 |
53 | $sequences3 = array(
54 | new Sequence(5),
55 | new Sequence(7),
56 | new Sequence(3)
57 | );
58 | $three = new SequenceList($sequences3);
59 |
60 | return array(
61 | array(
62 | $one
63 | ),
64 | array(
65 | $two
66 | ),
67 | array(
68 | $three
69 | )
70 | );
71 | }
72 |
73 | /**
74 | * @dataProvider dataProvider
75 | */
76 | public function testShouldReturnMinimumSequence($data)
77 | {
78 | $this->assertEquals(3, Util::getMinimumSequence($data));
79 | }
80 |
81 | public function testShouldReturnPhpIntMaxWhenNoEventProcessors()
82 | {
83 | $sequences = new SequenceList();
84 | $this->assertEquals(PHP_INT_MAX, Util::getMinimumSequence($sequences));
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/WaitStrategy/AbstractWaitStrategyTestCase.php:
--------------------------------------------------------------------------------
1 | start();
16 | $sequenceUpdater->waitForStartup();
17 | $cursor = new Sequence(0);
18 | $s = $sequenceUpdater->getSequence();
19 | $barrier = new DummySequenceBarrier();
20 | $sequence = $waitStrategy->waitFor(0, $cursor, $s, $barrier);
21 |
22 | $this->assertSame(0, $sequence);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/WaitStrategy/BusySpinWaitStrategyTest.php:
--------------------------------------------------------------------------------
1 | assertWaitForWithDelayOf(50000, $strategy);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/WaitStrategy/SleepingWaitStrategyTest.php:
--------------------------------------------------------------------------------
1 | assertWaitForWithDelayOf(50000, $strategy);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/WaitStrategy/TestAsset/DummySequenceBarrier.php:
--------------------------------------------------------------------------------
1 | sleepTimeMicros = $sleepTimeMicros;
22 | $this->waitStrategy = $waitStrategy;
23 | $this->barrier = new CyclicBarrier(2);
24 | $this->sequence = new Sequence();
25 | }
26 |
27 | public function run()
28 | {
29 | $this->barrier->await();
30 | if (0 != $this->sleepTimeMicros) {
31 | $this->wait($this->sleepTimeMicros);
32 | }
33 | $this->sequence->incrementAndGet();
34 | $this->waitStrategy->signalAllWhenBlocking();
35 | }
36 |
37 | public function waitForStartup()
38 | {
39 | $this->barrier->await();
40 | }
41 |
42 | public function getSequence()
43 | {
44 | return $this->sequence;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/PhpDisruptorTest/WaitStrategy/YieldingWaitStrategyTest.php:
--------------------------------------------------------------------------------
1 | assertWaitForWithDelayOf(50000, $strategy);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------