├── .gitignore ├── DispatchQueue.cxx ├── DispatchQueue.h ├── README.md └── disruptor └── include └── disruptor ├── claim_strategy.h ├── ring_buffer.h ├── sequence.h ├── sequence_barrier.h ├── sequencer.h ├── utils.h └── wait_strategy.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /DispatchQueue.cxx: -------------------------------------------------------------------------------- 1 | #include "DispatchQueue.h" 2 | #include 3 | #include 4 | #include 5 | 6 | namespace Dispatch { 7 | 8 | using kClaimStrategy = disruptor::MultiThreadedStrategy; 9 | using kWaitStrategy = disruptor::SleepingStrategy<>; 10 | using DisruptorSequencer = disruptor::Sequencer, disruptor::kDefaultRingBufferSize, kClaimStrategy, kWaitStrategy>; 11 | 12 | class MutexQueueImp : public DispatchQueue { 13 | public: 14 | MutexQueueImp(int thread_count) : DispatchQueue(thread_count), 15 | _cancel(false), 16 | _thread_count(thread_count) 17 | { 18 | for ( int i = 0; i < thread_count; i++ ) 19 | { 20 | create_thread(); 21 | } 22 | } 23 | 24 | ~MutexQueueImp() 25 | { 26 | _cancel = true; 27 | _condition.notify_all(); 28 | for ( auto& future : _futures ) 29 | { 30 | future.wait(); 31 | } 32 | } 33 | 34 | void sync_imp(std::shared_ptr task) override 35 | { 36 | if ( _thread_count == 1 && _thread_id == std::this_thread::get_id() ) 37 | { 38 | task->reset(); 39 | task->run(); 40 | task->signal(); 41 | } 42 | else 43 | { 44 | async_imp(task); 45 | task->wait(); 46 | } 47 | } 48 | 49 | int64_t async_imp(std::shared_ptr task) override 50 | { 51 | _mutex.lock(); 52 | task->reset(); 53 | _dispatch_tasks.push(task); 54 | _mutex.unlock(); 55 | _condition.notify_one(); 56 | return 0; 57 | } 58 | 59 | private: 60 | MutexQueueImp(const MutexQueueImp&); 61 | 62 | void create_thread() 63 | { 64 | _futures.emplace_back(std::async(std::launch::async, [&]() 65 | { 66 | _thread_id = std::this_thread::get_id(); 67 | while (!_cancel) 68 | { 69 | { 70 | std::unique_lock work_signal(_signal_mutex); 71 | _condition.wait(work_signal, [this](){ 72 | return _cancel || !_dispatch_tasks.empty(); 73 | }); 74 | } 75 | 76 | while (!_dispatch_tasks.empty() && !_cancel) 77 | { 78 | _mutex.lock(); 79 | if ( _dispatch_tasks.empty() ) 80 | { 81 | _mutex.unlock(); 82 | break; 83 | } 84 | std::shared_ptr task(_dispatch_tasks.front()); 85 | _dispatch_tasks.pop(); 86 | _mutex.unlock(); 87 | if ( nullptr != task ) 88 | { 89 | task->run(); 90 | task->signal(); 91 | } 92 | } 93 | } 94 | })); 95 | } 96 | 97 | private: 98 | std::vector> _futures; 99 | std::queue> _dispatch_tasks; 100 | std::recursive_mutex _mutex; 101 | bool _cancel; 102 | std::mutex _signal_mutex; 103 | std::condition_variable _condition; 104 | int _thread_count; 105 | std::thread::id _thread_id; 106 | }; 107 | 108 | class DisruptorImp : public DispatchQueue { 109 | 110 | public: 111 | DisruptorImp() : DispatchQueue(1), _cancel(false) 112 | { 113 | _sequencer = new DisruptorSequencer(_calls); 114 | create_thread(); 115 | } 116 | 117 | ~DisruptorImp() 118 | { 119 | _cancel = true; 120 | int64_t seq = _sequencer->Claim(); 121 | (*_sequencer)[seq] = nullptr; 122 | _sequencer->Publish(seq); 123 | _future.wait(); 124 | delete _sequencer; 125 | } 126 | 127 | private: 128 | DisruptorImp(const DisruptorImp&); 129 | 130 | void sync_imp(std::shared_ptr task) override 131 | { 132 | if ( _thread_id == std::this_thread::get_id() ) 133 | { 134 | task->reset(); 135 | task->run(); 136 | task->signal(); 137 | } 138 | else 139 | { 140 | async_imp(task); 141 | task->wait(); 142 | } 143 | } 144 | 145 | int64_t async_imp(std::shared_ptr task) override 146 | { 147 | task->reset(); 148 | int64_t seq = _sequencer->Claim(); 149 | (*_sequencer)[seq] = task; 150 | _sequencer->Publish(seq); 151 | return 0; 152 | } 153 | 154 | void create_thread() 155 | { 156 | _future = std::async(std::launch::async, [&]() 157 | { 158 | _thread_id = std::this_thread::get_id(); 159 | this->run(); 160 | }); 161 | } 162 | 163 | void run() 164 | { 165 | int64_t seqWant(disruptor::kFirstSequenceValue); 166 | int64_t seqGeted, i; 167 | std::vector dependents; 168 | disruptor::SequenceBarrier* barrier; 169 | 170 | disruptor::Sequence handled(disruptor::kInitialCursorValue); 171 | dependents.push_back(&handled); 172 | _sequencer->set_gating_sequences(dependents); 173 | dependents.clear(); 174 | barrier = _sequencer->NewBarrier(dependents); 175 | 176 | while (!_cancel) 177 | { 178 | seqGeted = barrier->WaitFor(seqWant); 179 | for (i = seqWant; i <= seqGeted; i++) 180 | { 181 | std::shared_ptr task((*_sequencer)[i]); 182 | (*_sequencer)[i] = nullptr; 183 | handled.set_sequence(i); 184 | if ( nullptr != task ) 185 | { 186 | task->run(); 187 | task->signal(); 188 | } 189 | } 190 | seqWant = seqGeted + 1; 191 | } 192 | 193 | delete barrier; 194 | } 195 | 196 | private: 197 | DisruptorSequencer *_sequencer; 198 | std::array, disruptor::kDefaultRingBufferSize> _calls; 199 | bool _cancel; 200 | std::future _future; 201 | std::thread::id _thread_id; 202 | }; 203 | 204 | std::shared_ptr create(int thread_count) 205 | { 206 | if ( 1 == thread_count ) 207 | { 208 | return std::shared_ptr(new DisruptorImp()); 209 | } 210 | else 211 | { 212 | return std::shared_ptr(new MutexQueueImp(thread_count)); 213 | } 214 | } 215 | 216 | } // namespace dispatch 217 | -------------------------------------------------------------------------------- /DispatchQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef _DISPATCHQUEUE_H_ 2 | #define _DISPATCHQUEUE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace Dispatch { 11 | 12 | class DispatchQueue; 13 | 14 | class QueueTask { 15 | public: 16 | QueueTask() : _signal(false) {} 17 | 18 | virtual ~QueueTask() {} 19 | 20 | virtual void run() = 0; 21 | 22 | virtual void signal() { 23 | _signal = true; 24 | _condition.notify_all(); 25 | } 26 | 27 | virtual void wait() { 28 | std::unique_lock lock(_mutex); 29 | _condition.wait(lock, [this](){ return _signal; }); 30 | _signal = false; 31 | } 32 | 33 | virtual void reset() { 34 | _signal = false; 35 | } 36 | 37 | private: 38 | bool _signal; 39 | std::mutex _mutex; 40 | std::condition_variable _condition; 41 | }; 42 | 43 | template 44 | class ClosureTask : public QueueTask { 45 | public: 46 | explicit ClosureTask(const T& closure) : _closure(closure) {} 47 | private: 48 | void run() override { 49 | _closure(); 50 | } 51 | T _closure; 52 | }; 53 | 54 | class DispatchQueue { 55 | public: 56 | DispatchQueue(int thread_count) {} 57 | 58 | virtual ~DispatchQueue() {} 59 | 60 | template ::value>::type* = nullptr> 61 | void sync(const T &task) { 62 | sync(std::shared_ptr(new ClosureTask(task))); 63 | } 64 | 65 | void sync(std::shared_ptr task) { 66 | if( nullptr != task ) { 67 | sync_imp(task); 68 | } 69 | } 70 | 71 | template ::value>::type* = nullptr> 72 | int64_t async(const T &task) { 73 | return async(std::shared_ptr(new ClosureTask(task))); 74 | } 75 | 76 | int64_t async(std::shared_ptr task) { 77 | if ( nullptr != task ) { 78 | return async_imp(task); 79 | } 80 | return -1; 81 | } 82 | 83 | protected: 84 | virtual void sync_imp(std::shared_ptr task) = 0; 85 | 86 | virtual int64_t async_imp(std::shared_ptr task) = 0; 87 | }; 88 | 89 | std::shared_ptr create(int thread_count = 1); 90 | 91 | } 92 | 93 | #endif /* _DISPATCHQUEUE_H_ */ 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DispatchQueue 2 | 3 | 使用 C++ 实现的 dispatch_queue 具体可阅读 [C++ 实现的 Dispatch Queue](http://www.enkichen.com/2018/03/10/dispatch-queue/) -------------------------------------------------------------------------------- /disruptor/include/disruptor/claim_strategy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-2015, Francois Saint-Jacques 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // * Neither the name of the disruptor-- nor the 12 | // names of its contributors may be used to endorse or promote products 13 | // derived from this software without specific prior written permission. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | // ARE DISCLAIMED. IN NO EVENT SHALL FRANCOIS SAINT-JACQUES BE LIABLE FOR ANY 19 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | #ifndef DISRUPTOR_CLAIM_STRATEGY_H_ // NOLINT 27 | #define DISRUPTOR_CLAIM_STRATEGY_H_ // NOLINT 28 | 29 | #include 30 | 31 | #include "disruptor/sequence.h" 32 | #include "disruptor/ring_buffer.h" 33 | 34 | namespace disruptor { 35 | 36 | /* 37 | // Strategy employed by a {@link Publisher} to wait claim and publish sequences 38 | // on the sequencer. 39 | // 40 | class ClaimStrategy { 41 | public: 42 | // Wait for the given sequence to be available for consumption. 43 | // 44 | // @param dependents dependents sequences to wait on (mostly consumers). 45 | // @param delta sequences to claim [default: 1]. 46 | // 47 | // @return last claimed sequence. 48 | int64_t IncrementAndGet(const std::vector& dependents, 49 | size_t delta = 1); 50 | 51 | // Verify in a non-blocking way that there exists claimable sequences. 52 | // 53 | // @param dependents dependents sequences to wait on (mostly consumers). 54 | // 55 | // @return last claimed sequence. 56 | bool HasAvailableCapacity(const std::vector& dependents); 57 | 58 | void SynchronizePublishing(const int64_t& sequence, const Sequence& cursor, 59 | const size_t& delta) {} 60 | }; 61 | */ 62 | 63 | template 64 | class SingleThreadedStrategy; 65 | using kDefaultClaimStrategy = SingleThreadedStrategy; 66 | 67 | // Optimised strategy can be used when there is a single publisher thread. 68 | template 69 | class SingleThreadedStrategy { 70 | public: 71 | SingleThreadedStrategy() 72 | : last_claimed_sequence_(kInitialCursorValue), 73 | last_consumer_sequence_(kInitialCursorValue) {} 74 | 75 | int64_t IncrementAndGet(const std::vector& dependents, 76 | size_t delta = 1) { 77 | const int64_t next_sequence = (last_claimed_sequence_ += delta); 78 | const int64_t wrap_point = next_sequence - N; 79 | if (last_consumer_sequence_ < wrap_point) { 80 | while (GetMinimumSequence(dependents) < wrap_point) { 81 | // TODO: configurable yield strategy 82 | std::this_thread::yield(); 83 | } 84 | } 85 | return next_sequence; 86 | } 87 | 88 | bool HasAvailableCapacity(const std::vector& dependents) { 89 | const int64_t wrap_point = last_claimed_sequence_ + 1L - N; 90 | if (wrap_point > last_consumer_sequence_) { 91 | const int64_t min_sequence = GetMinimumSequence(dependents); 92 | last_consumer_sequence_ = min_sequence; 93 | if (wrap_point > min_sequence) return false; 94 | } 95 | return true; 96 | } 97 | 98 | void SynchronizePublishing(const int64_t& sequence, const Sequence& cursor, 99 | const size_t& delta) {} 100 | 101 | private: 102 | // We do not need to use atomic values since this function is called by a 103 | // single publisher. 104 | int64_t last_claimed_sequence_; 105 | int64_t last_consumer_sequence_; 106 | 107 | DISALLOW_COPY_MOVE_AND_ASSIGN(SingleThreadedStrategy); 108 | }; 109 | 110 | // Optimised strategy can be used when there is a single publisher thread. 111 | template 112 | class MultiThreadedStrategy { 113 | public: 114 | MultiThreadedStrategy() {} 115 | 116 | int64_t IncrementAndGet(const std::vector& dependents, 117 | size_t delta = 1) { 118 | const int64_t next_sequence = last_claimed_sequence_.IncrementAndGet(delta); 119 | const int64_t wrap_point = next_sequence - N; 120 | if (last_consumer_sequence_.sequence() < wrap_point) { 121 | while (GetMinimumSequence(dependents) < wrap_point) { 122 | // TODO: configurable yield strategy 123 | std::this_thread::yield(); 124 | } 125 | } 126 | return next_sequence; 127 | } 128 | 129 | bool HasAvailableCapacity(const std::vector& dependents) { 130 | const int64_t wrap_point = last_claimed_sequence_.sequence() + 1L - N; 131 | if (wrap_point > last_consumer_sequence_.sequence()) { 132 | const int64_t min_sequence = GetMinimumSequence(dependents); 133 | last_consumer_sequence_.set_sequence(min_sequence); 134 | if (wrap_point > min_sequence) return false; 135 | } 136 | return true; 137 | } 138 | 139 | void SynchronizePublishing(const int64_t& sequence, const Sequence& cursor, 140 | const size_t& delta) { 141 | int64_t my_first_sequence = sequence - delta; 142 | 143 | while (cursor.sequence() < my_first_sequence) { 144 | // TODO: configurable yield strategy 145 | std::this_thread::yield(); 146 | } 147 | } 148 | 149 | private: 150 | Sequence last_claimed_sequence_; 151 | Sequence last_consumer_sequence_; 152 | 153 | DISALLOW_COPY_MOVE_AND_ASSIGN(MultiThreadedStrategy); 154 | }; 155 | 156 | }; // namespace disruptor 157 | 158 | #endif // DISRUPTOR_CLAIM_STRATEGY_H_ NOLINT 159 | -------------------------------------------------------------------------------- /disruptor/include/disruptor/ring_buffer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-2015, Francois Saint-Jacques 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // * Neither the name of the disruptor-- nor the 12 | // names of its contributors may be used to endorse or promote products 13 | // derived from this software without specific prior written permission. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | // ARE DISCLAIMED. IN NO EVENT SHALL FRANCOIS SAINT-JACQUES BE LIABLE FOR ANY 19 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | #ifndef DISRUPTOR_RING_BUFFER_H_ // NOLINT 27 | #define DISRUPTOR_RING_BUFFER_H_ // NOLINT 28 | 29 | #include 30 | #include "utils.h" 31 | 32 | namespace disruptor { 33 | 34 | constexpr size_t kDefaultRingBufferSize = 1024; 35 | 36 | // Ring buffer implemented with a fixed array. 37 | // 38 | // @param event type 39 | // @param size of the ring 40 | template 41 | class RingBuffer { 42 | public: 43 | // Construct a RingBuffer with the full option set. 44 | // 45 | // @param event_factory to instance new entries for filling the RingBuffer. 46 | // @param buffer_size of the RingBuffer, must be a power of 2. 47 | // @param claim_strategy_option threading strategy for publishers claiming 48 | // entries in the ring. 49 | // @param wait_strategy_option waiting strategy employed by 50 | // processors_to_track waiting in entries becoming available. 51 | RingBuffer(const std::array& events) : events_(events) {} 52 | 53 | static_assert(((N > 0) && ((N & (~N + 1)) == N)), 54 | "RingBuffer's size must be a positive power of 2"); 55 | 56 | // Get the event for a given sequence in the RingBuffer. 57 | // 58 | // @param sequence for the event 59 | // @return event reference at the specified sequence position. 60 | T& operator[](const int64_t& sequence) { return events_[sequence & (N - 1)]; } 61 | 62 | const T& operator[](const int64_t& sequence) const { 63 | return events_[sequence & (N - 1)]; 64 | } 65 | 66 | private: 67 | std::array events_; 68 | 69 | DISALLOW_COPY_MOVE_AND_ASSIGN(RingBuffer); 70 | }; 71 | 72 | }; // namespace disruptor 73 | 74 | #endif // DISRUPTOR_RING_BUFFER_H_ NOLINT 75 | -------------------------------------------------------------------------------- /disruptor/include/disruptor/sequence.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-2015, Francois Saint-Jacques 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // * Neither the name of the disruptor-- nor the 12 | // names of its contributors may be used to endorse or promote products 13 | // derived from this software without specific prior written permission. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | // ARE DISCLAIMED. IN NO EVENT SHALL FRANCOIS SAINT-JACQUES BE LIABLE FOR ANY 19 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | #ifndef CACHE_LINE_SIZE_IN_BYTES // NOLINT 27 | #define CACHE_LINE_SIZE_IN_BYTES 64 // NOLINT 28 | #endif // NOLINT 29 | #define ATOMIC_SEQUENCE_PADDING_LENGTH \ 30 | (CACHE_LINE_SIZE_IN_BYTES - sizeof(std::atomic)) / 8 31 | #define SEQUENCE_PADDING_LENGTH (CACHE_LINE_SIZE_IN_BYTES - sizeof(int64_t)) / 8 32 | 33 | #ifndef DISRUPTOR_SEQUENCE_H_ // NOLINT 34 | #define DISRUPTOR_SEQUENCE_H_ // NOLINT 35 | 36 | #include 37 | 38 | #include "disruptor/utils.h" 39 | 40 | namespace disruptor { 41 | 42 | // special cursor values 43 | constexpr int64_t kInitialCursorValue = -1L; 44 | constexpr int64_t kAlertedSignal = -2L; 45 | constexpr int64_t kTimeoutSignal = -3L; 46 | constexpr int64_t kFirstSequenceValue = kInitialCursorValue + 1L; 47 | 48 | // Sequence counter. 49 | class Sequence { 50 | public: 51 | // Construct a sequence counter that can be tracked across threads. 52 | // 53 | // @param initial_value for the counter. 54 | Sequence(int64_t initial_value = kInitialCursorValue) 55 | : sequence_(initial_value) {} 56 | 57 | // Get the current value of the {@link Sequence}. 58 | // 59 | // @return the current value. 60 | int64_t sequence() const { 61 | return sequence_.load(std::memory_order::memory_order_acquire); 62 | } 63 | 64 | // Set the current value of the {@link Sequence}. 65 | // 66 | // @param the value to which the {@link Sequence} will be set. 67 | void set_sequence(int64_t value) { 68 | sequence_.store(value, std::memory_order::memory_order_release); 69 | } 70 | 71 | // Increment and return the value of the {@link Sequence}. 72 | // 73 | // @param increment the {@link Sequence}. 74 | // @return the new value incremented. 75 | int64_t IncrementAndGet(const int64_t& increment) { 76 | return sequence_.fetch_add(increment, 77 | std::memory_order::memory_order_release) + 78 | increment; 79 | } 80 | 81 | private: 82 | // padding 83 | int64_t padding0_[ATOMIC_SEQUENCE_PADDING_LENGTH]; 84 | // members 85 | std::atomic sequence_; 86 | // padding 87 | int64_t padding1_[ATOMIC_SEQUENCE_PADDING_LENGTH]; 88 | 89 | DISALLOW_COPY_MOVE_AND_ASSIGN(Sequence); 90 | }; 91 | 92 | static int64_t GetMinimumSequence(const std::vector& sequences) { 93 | int64_t minimum = INT64_MAX; 94 | 95 | for (Sequence* sequence_ : sequences) { 96 | const int64_t sequence = sequence_->sequence(); 97 | minimum = minimum < sequence ? minimum : sequence; 98 | } 99 | 100 | return minimum; 101 | }; 102 | 103 | }; // namespace disruptor 104 | 105 | #endif // DISRUPTOR_SEQUENCE_H_ NOLINT 106 | -------------------------------------------------------------------------------- /disruptor/include/disruptor/sequence_barrier.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-2015, Francois Saint-Jacques 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // * Neither the name of the disruptor-- nor the 12 | // names of its contributors may be used to endorse or promote products 13 | // derived from this software without specific prior written permission. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | // ARE DISCLAIMED. IN NO EVENT SHALL FRANCOIS SAINT-JACQUES BE LIABLE FOR ANY 19 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | #ifndef DISRUPTOR_SEQUENCE_BARRIER_H_ // NOLINT 27 | #define DISRUPTOR_SEQUENCE_BARRIER_H_ // NOLINT 28 | 29 | #include 30 | #include 31 | 32 | #include "disruptor/wait_strategy.h" 33 | #include "disruptor/sequence.h" 34 | 35 | namespace disruptor { 36 | 37 | template 38 | class SequenceBarrier { 39 | public: 40 | SequenceBarrier(const Sequence& cursor, 41 | const std::vector& dependents) 42 | : cursor_(cursor), dependents_(dependents), alerted_(false) {} 43 | 44 | int64_t WaitFor(const int64_t& sequence) { 45 | return wait_strategy_.WaitFor(sequence, cursor_, dependents_, alerted_); 46 | } 47 | 48 | template 49 | int64_t WaitFor(const int64_t& sequence, 50 | const std::chrono::duration& timeout) { 51 | return wait_strategy_.WaitFor(sequence, cursor_, dependents_, alerted_, 52 | timeout); 53 | } 54 | 55 | int64_t get_sequence() const { return cursor_.sequence(); } 56 | 57 | bool alerted() const { 58 | return alerted_.load(std::memory_order::memory_order_acquire); 59 | } 60 | 61 | void set_alerted(bool alert) { 62 | alerted_.store(alert, std::memory_order::memory_order_release); 63 | } 64 | 65 | private: 66 | W wait_strategy_; 67 | const Sequence& cursor_; 68 | std::vector dependents_; 69 | std::atomic alerted_; 70 | }; 71 | 72 | }; // namespace disruptor 73 | 74 | #endif // DISRUPTOR_DEPENDENCY_BARRIER_H_ NOLINT 75 | -------------------------------------------------------------------------------- /disruptor/include/disruptor/sequencer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-2015, Francois Saint-Jacques 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // * Neither the name of the disruptor-- nor the 12 | // names of its contributors may be used to endorse or promote products 13 | // derived from this software without specific prior written permission. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | // ARE DISCLAIMED. IN NO EVENT SHALL FRANCOIS SAINT-JACQUES BE LIABLE FOR ANY 19 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | #ifndef DISRUPTOR_SEQUENCER_H_ // NOLINT 27 | #define DISRUPTOR_SEQUENCER_H_ // NOLINT 28 | 29 | #include "disruptor/claim_strategy.h" 30 | #include "disruptor/wait_strategy.h" 31 | #include "disruptor/sequence_barrier.h" 32 | 33 | namespace disruptor { 34 | 35 | // Coordinator for claiming sequences for access to a data structures while 36 | // tracking dependent {@link Sequence}s 37 | template 39 | class Sequencer { 40 | public: 41 | // Construct a Sequencer with the selected strategies. 42 | Sequencer(std::array events) : ring_buffer_(events) {} 43 | 44 | // Set the sequences that will gate publishers to prevent the buffer 45 | // wrapping. 46 | // 47 | // @param sequences to be gated on. 48 | void set_gating_sequences(const std::vector& sequences) { 49 | gating_sequences_ = sequences; 50 | } 51 | 52 | // Create a {@link SequenceBarrier} that gates on the cursor and a list of 53 | // {@link Sequence}s. 54 | // 55 | // @param sequences_to_track this barrier will track. 56 | // @return the barrier gated as required. 57 | SequenceBarrier* NewBarrier(const std::vector& dependents) { 58 | return new SequenceBarrier(cursor_, dependents); 59 | } 60 | 61 | // Get the value of the cursor indicating the published sequence. 62 | // 63 | // @return value of the cursor for events that have been published. 64 | int64_t GetCursor() { return cursor_.sequence(); } 65 | 66 | // Has the buffer capacity left to allocate another sequence. This is a 67 | // concurrent method so the response should only be taken as an indication 68 | // of available capacity. 69 | // 70 | // @return true if the buffer has the capacity to allocated another event. 71 | bool HasAvailableCapacity() { 72 | return claim_strategy_.HasAvailableCapacity(gating_sequences_); 73 | } 74 | 75 | // Claim the next batch of sequence numbers for publishing. 76 | // 77 | // @param delta the requested number of sequences. 78 | // @return the maximal claimed sequence 79 | int64_t Claim(size_t delta = 1) { 80 | return claim_strategy_.IncrementAndGet(gating_sequences_, delta); 81 | } 82 | 83 | // Publish an event and make it visible to {@link EventProcessor}s. 84 | // 85 | // @param sequence to be published. 86 | void Publish(const int64_t& sequence, size_t delta = 1) { 87 | claim_strategy_.SynchronizePublishing(sequence, cursor_, delta); 88 | const int64_t new_cursor = cursor_.IncrementAndGet(delta); 89 | wait_strategy_.SignalAllWhenBlocking(); 90 | } 91 | 92 | T& operator[](const int64_t& sequence) { return ring_buffer_[sequence]; } 93 | 94 | private: 95 | // Members 96 | RingBuffer ring_buffer_; 97 | 98 | Sequence cursor_; 99 | 100 | C claim_strategy_; 101 | 102 | W wait_strategy_; 103 | 104 | std::vector gating_sequences_; 105 | 106 | DISALLOW_COPY_MOVE_AND_ASSIGN(Sequencer); 107 | }; 108 | 109 | }; // namespace disruptor 110 | 111 | #endif // DISRUPTOR_RING_BUFFER_H_ NOLINT 112 | -------------------------------------------------------------------------------- /disruptor/include/disruptor/utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-2015, Francois Saint-Jacques 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // * Neither the name of the disruptor-- nor the 12 | // names of its contributors may be used to endorse or promote products 13 | // derived from this software without specific prior written permission. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | // ARE DISCLAIMED. IN NO EVENT SHALL FRANCOIS SAINT-JACQUES BE LIABLE FOR ANY 19 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | #ifndef DISRUPTOR_UTILS_H_ // NOLINT 27 | #define DISRUPTOR_UTILS_H_ // NOLINT 28 | 29 | // From Google C++ Standard, modified to use C++11 deleted functions. 30 | // A macro to disallow the copy constructor and operator= functions. 31 | #define DISALLOW_COPY_MOVE_AND_ASSIGN(TypeName) \ 32 | TypeName(const TypeName&) = delete; \ 33 | TypeName(const TypeName&&) = delete; \ 34 | void operator=(const TypeName&) = delete 35 | 36 | #endif // DISRUPTOR_UTILS_H_ NOLINT 37 | -------------------------------------------------------------------------------- /disruptor/include/disruptor/wait_strategy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011-2015, Francois Saint-Jacques 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above copyright 9 | // notice, this list of conditions and the following disclaimer in the 10 | // documentation and/or other materials provided with the distribution. 11 | // * Neither the name of the disruptor-- nor the 12 | // names of its contributors may be used to endorse or promote products 13 | // derived from this software without specific prior written permission. 14 | // 15 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | // ARE DISCLAIMED. IN NO EVENT SHALL FRANCOIS SAINT-JACQUES BE LIABLE FOR ANY 19 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | #ifndef DISRUPTOR_WAITSTRATEGY_H_ // NOLINT 27 | #define DISRUPTOR_WAITSTRATEGY_H_ // NOLINT 28 | 29 | #ifndef WIN32 30 | #include 31 | #endif 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "disruptor/sequence.h" 40 | 41 | namespace disruptor { 42 | 43 | /* 44 | // Strategy employed for a {@link Consumer} to wait on the sequencer's 45 | // cursor and a set of consumers' {@link Sequence}s. 46 | // 47 | class WaitStrategy { 48 | public: 49 | // Wait for the given sequence to be available for consumption. 50 | // 51 | // @param sequence to wait for. 52 | // @param cursor sequencer's cursor. 53 | // @param dependents 54 | // @param alerted indicator of consumer alert. 55 | // 56 | // @return kAltertedSignal if the barrier signaled an alert, otherwise 57 | // return the greatest available sequence which may be greater 58 | // than requested. 59 | int64_t WaitFor(const int64_t& sequence, 60 | const Sequence& cursor, 61 | const std::vector& dependents, 62 | const std::atomic& alerted); 63 | 64 | // Wait for the given sequence to be available for consumption with a 65 | // specified timeout. 66 | // 67 | // @param sequence to wait for. 68 | // @param cursor sequencer maximal sequence. 69 | // @param dependents further back the chain that must advance first. 70 | // @param alerted indicator of consumer state. 71 | // @param timeout indicator of consumer state. 72 | // 73 | // @return kAltertedSignal if the barrier signaled an alert, 74 | // kTimeoutSignal if the the requested timeout was reached while 75 | // waiting, otherwise return the greatest available sequence which 76 | // may be greater than requested. 77 | // 78 | int64_t WaitFor(const int64_t& sequence, 79 | const Sequence& cursor, 80 | const std::vector& dependents, 81 | const std::atomic& consumer_is_running, 82 | const std::chrono::duration& timeout); 83 | 84 | // Signal the strategy that the cursor as advanced. Some strategy depends 85 | // on this behaviour to unblock. 86 | void SignalAllWhenBlocking(); 87 | }; 88 | */ 89 | 90 | // Busy Spin strategy that uses a busy spin loop waiting on a barrier. 91 | // This strategy will use CPU resource to avoid syscalls which can introduce 92 | // latency jitter. It is strongly recommended to pin threads on isolated 93 | // CPU cores to minimize context switching and latency. 94 | class BusySpinStrategy; 95 | 96 | // Yielding strategy that uses a thread::yield() for waiting on a barrier. 97 | // This strategy is a good compromise between performance and CPU resource. 98 | template 99 | class YieldingStrategy; 100 | 101 | // Sleeping strategy uses a progressive back off strategy by first spinning for 102 | // S/2 loops, then yielding for S/2 loops, and finally sleeping for 103 | // duration until ready to advance. This is a good strategy for burst 104 | // traffic then quiet periods when latency is not critical. 105 | template 106 | class SleepingStrategy; 107 | 108 | // Blocking strategy that waits for the sequencer's cursor to advance on the 109 | // requested sequence. 110 | // 111 | // The sequencer MUST call SignalAllWhenBlocking() to unblock the strategy 112 | // when waiting on the cursor. Once the cursor is advanced, the strategy will 113 | // busy spin on the dependents' sequences and can be cancelled by affecting the 114 | // `alerted` atomic. 115 | // 116 | // The user can optionnaly provide a maximum timeout to the blocking operation, 117 | // see std::condition_any::wait_for() documentation for limitations. 118 | // 119 | // This strategy uses a condition variable inside a lock to block the 120 | // event procesor which saves CPU resource at the expense of lock 121 | // contention. Publishers must explicitely call SignalAllWhenBlocking() 122 | // to unblock consumers. This strategy should be used when performance and 123 | // low-latency are not as important as CPU resource. 124 | class BlockingStrategy; 125 | 126 | // defaults 127 | using kDefaultWaitStrategy = BusySpinStrategy; 128 | constexpr int64_t kDefaultRetryLoops = 200L; 129 | using kDefaultDuration = std::chrono::milliseconds; 130 | constexpr int kDefaultDurationValue = 1; 131 | 132 | // used internally 133 | static inline std::function buildMinSequenceFunction( 134 | const Sequence& cursor, const std::vector& dependents); 135 | 136 | class BusySpinStrategy { 137 | public: 138 | BusySpinStrategy() {} 139 | 140 | int64_t WaitFor(const int64_t& sequence, const Sequence& cursor, 141 | const std::vector& dependents, 142 | const std::atomic& alerted) { 143 | int64_t available_sequence = kInitialCursorValue; 144 | const auto min_sequence = buildMinSequenceFunction(cursor, dependents); 145 | 146 | while ((available_sequence = min_sequence()) < sequence) { 147 | if (alerted.load()) return kAlertedSignal; 148 | } 149 | 150 | return available_sequence; 151 | } 152 | 153 | template 154 | int64_t WaitFor(const int64_t& sequence, const Sequence& cursor, 155 | const std::vector& dependents, 156 | const std::atomic& alerted, 157 | const std::chrono::duration& timeout) { 158 | int64_t available_sequence = kInitialCursorValue; 159 | 160 | const auto start = std::chrono::system_clock::now(); 161 | const auto stop = start + timeout; 162 | const auto min_sequence = buildMinSequenceFunction(cursor, dependents); 163 | 164 | while ((available_sequence = min_sequence()) < sequence) { 165 | if (alerted.load()) return kAlertedSignal; 166 | 167 | if (stop <= std::chrono::system_clock::now()) return kTimeoutSignal; 168 | } 169 | 170 | return available_sequence; 171 | } 172 | 173 | virtual void SignalAllWhenBlocking() {} 174 | 175 | DISALLOW_COPY_MOVE_AND_ASSIGN(BusySpinStrategy); 176 | }; 177 | 178 | template 179 | class YieldingStrategy { 180 | public: 181 | YieldingStrategy() {} 182 | 183 | int64_t WaitFor(const int64_t& sequence, const Sequence& cursor, 184 | const std::vector& dependents, 185 | const std::atomic& alerted) { 186 | int64_t available_sequence = kInitialCursorValue; 187 | int counter = S; 188 | 189 | const auto min_sequence = buildMinSequenceFunction(cursor, dependents); 190 | 191 | while ((available_sequence = min_sequence()) < sequence) { 192 | if (alerted.load()) return kAlertedSignal; 193 | 194 | counter = ApplyWaitMethod(counter); 195 | } 196 | 197 | return available_sequence; 198 | } 199 | 200 | template 201 | int64_t WaitFor(const int64_t& sequence, const Sequence& cursor, 202 | const std::vector& dependents, 203 | const std::atomic& alerted, 204 | const std::chrono::duration& timeout) { 205 | int64_t available_sequence = kInitialCursorValue; 206 | int64_t counter = S; 207 | 208 | const auto start = std::chrono::system_clock::now(); 209 | const auto stop = start + timeout; 210 | const auto min_sequence = buildMinSequenceFunction(cursor, dependents); 211 | 212 | while ((available_sequence = min_sequence()) < sequence) { 213 | if (alerted.load()) return kAlertedSignal; 214 | 215 | counter = ApplyWaitMethod(counter); 216 | 217 | if (stop <= std::chrono::system_clock::now()) return kTimeoutSignal; 218 | } 219 | 220 | return available_sequence; 221 | } 222 | 223 | virtual void SignalAllWhenBlocking() {} 224 | 225 | private: 226 | inline int64_t ApplyWaitMethod(int64_t counter) { 227 | if (counter) { 228 | return --counter; 229 | } 230 | 231 | std::this_thread::yield(); 232 | return counter; 233 | } 234 | 235 | DISALLOW_COPY_MOVE_AND_ASSIGN(YieldingStrategy); 236 | }; 237 | 238 | template 240 | class SleepingStrategy { 241 | public: 242 | SleepingStrategy() {} 243 | 244 | int64_t WaitFor(const int64_t& sequence, const Sequence& cursor, 245 | const std::vector& dependents, 246 | const std::atomic& alerted) { 247 | int64_t available_sequence = kInitialCursorValue; 248 | int64_t counter = S; 249 | 250 | const auto min_sequence = buildMinSequenceFunction(cursor, dependents); 251 | 252 | while ((available_sequence = min_sequence()) < sequence) { 253 | if (alerted.load()) return kAlertedSignal; 254 | 255 | counter = ApplyWaitMethod(counter); 256 | } 257 | 258 | return available_sequence; 259 | } 260 | 261 | template 262 | int64_t WaitFor(const int64_t& sequence, const Sequence& cursor, 263 | const std::vector& dependents, 264 | const std::atomic& alerted, 265 | const std::chrono::duration& timeout) { 266 | int64_t available_sequence = kInitialCursorValue; 267 | int64_t counter = S; 268 | 269 | const auto start = std::chrono::system_clock::now(); 270 | const auto stop = start + timeout; 271 | const auto min_sequence = buildMinSequenceFunction(cursor, dependents); 272 | 273 | while ((available_sequence = min_sequence()) < sequence) { 274 | if (alerted.load()) return kAlertedSignal; 275 | 276 | counter = ApplyWaitMethod(counter); 277 | 278 | if (stop <= std::chrono::system_clock::now()) return kTimeoutSignal; 279 | } 280 | 281 | return available_sequence; 282 | } 283 | 284 | void SignalAllWhenBlocking() {} 285 | 286 | private: 287 | inline int64_t ApplyWaitMethod(int64_t counter) { 288 | if (counter > (S / 2)) { 289 | --counter; 290 | } else if (counter > 0) { 291 | --counter; 292 | std::this_thread::yield(); 293 | } else { 294 | std::this_thread::sleep_for(D(DV)); 295 | } 296 | 297 | return counter; 298 | } 299 | 300 | DISALLOW_COPY_MOVE_AND_ASSIGN(SleepingStrategy); 301 | }; 302 | 303 | class BlockingStrategy { 304 | private: 305 | using Lock = std::unique_lock; 306 | public: 307 | BlockingStrategy() {} 308 | 309 | int64_t WaitFor(const int64_t& sequence, const Sequence& cursor, 310 | const std::vector& dependents, 311 | const std::atomic& alerted) { 312 | return WaitFor(sequence, cursor, dependents, alerted, [this](Lock& lock) { 313 | consumer_notify_condition_.wait(lock); 314 | return false; 315 | }); 316 | } 317 | 318 | template 319 | int64_t WaitFor(const int64_t& sequence, const Sequence& cursor, 320 | const std::vector& dependents, 321 | const std::atomic& alerted, 322 | const std::chrono::duration& timeout) { 323 | return WaitFor(sequence, cursor, dependents, alerted, 324 | [this, timeout](Lock& lock) { 325 | return std::cv_status::timeout == 326 | consumer_notify_condition_.wait_for( 327 | lock, std::chrono::microseconds(timeout)); 328 | }); 329 | } 330 | 331 | void SignalAllWhenBlocking() { 332 | std::unique_lock ulock(mutex_); 333 | consumer_notify_condition_.notify_all(); 334 | } 335 | 336 | private: 337 | using Waiter = std::function; 338 | 339 | inline int64_t WaitFor(const int64_t& sequence, const Sequence& cursor, 340 | const std::vector& dependents, 341 | const std::atomic& alerted, 342 | const Waiter& locker) { 343 | int64_t available_sequence = kInitialCursorValue; 344 | // BlockingStrategy is a special case where the unblock signal comes from 345 | // the sequencer. This is why we need to wait on the cursor first, and 346 | // then on the dependents. 347 | if ((available_sequence = cursor.sequence()) < sequence) { 348 | std::unique_lock ulock(mutex_); 349 | while ((available_sequence = cursor.sequence()) < sequence) { 350 | if (alerted) return kAlertedSignal; 351 | 352 | // locker indicate if a timeout occured 353 | if (locker(ulock)) return kTimeoutSignal; 354 | } 355 | } 356 | 357 | // Now we wait on dependents. 358 | if (dependents.size()) { 359 | while ((available_sequence = GetMinimumSequence(dependents)) < sequence) { 360 | if (alerted) return kAlertedSignal; 361 | } 362 | } 363 | 364 | return available_sequence; 365 | } 366 | 367 | // members 368 | std::recursive_mutex mutex_; 369 | std::condition_variable_any consumer_notify_condition_; 370 | 371 | DISALLOW_COPY_MOVE_AND_ASSIGN(BlockingStrategy); 372 | }; 373 | 374 | static inline std::function buildMinSequenceFunction( 375 | const Sequence& cursor, const std::vector& dependents) { 376 | if (!dependents.size()) 377 | return [&cursor]() { return cursor.sequence(); }; 378 | else 379 | return [&dependents]() { return GetMinimumSequence(dependents); }; 380 | } 381 | 382 | }; // namespace disruptor 383 | 384 | #endif // DISRUPTOR_WAITSTRATEGY_H_ NOLINT 385 | --------------------------------------------------------------------------------