condition_algorithm_8a.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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<class Lock>
  188. static bool wait ( ConditionMembers &data, Lock &lock
  189. , bool timeout_enabled, const boost::posix_time::ptime &abs_time);
  190. static void signal(ConditionMembers &data, bool broadcast);
  191. };
  192. template<class ConditionMembers>
  193. inline void condition_algorithm_8a<ConditionMembers>::signal(ConditionMembers &data, bool broadcast)
  194. {
  195. integer_type nsignals_to_issue;
  196. {
  197. scoped_lock<mutex_type> locker(data.get_mtx_unblock_lock());
  198. if ( 0 != data.get_nwaiters_to_unblock() ) { // the gate is closed!!!
  199. if ( 0 == data.get_nwaiters_blocked() ) { // NO-OP
  200. //locker's destructor triggers data.get_mtx_unblock_lock().unlock()
  201. return;
  202. }
  203. if (broadcast) {
  204. data.get_nwaiters_to_unblock() += nsignals_to_issue = data.get_nwaiters_blocked();
  205. data.get_nwaiters_blocked() = 0;
  206. }
  207. else {
  208. nsignals_to_issue = 1;
  209. data.get_nwaiters_to_unblock()++;
  210. data.get_nwaiters_blocked()--;
  211. }
  212. }
  213. else if ( data.get_nwaiters_blocked() > data.get_nwaiters_gone() ) { // HARMLESS RACE CONDITION!
  214. data.get_sem_block_lock().wait(); // close the gate
  215. if ( 0 != data.get_nwaiters_gone() ) {
  216. data.get_nwaiters_blocked() -= data.get_nwaiters_gone();
  217. data.get_nwaiters_gone() = 0;
  218. }
  219. if (broadcast) {
  220. nsignals_to_issue = data.get_nwaiters_to_unblock() = data.get_nwaiters_blocked();
  221. data.get_nwaiters_blocked() = 0;
  222. }
  223. else {
  224. nsignals_to_issue = data.get_nwaiters_to_unblock() = 1;
  225. data.get_nwaiters_blocked()--;
  226. }
  227. }
  228. else { // NO-OP
  229. //locker's destructor triggers data.get_mtx_unblock_lock().unlock()
  230. return;
  231. }
  232. //locker's destructor triggers data.get_mtx_unblock_lock().unlock()
  233. }
  234. data.get_sem_block_queue().post(nsignals_to_issue);
  235. }
  236. template<class ConditionMembers>
  237. template<class Lock>
  238. inline bool condition_algorithm_8a<ConditionMembers>::wait
  239. ( ConditionMembers &data
  240. , Lock &lock
  241. , bool tout_enabled
  242. , const boost::posix_time::ptime &abs_time
  243. )
  244. {
  245. //Initialize to avoid warnings
  246. integer_type nsignals_was_left = 0;
  247. integer_type nwaiters_was_gone = 0;
  248. data.get_sem_block_lock().wait();
  249. ++data.get_nwaiters_blocked();
  250. data.get_sem_block_lock().post();
  251. //Unlock external lock and program for relock
  252. lock_inverter<Lock> inverted_lock(lock);
  253. scoped_lock<lock_inverter<Lock> > external_unlock(inverted_lock);
  254. bool bTimedOut = tout_enabled
  255. ? !data.get_sem_block_queue().timed_wait(abs_time)
  256. : (data.get_sem_block_queue().wait(), false);
  257. {
  258. scoped_lock<mutex_type> locker(data.get_mtx_unblock_lock());
  259. if ( 0 != (nsignals_was_left = data.get_nwaiters_to_unblock()) ) {
  260. if ( bTimedOut ) { // timeout (or canceled)
  261. if ( 0 != data.get_nwaiters_blocked() ) {
  262. data.get_nwaiters_blocked()--;
  263. }
  264. else {
  265. data.get_nwaiters_gone()++; // count spurious wakeups.
  266. }
  267. }
  268. if ( 0 == --data.get_nwaiters_to_unblock() ) {
  269. if ( 0 != data.get_nwaiters_blocked() ) {
  270. data.get_sem_block_lock().post(); // open the gate.
  271. nsignals_was_left = 0; // do not open the gate below again.
  272. }
  273. else if ( 0 != (nwaiters_was_gone = data.get_nwaiters_gone()) ) {
  274. data.get_nwaiters_gone() = 0;
  275. }
  276. }
  277. }
  278. else if ( (std::numeric_limits<integer_type>::max)()/2
  279. == ++data.get_nwaiters_gone() ) { // timeout/canceled or spurious semaphore :-)
  280. data.get_sem_block_lock().wait();
  281. data.get_nwaiters_blocked() -= data.get_nwaiters_gone(); // something is going on here - test of timeouts? :-)
  282. data.get_sem_block_lock().post();
  283. data.get_nwaiters_gone() = 0;
  284. }
  285. //locker's destructor triggers data.get_mtx_unblock_lock().unlock()
  286. }
  287. if ( 1 == nsignals_was_left ) {
  288. if ( 0 != nwaiters_was_gone ) {
  289. // sem_adjust( data.get_sem_block_queue(),-nwaiters_was_gone );
  290. while ( nwaiters_was_gone-- ) {
  291. data.get_sem_block_queue().wait(); // better now than spurious later
  292. }
  293. }
  294. data.get_sem_block_lock().post(); // open the gate
  295. }
  296. //lock.lock(); called from unlocker destructor
  297. return ( bTimedOut ) ? false : true;
  298. }
  299. template<class ConditionMembers>
  300. class condition_8a_wrapper
  301. {
  302. //Non-copyable
  303. condition_8a_wrapper(const condition_8a_wrapper &);
  304. condition_8a_wrapper &operator=(const condition_8a_wrapper &);
  305. ConditionMembers m_data;
  306. typedef ipcdetail::condition_algorithm_8a<ConditionMembers> algo_type;
  307. public:
  308. condition_8a_wrapper(){}
  309. //Compiler-generated destructor is OK
  310. //~condition_8a_wrapper(){}
  311. ConditionMembers & get_members()
  312. { return m_data; }
  313. const ConditionMembers & get_members() const
  314. { return m_data; }
  315. void notify_one()
  316. { algo_type::signal(m_data, false); }
  317. void notify_all()
  318. { algo_type::signal(m_data, true); }
  319. template <typename L>
  320. void wait(L& lock)
  321. {
  322. if (!lock)
  323. throw lock_exception();
  324. algo_type::wait(m_data, lock, false, boost::posix_time::ptime());
  325. }
  326. template <typename L, typename Pr>
  327. void wait(L& lock, Pr pred)
  328. {
  329. if (!lock)
  330. throw lock_exception();
  331. while (!pred())
  332. algo_type::wait(m_data, lock, false, boost::posix_time::ptime());
  333. }
  334. template <typename L>
  335. bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time)
  336. {
  337. if (!lock)
  338. throw lock_exception();
  339. return algo_type::wait(m_data, lock, true, abs_time);
  340. }
  341. template <typename L, typename Pr>
  342. bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred)
  343. {
  344. if (!lock)
  345. throw lock_exception();
  346. while (!pred()){
  347. if (!algo_type::wait(m_data, lock, true, abs_time))
  348. return pred();
  349. }
  350. return true;
  351. }
  352. };
  353. } //namespace ipcdetail
  354. } //namespace interprocess
  355. } //namespace boost
  356. #include <boost/interprocess/detail/config_end.hpp>
  357. #endif //BOOST_INTERPROCESS_DETAIL_CONDITION_ALGORITHM_8A_HPP