condition_algorithm_8a.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
  4. // Software License, Version 1.0. (See accompanying file
  5. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // See http://www.boost.org/libs/interprocess for documentation.
  8. //
  9. //////////////////////////////////////////////////////////////////////////////
  10. #ifndef BOOST_INTERPROCESS_DETAIL_CONDITION_ALGORITHM_8A_HPP
  11. #define BOOST_INTERPROCESS_DETAIL_CONDITION_ALGORITHM_8A_HPP
  12. #ifndef BOOST_CONFIG_HPP
  13. # include <boost/config.hpp>
  14. #endif
  15. #
  16. #if defined(BOOST_HAS_PRAGMA_ONCE)
  17. # pragma once
  18. #endif
  19. #include <boost/interprocess/detail/config_begin.hpp>
  20. #include <boost/interprocess/detail/workaround.hpp>
  21. #include <boost/interprocess/sync/scoped_lock.hpp>
  22. #include <boost/interprocess/sync/detail/locks.hpp>
  23. #include <limits>
  24. namespace boost {
  25. namespace interprocess {
  26. namespace ipcdetail {
  27. ////////////////////////////////////////////////////////////////////////
  28. ////////////////////////////////////////////////////////////////////////
  29. ////////////////////////////////////////////////////////////////////////
  30. //
  31. // Condition variable algorithm taken from pthreads-win32 discussion.
  32. //
  33. // The algorithm was developed by Alexander Terekhov in colaboration with
  34. // Louis Thomas.
  35. //
  36. // Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
  37. //
  38. // semBlockLock - bin.semaphore
  39. // semBlockQueue - semaphore
  40. // mtxExternal - mutex or CS
  41. // mtxUnblockLock - mutex or CS
  42. // nWaitersGone - int
  43. // nWaitersBlocked - int
  44. // nWaitersToUnblock - int
  45. //
  46. // wait( timeout ) {
  47. //
  48. // [auto: register int result ] // error checking omitted
  49. // [auto: register int nSignalsWasLeft ]
  50. // [auto: register int nWaitersWasGone ]
  51. //
  52. // sem_wait( semBlockLock );
  53. // nWaitersBlocked++;
  54. // sem_post( semBlockLock );
  55. //
  56. // unlock( mtxExternal );
  57. // bTimedOut = sem_wait( semBlockQueue,timeout );
  58. //
  59. // lock( mtxUnblockLock );
  60. // if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
  61. // if ( bTimedOut ) { // timeout (or canceled)
  62. // if ( 0 != nWaitersBlocked ) {
  63. // nWaitersBlocked--;
  64. // }
  65. // else {
  66. // nWaitersGone++; // count spurious wakeups.
  67. // }
  68. // }
  69. // if ( 0 == --nWaitersToUnblock ) {
  70. // if ( 0 != nWaitersBlocked ) {
  71. // sem_post( semBlockLock ); // open the gate.
  72. // nSignalsWasLeft = 0; // do not open the gate
  73. // // below again.
  74. // }
  75. // else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
  76. // nWaitersGone = 0;
  77. // }
  78. // }
  79. // }
  80. // else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
  81. // // spurious semaphore :-)
  82. // sem_wait( semBlockLock );
  83. // nWaitersBlocked -= nWaitersGone; // something is going on here
  84. // // - test of timeouts? :-)
  85. // sem_post( semBlockLock );
  86. // nWaitersGone = 0;
  87. // }
  88. // unlock( mtxUnblockLock );
  89. //
  90. // if ( 1 == nSignalsWasLeft ) {
  91. // if ( 0 != nWaitersWasGone ) {
  92. // // sem_adjust( semBlockQueue,-nWaitersWasGone );
  93. // while ( nWaitersWasGone-- ) {
  94. // sem_wait( semBlockQueue ); // better now than spurious later
  95. // }
  96. // } sem_post( semBlockLock ); // open the gate
  97. // }
  98. //
  99. // lock( mtxExternal );
  100. //
  101. // return ( bTimedOut ) ? ETIMEOUT : 0;
  102. // }
  103. //
  104. // signal(bAll) {
  105. //
  106. // [auto: register int result ]
  107. // [auto: register int nSignalsToIssue]
  108. //
  109. // lock( mtxUnblockLock );
  110. //
  111. // if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
  112. // if ( 0 == nWaitersBlocked ) { // NO-OP
  113. // return unlock( mtxUnblockLock );
  114. // }
  115. // if (bAll) {
  116. // nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
  117. // nWaitersBlocked = 0;
  118. // }
  119. // else {
  120. // nSignalsToIssue = 1;
  121. // nWaitersToUnblock++;
  122. // nWaitersBlocked--;
  123. // }
  124. // }
  125. // else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
  126. // sem_wait( semBlockLock ); // close the gate
  127. // if ( 0 != nWaitersGone ) {
  128. // nWaitersBlocked -= nWaitersGone;
  129. // nWaitersGone = 0;
  130. // }
  131. // if (bAll) {
  132. // nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
  133. // nWaitersBlocked = 0;
  134. // }
  135. // else {
  136. // nSignalsToIssue = nWaitersToUnblock = 1;
  137. // nWaitersBlocked--;
  138. // }
  139. // }
  140. // else { // NO-OP
  141. // return unlock( mtxUnblockLock );
  142. // }
  143. //
  144. // unlock( mtxUnblockLock );
  145. // sem_post( semBlockQueue,nSignalsToIssue );
  146. // return result;
  147. // }
  148. ////////////////////////////////////////////////////////////////////////
  149. ////////////////////////////////////////////////////////////////////////
  150. ////////////////////////////////////////////////////////////////////////
  151. // Required interface for ConditionMembers
  152. // class ConditionMembers
  153. // {
  154. // typedef implementation_defined semaphore_type;
  155. // typedef implementation_defined mutex_type;
  156. // typedef implementation_defined integer_type;
  157. //
  158. // integer_type &get_nwaiters_blocked()
  159. // integer_type &get_nwaiters_gone()
  160. // integer_type &get_nwaiters_to_unblock()
  161. // semaphore_type &get_sem_block_queue()
  162. // semaphore_type &get_sem_block_lock()
  163. // mutex_type &get_mtx_unblock_lock()
  164. // };
  165. //
  166. // Must be initialized as following
  167. //
  168. // get_nwaiters_blocked() == 0
  169. // get_nwaiters_gone() == 0
  170. // get_nwaiters_to_unblock() == 0
  171. // get_sem_block_queue() == initial count 0
  172. // get_sem_block_lock() == initial count 1
  173. // get_mtx_unblock_lock() (unlocked)
  174. //
  175. template<class ConditionMembers>
  176. class condition_algorithm_8a
  177. {
  178. private:
  179. condition_algorithm_8a();
  180. ~condition_algorithm_8a();
  181. condition_algorithm_8a(const condition_algorithm_8a &);
  182. condition_algorithm_8a &operator=(const condition_algorithm_8a &);
  183. typedef typename ConditionMembers::semaphore_type semaphore_type;
  184. typedef typename ConditionMembers::mutex_type mutex_type;
  185. typedef typename ConditionMembers::integer_type integer_type;
  186. public:
  187. template<bool TimeoutEnabled, class Lock, class TimePoint>
  188. static bool wait ( ConditionMembers &data, Lock &lock, const TimePoint &abs_time)
  189. {
  190. //Initialize to avoid warnings
  191. integer_type nsignals_was_left = 0;
  192. integer_type nwaiters_was_gone = 0;
  193. data.get_sem_block_lock().wait();
  194. ++data.get_nwaiters_blocked();
  195. data.get_sem_block_lock().post();
  196. //Unlock external lock and program for relock
  197. lock_inverter<Lock> inverted_lock(lock);
  198. scoped_lock<lock_inverter<Lock> > external_unlock(inverted_lock);
  199. bool bTimedOut = !do_sem_timed_wait(data.get_sem_block_queue(), abs_time, bool_<TimeoutEnabled>());
  200. {
  201. scoped_lock<mutex_type> locker(data.get_mtx_unblock_lock());
  202. if ( 0 != (nsignals_was_left = data.get_nwaiters_to_unblock()) ) {
  203. if ( bTimedOut ) { // timeout (or canceled)
  204. if ( 0 != data.get_nwaiters_blocked() ) {
  205. data.get_nwaiters_blocked()--;
  206. }
  207. else {
  208. data.get_nwaiters_gone()++; // count spurious wakeups.
  209. }
  210. }
  211. if ( 0 == --data.get_nwaiters_to_unblock() ) {
  212. if ( 0 != data.get_nwaiters_blocked() ) {
  213. data.get_sem_block_lock().post(); // open the gate.
  214. nsignals_was_left = 0; // do not open the gate below again.
  215. }
  216. else if ( 0 != (nwaiters_was_gone = data.get_nwaiters_gone()) ) {
  217. data.get_nwaiters_gone() = 0;
  218. }
  219. }
  220. }
  221. else if ( (std::numeric_limits<integer_type>::max)()/2
  222. == ++data.get_nwaiters_gone() ) { // timeout/canceled or spurious semaphore :-)
  223. data.get_sem_block_lock().wait();
  224. data.get_nwaiters_blocked() -= data.get_nwaiters_gone(); // something is going on here - test of timeouts? :-)
  225. data.get_sem_block_lock().post();
  226. data.get_nwaiters_gone() = 0;
  227. }
  228. //locker's destructor triggers data.get_mtx_unblock_lock().unlock()
  229. }
  230. if ( 1 == nsignals_was_left ) {
  231. if ( 0 != nwaiters_was_gone ) {
  232. // sem_adjust( data.get_sem_block_queue(),-nwaiters_was_gone );
  233. while ( nwaiters_was_gone-- ) {
  234. data.get_sem_block_queue().wait(); // better now than spurious later
  235. }
  236. }
  237. data.get_sem_block_lock().post(); // open the gate
  238. }
  239. //lock.lock(); called from unlocker destructor
  240. return ( bTimedOut ) ? false : true;
  241. }
  242. static void signal(ConditionMembers &data, bool broadcast)
  243. {
  244. integer_type nsignals_to_issue;
  245. {
  246. scoped_lock<mutex_type> locker(data.get_mtx_unblock_lock());
  247. if ( 0 != data.get_nwaiters_to_unblock() ) { // the gate is closed!!!
  248. if ( 0 == data.get_nwaiters_blocked() ) { // NO-OP
  249. //locker's destructor triggers data.get_mtx_unblock_lock().unlock()
  250. return;
  251. }
  252. if (broadcast) {
  253. data.get_nwaiters_to_unblock() += nsignals_to_issue = data.get_nwaiters_blocked();
  254. data.get_nwaiters_blocked() = 0;
  255. }
  256. else {
  257. nsignals_to_issue = 1;
  258. data.get_nwaiters_to_unblock()++;
  259. data.get_nwaiters_blocked()--;
  260. }
  261. }
  262. else if ( data.get_nwaiters_blocked() > data.get_nwaiters_gone() ) { // HARMLESS RACE CONDITION!
  263. data.get_sem_block_lock().wait(); // close the gate
  264. if ( 0 != data.get_nwaiters_gone() ) {
  265. data.get_nwaiters_blocked() -= data.get_nwaiters_gone();
  266. data.get_nwaiters_gone() = 0;
  267. }
  268. if (broadcast) {
  269. nsignals_to_issue = data.get_nwaiters_to_unblock() = data.get_nwaiters_blocked();
  270. data.get_nwaiters_blocked() = 0;
  271. }
  272. else {
  273. nsignals_to_issue = data.get_nwaiters_to_unblock() = 1;
  274. data.get_nwaiters_blocked()--;
  275. }
  276. }
  277. else { // NO-OP
  278. //locker's destructor triggers data.get_mtx_unblock_lock().unlock()
  279. return;
  280. }
  281. //locker's destructor triggers data.get_mtx_unblock_lock().unlock()
  282. }
  283. data.get_sem_block_queue().post(nsignals_to_issue);
  284. }
  285. private:
  286. template<class TimePoint>
  287. static bool do_sem_timed_wait(semaphore_type &sem, const TimePoint &abs_time, bool_<true>)
  288. { return sem.timed_wait(abs_time); }
  289. template<class TimePoint>
  290. static bool do_sem_timed_wait(semaphore_type &sem, const TimePoint &, bool_<false>)
  291. { sem.wait(); return true; }
  292. };
  293. template<class ConditionMembers>
  294. class condition_8a_wrapper
  295. {
  296. //Non-copyable
  297. condition_8a_wrapper(const condition_8a_wrapper &);
  298. condition_8a_wrapper &operator=(const condition_8a_wrapper &);
  299. ConditionMembers m_data;
  300. typedef condition_algorithm_8a<ConditionMembers> algo_type;
  301. public:
  302. condition_8a_wrapper(){}
  303. //Compiler-generated destructor is OK
  304. //~condition_8a_wrapper(){}
  305. ConditionMembers & get_members()
  306. { return m_data; }
  307. const ConditionMembers & get_members() const
  308. { return m_data; }
  309. void notify_one()
  310. { algo_type::signal(m_data, false); }
  311. void notify_all()
  312. { algo_type::signal(m_data, true); }
  313. template <typename L>
  314. void wait(L& lock)
  315. {
  316. if (!lock)
  317. throw lock_exception();
  318. algo_type::template wait<false>(m_data, lock, 0);
  319. }
  320. template <typename L, typename Pr>
  321. void wait(L& lock, Pr pred)
  322. {
  323. if (!lock)
  324. throw lock_exception();
  325. while (!pred())
  326. algo_type::template wait<false>(m_data, lock, 0);
  327. }
  328. template <typename L, class TimePoint>
  329. bool timed_wait(L& lock, const TimePoint &abs_time)
  330. {
  331. if (!lock)
  332. throw lock_exception();
  333. return algo_type::template wait<true>(m_data, lock, abs_time);
  334. }
  335. template <typename L, class TimePoint, typename Pr>
  336. bool timed_wait(L& lock, const TimePoint &abs_time, Pr pred)
  337. {
  338. if (!lock)
  339. throw lock_exception();
  340. while (!pred()){
  341. if (!algo_type::template wait<true>(m_data, lock, abs_time))
  342. return pred();
  343. }
  344. return true;
  345. }
  346. };
  347. } //namespace ipcdetail
  348. } //namespace interprocess
  349. } //namespace boost
  350. #include <boost/interprocess/detail/config_end.hpp>
  351. #endif //BOOST_INTERPROCESS_DETAIL_CONDITION_ALGORITHM_8A_HPP