hmbdc
simplify-high-performance-messaging-programming
PoolMinusImpl.hpp
1 #include "RingBuffer.hpp"
2 #include "hmbdc/numeric/BitMath.hpp"
3 #include "hmbdc/time/Timers.hpp"
4 #include "hmbdc/pattern/PoolMinus.hpp"
5 #include "hmbdc/pattern/LockFreeBufferT.hpp"
6 #include "hmbdc/Exception.hpp"
7 #include "hmbdc/Compile.hpp"
8 #include "hmbdc/os/Thread.hpp"
9 
10 #include <boost/smart_ptr/detail/yield_k.hpp>
11 #include <utility>
12 #include <functional>
13 #include <vector>
14 #include <thread>
15 #include <string>
16 
17 #pragma GCC diagnostic push
18 #ifdef __clang__
19 #pragma GCC diagnostic ignored "-Waddress-of-packed-member"
20 #endif
21 
22 namespace hmbdc { namespace pattern {
23 
24 struct PoolMinusImpl {
25  PoolMinusImpl(PoolMinusImpl const&) = delete;
26  PoolMinusImpl& operator = (PoolMinusImpl const&) = delete;
27 
28  PoolMinusImpl(uint16_t maxConsumerSize
29  , uint64_t activePoolThreadSequenceMask = 0) //if set other than 0, start cannot be called
30  : stopped_(false)
31  , activePoolThreadSequenceMask_(activePoolThreadSequenceMask)
32  , consumerCount_(0u)
33  , consumerQ_(hmbdc::numeric::log2Upper(maxConsumerSize * 2u)) {
34  }
35 
36  uint32_t consumerSize() const {
37  return consumerCount_;
38  }
39 
40  void addConsumer(PoolConsumer& c, uint64_t poolThreadAffinityIn) {
41  auto count = __sync_add_and_fetch(&consumerCount_, 1u);
42  if (count > consumerQ_.CAPACITY) {
43  __sync_sub_and_fetch(&consumerCount_, 1u);
44  HMBDC_THROW(std::runtime_error
45  , "too many consumers, poolsize = " << consumerCount_);
46  }
47 
48  auto ptr = static_cast<PoolConsumer*>(&c);
49  ptr->reset();
50  c.poolThreadAffinity = poolThreadAffinityIn;
51  consumerQ_.put(&c);
52  }
53 
54  void start(uint16_t threadCount, uint64_t cpuAffinityMask) {
55  if (threadCount > 64u) {
56  HMBDC_THROW(std::runtime_error,
57  "threadCount too large " << threadCount << " > 64");
58  }
59  if (activePoolThreadSequenceMask_) {
60  HMBDC_THROW(std::runtime_error, "cannot restart a pool.");
61  }
62  activePoolThreadSequenceMask_ = (1ul << threadCount) - 1u;
63  for (auto i = 0u; i < threadCount; ++i) {
64  threads_.push_back(
65  std::thread([this, i, cpuAffinityMask]() {
66  threadEntry(i, cpuAffinityMask);
67  })
68  );
69  }
70  }
71 
72  void stop() {
73  __sync_synchronize();
74  stopped_ = true;
75  }
76 
77  void join() {
78  for (Threads::value_type& t : threads_) t.join();
79  threads_.clear();
80  }
81 
83 
84  template <typename HoggingCond>
85  void runOnce(uint16_t threadSerialNumber
86  , HoggingCond&& hoggingCond) {
87  runOnceImpl(threadSerialNumber, std::forward<HoggingCond>(hoggingCond));
88  }
89 
90  void cleanup(uint16_t threadSerialNumber) {
91  if (stopped_) {
92  while (consumerCount_) {
93  PoolConsumer* consumer;
94  if (consumerQ_.tryTake(consumer)) {
95  if (hmbdc_unlikely(!(consumer->poolThreadAffinity & (1u << threadSerialNumber)))) {
96  consumerQ_.put(consumer);
97  continue;
98  }
99  __sync_sub_and_fetch(&consumerCount_, 1u);
100  consumer->dropped();
101  }
102  }
103  }
104  }
105 
106 private:
107  void threadEntry(uint16_t threadSerialNumber, uint64_t cpuAffinityMask) HMBDC_RESTRICT {
108  std::string name = "hmbdcp" + std::to_string(threadSerialNumber);
109  if (cpuAffinityMask == 0) {
110  auto cpuCount = std::thread::hardware_concurrency();
111  cpuAffinityMask = (1ul << cpuCount) - 1u;
112  }
113  cpuAffinityMask = hmbdc::numeric::nthSetBitFromLsb(cpuAffinityMask
114  , threadSerialNumber % hmbdc::numeric::setBitsCount(cpuAffinityMask));
115 
116  hmbdc::os::configureCurrentThread(name.c_str(), cpuAffinityMask);
117 
118  while(!stopped_){
119  runOnceImpl(threadSerialNumber, [](){return true;});
120  }
121  cleanup(threadSerialNumber);
122  }
123 
124  template <typename HoggingCond>
125  void runOnceImpl(uint16_t threadSerialNumber, HoggingCond&& hoggingCond) HMBDC_RESTRICT {
126  if (hmbdc_unlikely(stopped_)) return;
127  PoolConsumer* consumer;
128  if (hmbdc_unlikely(!consumerQ_.tryTake(consumer))) return;
129  if (hmbdc_unlikely(!(consumer->poolThreadAffinity & (1u << threadSerialNumber)))) {
130  consumerQ_.put(consumer);
131  return;
132  }
133  try {
134  if (hmbdc_unlikely(!consumer->messageDispatchingStarted_)) {
135  consumer->messageDispatchingStarted();
136  }
137  do {
138  consumer->invoked(threadSerialNumber);
139  } while (hoggingCond() && consumerQ_.remainingSize() == 0 && !stopped_);
140  } catch (std::exception const& e) {
141  consumer->stopped(e);
142  __sync_sub_and_fetch(&consumerCount_, 1u);
143  if (!consumer->dropped()) {//resume
144  addConsumer(*consumer
145  , consumer->poolThreadAffinity & activePoolThreadSequenceMask_);
146  }
147  return;
148  }
149  consumerQ_.put(consumer);
150  }
151 
152 
153  using Threads = std::vector<std::thread>;
154  Threads threads_;
155  bool stopped_;
156  uint64_t activePoolThreadSequenceMask_;
157  uint32_t consumerCount_;
158  ConsumerQ consumerQ_;
159 };
160 }}
161 
162 #pragma GCC diagnostic pop
163 
164 #include "hmbdc/os/Allocators.hpp"
165 
166 namespace hmbdc { namespace pattern {
167 
168 inline
169 PoolMinus::
170 PoolMinus(uint32_t maxConsumerSize)
171 : impl_(os::DefaultAllocator::instance().allocate<PoolMinusImpl>(SMP_CACHE_BYTES, maxConsumerSize)) {
172 }
173 
174 inline
175 PoolMinus::
176 ~PoolMinus() {
177  os::DefaultAllocator::instance().unallocate(static_cast<PoolMinusImpl*>(impl_));
178 }
179 
180 inline void PoolMinus::addConsumer(PoolConsumer& c, uint64_t poolThreadAffinityIn) { static_cast<PoolMinusImpl*>(impl_)->addConsumer(c, poolThreadAffinityIn);}
181 inline uint32_t PoolMinus::consumerSize() const {return static_cast<PoolMinusImpl*>(impl_)->consumerSize();}
182 inline void PoolMinus::start(uint16_t threadCount, uint64_t cpuAffinityMask, bool) { static_cast<PoolMinusImpl*>(impl_)->start(threadCount, cpuAffinityMask);}
183 inline void PoolMinus::runOnce(uint16_t threadSerialNumber) HMBDC_RESTRICT { static_cast<PoolMinusImpl*>(impl_)->runOnce(threadSerialNumber, [](){return true;});}
184 inline void PoolMinus::stop() { static_cast<PoolMinusImpl*>(impl_)->stop();}
185 inline void PoolMinus::join() { static_cast<PoolMinusImpl*>(impl_)->join();}
186 }}
Definition: PoolConsumer.hpp:16
Definition: PoolMinusImpl.hpp:24
Definition: Base.hpp:12