jmutex.hpp 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #ifndef __JMUTEX__
  14. #define __JMUTEX__
  15. #include <assert.h>
  16. #include <atomic>
  17. #include "jiface.hpp"
  18. #include "jsem.hpp"
  19. extern jlib_decl void ThreadYield();
  20. extern jlib_decl void spinUntilReady(std::atomic_uint &value);
  21. #ifdef _DEBUG
  22. //#define SPINLOCK_USE_MUTEX // for testing
  23. #define SPINLOCK_RR_CHECK // checks for realtime threads
  24. #define _ASSERT_LOCK_SUPPORT
  25. #endif
  26. #ifdef SPINLOCK_USE_MUTEX
  27. #define NRESPINLOCK_USE_SPINLOCK
  28. #endif
  29. #ifdef _WIN32
  30. #define NRESPINLOCK_USE_SPINLOCK
  31. #endif
  32. #ifdef _WIN32
  33. class jlib_decl Mutex
  34. {
  35. protected:
  36. Mutex(const char *name)
  37. {
  38. mutex = CreateMutex(NULL, FALSE, name);
  39. assertex(mutex);
  40. lockcount = 0;
  41. owner = 0;
  42. }
  43. public:
  44. Mutex()
  45. {
  46. mutex = CreateMutex(NULL, FALSE, NULL);
  47. lockcount = 0;
  48. owner = 0;
  49. }
  50. ~Mutex()
  51. {
  52. if (owner != 0)
  53. printf("Warning - Owned mutex destroyed"); // can't use DBGLOG here!
  54. CloseHandle(mutex);
  55. }
  56. void lock()
  57. {
  58. WaitForSingleObject(mutex, INFINITE);
  59. if (lockcount) {
  60. if(owner!=GetCurrentThreadId()) // I think only way this can happen is with unhandled thread exception
  61. lockcount = 0; // (don't assert as unhandled error may get lost)
  62. }
  63. lockcount++;
  64. owner=GetCurrentThreadId();
  65. }
  66. bool lockWait(unsigned timeout)
  67. {
  68. if (WaitForSingleObject(mutex, (long)timeout)!=WAIT_OBJECT_0)
  69. return false;
  70. if (lockcount) {
  71. if(owner!=GetCurrentThreadId()) // I think only way this can happen is with unhandled thread exception
  72. lockcount = 0; // (don't assert as unhandled error may get lost)
  73. }
  74. lockcount++;
  75. owner=GetCurrentThreadId();
  76. return true;
  77. }
  78. void unlock()
  79. {
  80. assertex(owner==GetCurrentThreadId());
  81. --lockcount;
  82. if (lockcount==0)
  83. owner = 0;
  84. ReleaseMutex(mutex);
  85. }
  86. protected:
  87. MutexId mutex;
  88. ThreadId owner;
  89. int unlockAll()
  90. {
  91. assertex(owner==GetCurrentThreadId());
  92. assertex(lockcount);
  93. int ret = lockcount;
  94. int lc = ret;
  95. while (lc--)
  96. unlock();
  97. return ret;
  98. }
  99. void lockAll(int count)
  100. {
  101. while (count--)
  102. lock();
  103. }
  104. private:
  105. int lockcount;
  106. };
  107. class jlib_decl NamedMutex: public Mutex
  108. {
  109. public:
  110. NamedMutex(const char *name)
  111. : Mutex(name)
  112. {
  113. }
  114. };
  115. #else // posix
  116. class jlib_decl Mutex
  117. {
  118. public:
  119. Mutex();
  120. // Mutex(const char *name); //not supported
  121. ~Mutex();
  122. void lock();
  123. bool lockWait(unsigned timeout);
  124. void unlock();
  125. protected:
  126. MutexId mutex;
  127. ThreadId owner;
  128. int unlockAll();
  129. void lockAll(int);
  130. private:
  131. int lockcount;
  132. pthread_cond_t lock_free;
  133. };
  134. class jlib_decl NamedMutex
  135. {
  136. public:
  137. NamedMutex(const char *name);
  138. ~NamedMutex();
  139. void lock();
  140. bool lockWait(unsigned timeout);
  141. void unlock();
  142. private:
  143. Mutex threadmutex;
  144. char *mutexfname;
  145. };
  146. #endif
  147. class jlib_decl synchronized
  148. {
  149. private:
  150. Mutex &mutex;
  151. void throwLockException(unsigned timeout);
  152. public:
  153. synchronized(Mutex &m) : mutex(m) { mutex.lock(); };
  154. synchronized(Mutex &m,unsigned timeout) : mutex(m) { if(!mutex.lockWait(timeout)) throwLockException(timeout); }
  155. inline ~synchronized() { mutex.unlock(); };
  156. };
  157. #ifdef _WIN32
  158. extern "C" {
  159. WINBASEAPI
  160. BOOL
  161. WINAPI
  162. TryEnterCriticalSection(
  163. IN OUT LPCRITICAL_SECTION lpCriticalSection
  164. );
  165. };
  166. class jlib_decl CriticalSection
  167. {
  168. // lightweight mutex within a single process
  169. private:
  170. CRITICAL_SECTION flags;
  171. #ifdef _ASSERT_LOCK_SUPPORT
  172. ThreadId owner;
  173. unsigned depth;
  174. #endif
  175. inline CriticalSection(CriticalSection &) = delete;
  176. public:
  177. inline CriticalSection()
  178. {
  179. InitializeCriticalSection(&flags);
  180. #ifdef _ASSERT_LOCK_SUPPORT
  181. owner = 0;
  182. depth = 0;
  183. #endif
  184. };
  185. inline ~CriticalSection()
  186. {
  187. #ifdef _ASSERT_LOCK_SUPPORT
  188. assertex(owner==0 && depth==0);
  189. #endif
  190. DeleteCriticalSection(&flags);
  191. };
  192. inline void enter()
  193. {
  194. EnterCriticalSection(&flags);
  195. #ifdef _ASSERT_LOCK_SUPPORT
  196. if (owner)
  197. {
  198. assertex(owner==GetCurrentThreadId());
  199. depth++;
  200. }
  201. else
  202. owner = GetCurrentThreadId();
  203. #endif
  204. };
  205. inline void leave()
  206. {
  207. #ifdef _ASSERT_LOCK_SUPPORT
  208. assertex(owner==GetCurrentThreadId());
  209. if (depth)
  210. depth--;
  211. else
  212. owner = 0;
  213. #endif
  214. LeaveCriticalSection(&flags);
  215. };
  216. inline void assertLocked()
  217. {
  218. #ifdef _ASSERT_LOCK_SUPPORT
  219. assertex(owner == GetCurrentThreadId());
  220. #endif
  221. }
  222. #ifdef ENABLE_CHECKEDCRITICALSECTIONS
  223. bool wouldBlock() { if (TryEnterCriticalSection(&flags)) { leave(); return false; } return true; } // debug only
  224. #endif
  225. };
  226. #else
  227. /**
  228. * Mutex locking wrapper. Use enter/leave to lock/unlock.
  229. */
  230. class CriticalSection
  231. {
  232. private:
  233. MutexId mutex;
  234. #ifdef _ASSERT_LOCK_SUPPORT
  235. ThreadId owner;
  236. unsigned depth;
  237. #endif
  238. CriticalSection (const CriticalSection &);
  239. public:
  240. inline CriticalSection()
  241. {
  242. pthread_mutexattr_t attr;
  243. pthread_mutexattr_init(&attr);
  244. #ifdef _DEBUG
  245. verifyex(pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE)==0); // verify supports attr
  246. #else
  247. pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
  248. #endif
  249. pthread_mutex_init(&mutex, &attr);
  250. pthread_mutexattr_destroy(&attr);
  251. #ifdef _ASSERT_LOCK_SUPPORT
  252. owner = 0;
  253. depth = 0;
  254. #endif
  255. }
  256. inline ~CriticalSection()
  257. {
  258. #ifdef _ASSERT_LOCK_SUPPORT
  259. assert(owner==0 && depth==0);
  260. #endif
  261. pthread_mutex_destroy(&mutex);
  262. }
  263. inline void enter()
  264. {
  265. pthread_mutex_lock(&mutex);
  266. #ifdef _ASSERT_LOCK_SUPPORT
  267. if (owner)
  268. {
  269. assertex(owner==GetCurrentThreadId());
  270. depth++;
  271. }
  272. else
  273. owner = GetCurrentThreadId();
  274. #endif
  275. }
  276. inline void leave()
  277. {
  278. #ifdef _ASSERT_LOCK_SUPPORT
  279. assertex(owner==GetCurrentThreadId());
  280. if (depth)
  281. depth--;
  282. else
  283. owner = 0;
  284. #endif
  285. pthread_mutex_unlock(&mutex);
  286. }
  287. inline void assertLocked()
  288. {
  289. #ifdef _ASSERT_LOCK_SUPPORT
  290. assertex(owner == GetCurrentThreadId());
  291. #endif
  292. }
  293. };
  294. #endif
  295. /**
  296. * Critical section delimiter, using scope to define lifetime of
  297. * the lock on a critical section (parameter).
  298. * Blocks on construction, unblocks on destruction.
  299. */
  300. class CriticalBlock
  301. {
  302. CriticalSection &crit;
  303. public:
  304. inline CriticalBlock(CriticalSection &c) : crit(c) { crit.enter(); }
  305. inline ~CriticalBlock() { crit.leave(); }
  306. };
  307. /**
  308. * Critical section delimiter, using scope to define lifetime of
  309. * the lock on a critical section (parameter).
  310. * Unblocks on construction, blocks on destruction.
  311. */
  312. class CriticalUnblock
  313. {
  314. CriticalSection &crit;
  315. public:
  316. inline CriticalUnblock(CriticalSection &c) : crit(c) { crit.leave(); }
  317. inline ~CriticalUnblock() { crit.enter(); }
  318. };
  319. class CLeavableCriticalBlock
  320. {
  321. CriticalSection &crit;
  322. bool locked = false;
  323. public:
  324. inline CLeavableCriticalBlock(CriticalSection &_crit) : crit(_crit)
  325. {
  326. enter();
  327. }
  328. inline CLeavableCriticalBlock(CriticalSection &_crit, bool lock) : crit(_crit)
  329. {
  330. if (lock)
  331. enter();
  332. }
  333. inline ~CLeavableCriticalBlock()
  334. {
  335. if (locked)
  336. crit.leave();
  337. }
  338. inline void enter()
  339. {
  340. if (locked)
  341. return;
  342. crit.enter();
  343. locked = true;
  344. }
  345. inline void leave()
  346. {
  347. if (locked)
  348. {
  349. locked = false;
  350. crit.leave();
  351. }
  352. }
  353. };
  354. #ifdef SPINLOCK_USE_MUTEX // for testing
  355. class SpinLock
  356. {
  357. CriticalSection sect;
  358. public:
  359. inline void enter()
  360. {
  361. sect.enter();
  362. }
  363. inline void leave()
  364. {
  365. sect.leave();
  366. }
  367. };
  368. #else
  369. class jlib_decl SpinLock
  370. {
  371. std::atomic_uint value{false}; // Use an atomic_uint rather than a bool because it is more efficient on power8
  372. unsigned nesting = 0; // This is not atomic since it is only accessed by one thread at a time
  373. std::atomic<ThreadId> owner{0};
  374. inline SpinLock(SpinLock & value __attribute__((unused))) = delete; // to prevent inadvertent use as block
  375. public:
  376. inline SpinLock()
  377. {
  378. }
  379. #ifdef _DEBUG
  380. ~SpinLock()
  381. {
  382. if (value)
  383. printf("Warning - Owned Spinlock destroyed"); // can't use DBGLOG here!
  384. }
  385. #endif
  386. inline void enter()
  387. {
  388. ThreadId self = GetCurrentThreadId();
  389. #ifdef SPINLOCK_RR_CHECK // as requested by RKC
  390. int policy;
  391. sched_param param;
  392. if ((pthread_getschedparam(self, &policy, &param)==0)&&(policy==SCHED_RR)) {
  393. param.sched_priority = 0;
  394. pthread_setschedparam(self, SCHED_OTHER, &param); // otherwise will likely re-enter
  395. assertex(!"SpinLock enter on SCHED_RR thread");
  396. }
  397. #endif
  398. //owner can only match if it was set on this thread. Therefore the load can be relaxed since single threaded
  399. //code is always sequentially consistent.
  400. if (self==owner.load(std::memory_order_relaxed))
  401. {
  402. dbgassertex(value);
  403. nesting++;
  404. return;
  405. }
  406. while (unlikely(value.exchange(true, std::memory_order_acquire)))
  407. spinUntilReady(value);
  408. owner.store(self, std::memory_order_relaxed);
  409. }
  410. inline void leave()
  411. {
  412. //It is safe to access nesting - since this thread is the only one that can access
  413. //it, so no need for a synchronized access
  414. if (nesting == 0)
  415. {
  416. owner.store(0, std::memory_order_relaxed);
  417. value.store(false, std::memory_order_release);
  418. }
  419. else
  420. nesting--;
  421. }
  422. };
  423. #endif
  424. class SpinBlock
  425. {
  426. SpinLock &lock;
  427. public:
  428. inline SpinBlock(SpinLock & _lock) : lock(_lock) { lock.enter(); }
  429. inline ~SpinBlock() { lock.leave(); }
  430. };
  431. class SpinUnblock
  432. {
  433. SpinLock &lock;
  434. public:
  435. inline SpinUnblock(SpinLock & _lock) : lock(_lock) { lock.leave(); }
  436. inline ~SpinUnblock() { lock.enter(); }
  437. };
  438. // Non re-entrant Spin locks where *absolutely* certain enters are not nested on same thread
  439. // (debug version checks and asserts if are, release version will deadlock
  440. #ifdef NRESPINLOCK_USE_SPINLOCK
  441. class jlib_decl NonReentrantSpinLock: public SpinLock
  442. {
  443. };
  444. #else
  445. #ifdef _DEBUG
  446. class jlib_decl NonReentrantSpinLock
  447. {
  448. std::atomic_uint value;
  449. std::atomic<ThreadId> owner;
  450. inline NonReentrantSpinLock(NonReentrantSpinLock & value __attribute__((unused))) = delete; // to prevent inadvertent use as block
  451. public:
  452. inline NonReentrantSpinLock() : value(false), owner(0)
  453. {
  454. }
  455. inline void enter()
  456. {
  457. ThreadId self = GetCurrentThreadId();
  458. assertex(self!=owner.load(std::memory_order_relaxed)); // check for reentrancy
  459. while (unlikely(value.exchange(true, std::memory_order_acquire)))
  460. spinUntilReady(value);
  461. owner.store(self, std::memory_order_relaxed);
  462. }
  463. inline void leave()
  464. {
  465. assertex(GetCurrentThreadId()==owner.load(std::memory_order_relaxed)); // check for spurious leave
  466. owner.store(0, std::memory_order_relaxed);
  467. value.store(false, std::memory_order_release);
  468. }
  469. };
  470. #else
  471. class jlib_decl NonReentrantSpinLock
  472. {
  473. std::atomic_uint value;
  474. inline NonReentrantSpinLock(NonReentrantSpinLock & value __attribute__((unused))) = delete; // to prevent inadvertent use as block
  475. public:
  476. inline NonReentrantSpinLock() : value(false)
  477. {
  478. }
  479. inline void enter()
  480. {
  481. while (unlikely(value.exchange(true, std::memory_order_acquire)))
  482. spinUntilReady(value);
  483. }
  484. inline void leave()
  485. {
  486. value.store(false, std::memory_order_release);
  487. }
  488. };
  489. #endif
  490. #endif
  491. class NonReentrantSpinBlock
  492. {
  493. NonReentrantSpinLock &lock;
  494. public:
  495. inline NonReentrantSpinBlock(NonReentrantSpinLock & _lock) : lock(_lock) { lock.enter(); }
  496. inline ~NonReentrantSpinBlock() { lock.leave(); }
  497. };
  498. class NonReentrantSpinUnblock
  499. {
  500. NonReentrantSpinLock &lock;
  501. public:
  502. inline NonReentrantSpinUnblock(NonReentrantSpinLock & _lock) : lock(_lock) { lock.leave(); }
  503. inline ~NonReentrantSpinUnblock() { lock.enter(); }
  504. };
  505. class jlib_decl Monitor: public Mutex
  506. {
  507. // Like a java object - you can synchronize on it for a block, wait for a notify on it, or notify on it
  508. Semaphore *sem;
  509. int waiting;
  510. void *last;
  511. public:
  512. Monitor() : Mutex() { sem = new Semaphore(); waiting = 0; last = NULL; }
  513. // Monitor(const char *name) : Mutex(name) { sem = new Semaphore(name); waiting = 0; last = NULL; } // not supported
  514. ~Monitor() {delete sem;};
  515. void wait(); // only called when locked
  516. void notify(); // only called when locked
  517. void notifyAll(); // only called when locked -- notifys for all waiting threads
  518. };
  519. //--------------------------------------------------------------------------------------------------------------------
  520. //Currently disabled since performance profile of own implementation is preferable, and queryWriteLocked() cannot be implemented
  521. //#define USE_PTHREAD_RWLOCK
  522. #ifndef USE_PTHREAD_RWLOCK
  523. class jlib_decl ReadWriteLock
  524. {
  525. bool lockRead(bool timed, unsigned timeout) {
  526. cs.enter();
  527. if (writeLocks == 0)
  528. {
  529. readLocks++;
  530. cs.leave();
  531. }
  532. else
  533. {
  534. readWaiting++;
  535. cs.leave();
  536. if (timed)
  537. {
  538. if (!readSem.wait(timeout)) {
  539. cs.enter();
  540. if (!readSem.wait(0)) {
  541. readWaiting--;
  542. cs.leave();
  543. return false;
  544. }
  545. cs.leave();
  546. }
  547. }
  548. else
  549. readSem.wait();
  550. //NB: waiting and locks adjusted before the signal occurs.
  551. }
  552. return true;
  553. }
  554. bool lockWrite(bool timed, unsigned timeout) {
  555. cs.enter();
  556. if ((readLocks == 0) && (writeLocks == 0))
  557. {
  558. writeLocks++;
  559. cs.leave();
  560. }
  561. else
  562. {
  563. writeWaiting++;
  564. cs.leave();
  565. if (timed)
  566. {
  567. if (!writeSem.wait(timeout)) {
  568. cs.enter();
  569. if (!writeSem.wait(0)) {
  570. writeWaiting--;
  571. cs.leave();
  572. return false;
  573. }
  574. cs.leave();
  575. }
  576. }
  577. else
  578. writeSem.wait();
  579. //NB: waiting and locks adjusted before the signal occurs.
  580. }
  581. #ifdef _DEBUG
  582. exclWriteOwner = GetCurrentThreadId();
  583. #endif
  584. return true;
  585. }
  586. public:
  587. ReadWriteLock()
  588. {
  589. readLocks = 0; writeLocks = 0; readWaiting = 0; writeWaiting = 0;
  590. #ifdef _DEBUG
  591. exclWriteOwner = 0;
  592. #endif
  593. }
  594. ~ReadWriteLock() { assertex(readLocks == 0 && writeLocks == 0); }
  595. void lockRead() { lockRead(false, 0); }
  596. void lockWrite() { lockWrite(false, 0); }
  597. bool lockRead(unsigned timeout) { return lockRead(true, timeout); }
  598. bool lockWrite(unsigned timeout) { return lockWrite(true, timeout); }
  599. unsigned queryReadLockCount() const { return readLocks; }
  600. void unlock() {
  601. cs.enter();
  602. if (readLocks) readLocks--;
  603. else
  604. {
  605. writeLocks--;
  606. #ifdef _DEBUG
  607. exclWriteOwner = 0;
  608. #endif
  609. }
  610. assertex(writeLocks == 0);
  611. if (readLocks == 0)
  612. {
  613. if (readWaiting)
  614. {
  615. unsigned numWaiting = readWaiting;
  616. readWaiting = 0;
  617. readLocks += numWaiting;
  618. readSem.signal(numWaiting);
  619. }
  620. else if (writeWaiting)
  621. {
  622. writeWaiting--;
  623. writeLocks++;
  624. writeSem.signal();
  625. }
  626. }
  627. cs.leave();
  628. }
  629. bool queryWriteLocked() { return (writeLocks != 0); }
  630. void unlockRead() { unlock(); }
  631. void unlockWrite() { unlock(); }
  632. //MORE: May want to use the pthread implementations under linux.
  633. protected:
  634. CriticalSection cs;
  635. Semaphore readSem;
  636. Semaphore writeSem;
  637. unsigned readLocks;
  638. unsigned writeLocks;
  639. unsigned readWaiting;
  640. unsigned writeWaiting;
  641. #ifdef _DEBUG
  642. ThreadId exclWriteOwner;
  643. #endif
  644. };
  645. #else
  646. class jlib_decl ReadWriteLock
  647. {
  648. public:
  649. ReadWriteLock() { pthread_rwlock_init(&rwlock, nullptr); }
  650. ~ReadWriteLock() { pthread_rwlock_destroy(&rwlock); }
  651. void lockRead() { pthread_rwlock_rdlock(&rwlock); }
  652. void lockWrite() { pthread_rwlock_wrlock(&rwlock); }
  653. bool lockRead(unsigned timeout);
  654. bool lockWrite(unsigned timeout);
  655. void unlock() { pthread_rwlock_unlock(&rwlock); }
  656. void unlockRead() { pthread_rwlock_unlock(&rwlock); }
  657. void unlockWrite() { pthread_rwlock_unlock(&rwlock); }
  658. // bool queryWriteLocked(); // I don't think this can be implemented on top of the pthread interface
  659. protected:
  660. pthread_rwlock_t rwlock;
  661. };
  662. #endif
  663. class ReadLockBlock
  664. {
  665. ReadWriteLock *lock;
  666. public:
  667. ReadLockBlock(ReadWriteLock &l) : lock(&l) { lock->lockRead(); }
  668. ~ReadLockBlock() { if (lock) lock->unlockRead(); }
  669. void clear()
  670. {
  671. if (lock)
  672. {
  673. lock->unlockRead();
  674. lock = NULL;
  675. }
  676. }
  677. };
  678. class WriteLockBlock
  679. {
  680. ReadWriteLock *lock;
  681. public:
  682. WriteLockBlock(ReadWriteLock &l) : lock(&l) { lock->lockWrite(); }
  683. ~WriteLockBlock() { if (lock) lock->unlockWrite(); }
  684. void clear()
  685. {
  686. if (lock)
  687. {
  688. lock->unlockWrite();
  689. lock = NULL;
  690. }
  691. }
  692. };
  693. //--------------------------------------------------------------------------------------------------------------------
  694. class Barrier
  695. {
  696. CriticalSection crit;
  697. int limit, remaining, waiting;
  698. Semaphore sem;
  699. public:
  700. Barrier(int _limit) { init(_limit); }
  701. Barrier() { init(0); }
  702. void init(int _limit)
  703. {
  704. waiting = 0;
  705. limit = _limit;
  706. remaining = limit;
  707. }
  708. void wait() // blocks until 'limit' barrier points are entered.
  709. {
  710. CriticalBlock block(crit);
  711. while (remaining==0) {
  712. if (waiting) {
  713. crit.leave();
  714. ThreadYield();
  715. crit.enter();
  716. }
  717. else
  718. remaining = limit;
  719. }
  720. remaining--;
  721. if (remaining==0)
  722. sem.signal(waiting);
  723. else if (remaining>0) {
  724. waiting++;
  725. crit.leave();
  726. sem.wait();
  727. crit.enter();
  728. waiting--;
  729. }
  730. }
  731. void abort()
  732. {
  733. CriticalBlock block(crit);
  734. remaining = -1;
  735. sem.signal(waiting);
  736. }
  737. void cancel(int n, bool remove) // cancel n barrier points from this instance, if remove=true reduces barrier width
  738. {
  739. CriticalBlock block(crit);
  740. while (remaining==0) {
  741. if (waiting) {
  742. crit.leave();
  743. ThreadYield();
  744. crit.enter();
  745. }
  746. else
  747. remaining = limit;
  748. }
  749. assertex(remaining>=n);
  750. remaining-=n;
  751. if (remaining==0)
  752. sem.signal(waiting);
  753. if (remove)
  754. limit-=n;
  755. }
  756. };
  757. // checked versions of critical block and readwrite blocks - report deadlocks
  758. #define USECHECKEDCRITICALSECTIONS
  759. #ifdef USECHECKEDCRITICALSECTIONS
  760. typedef Mutex CheckedCriticalSection;
  761. void jlib_decl checkedCritEnter(CheckedCriticalSection &crit, unsigned timeout, const char *fname, unsigned lnum);
  762. void jlib_decl checkedCritLeave(CheckedCriticalSection &crit);
  763. class jlib_decl CheckedCriticalBlock
  764. {
  765. CheckedCriticalSection &crit;
  766. public:
  767. CheckedCriticalBlock(CheckedCriticalSection &c, unsigned timeout, const char *fname,unsigned lnum);
  768. ~CheckedCriticalBlock()
  769. {
  770. crit.unlock();
  771. }
  772. };
  773. class jlib_decl CheckedCriticalUnblock
  774. {
  775. CheckedCriticalSection &crit;
  776. const char *fname;
  777. unsigned lnum;
  778. unsigned timeout;
  779. public:
  780. CheckedCriticalUnblock(CheckedCriticalSection &c,unsigned _timeout,const char *_fname,unsigned _lnum)
  781. : crit(c)
  782. {
  783. timeout = _timeout;
  784. fname = _fname;
  785. lnum = _lnum;
  786. crit.unlock();
  787. }
  788. ~CheckedCriticalUnblock();
  789. };
  790. #define CHECKEDCRITICALBLOCK(sect,timeout) CheckedCriticalBlock glue(block,__LINE__)(sect,timeout,__FILE__,__LINE__)
  791. #define CHECKEDCRITICALUNBLOCK(sect,timeout) CheckedCriticalUnblock glue(unblock,__LINE__)(sect,timeout,__FILE__,__LINE__)
  792. #define CHECKEDCRITENTER(sect,timeout) checkedCritEnter(sect,timeout,__FILE__,__LINE__)
  793. #define CHECKEDCRITLEAVE(sect) checkedCritLeave(sect)
  794. class jlib_decl CheckedReadLockBlock
  795. {
  796. ReadWriteLock &lock;
  797. public:
  798. CheckedReadLockBlock(ReadWriteLock &l, unsigned timeout, const char *fname,unsigned lnum);
  799. ~CheckedReadLockBlock() { lock.unlockRead(); }
  800. };
  801. class jlib_decl CheckedWriteLockBlock
  802. {
  803. ReadWriteLock &lock;
  804. public:
  805. CheckedWriteLockBlock(ReadWriteLock &l, unsigned timeout, const char *fname, unsigned lnum);
  806. ~CheckedWriteLockBlock() { lock.unlockWrite(); }
  807. };
  808. void jlib_decl checkedReadLockEnter(ReadWriteLock &l, unsigned timeout, const char *fname, unsigned lnum);
  809. void jlib_decl checkedWriteLockEnter(ReadWriteLock &l, unsigned timeout, const char *fname, unsigned lnum);
  810. #define CHECKEDREADLOCKBLOCK(l,timeout) CheckedReadLockBlock glue(block,__LINE__)(l,timeout,__FILE__,__LINE__)
  811. #define CHECKEDWRITELOCKBLOCK(l,timeout) CheckedWriteLockBlock glue(block,__LINE__)(l,timeout,__FILE__,__LINE__)
  812. #define CHECKEDREADLOCKENTER(l,timeout) checkedReadLockEnter(l,timeout,__FILE__,__LINE__)
  813. #define CHECKEDWRITELOCKENTER(l,timeout) checkedWriteLockEnter(l,timeout,__FILE__,__LINE__)
  814. #else
  815. #define CheckedCriticalSection CriticalSection
  816. #define CheckedCriticalBlock CriticalBlock
  817. #define CheckedCriticalUnblock CriticalUnblock
  818. #define CHECKEDCRITENTER(sect,timeout) (sect).enter()
  819. #define CHECKEDCRITLEAVE(sect) (sect).leave()
  820. #define CHECKEDCRITICALBLOCK(sect,timeout) CheckedCriticalBlock glue(block,__LINE__)(sect)
  821. #define CHECKEDCRITICALUNBLOCK(sect,timeout) CheckedCriticalUnblock glue(unblock,__LINE__)(sect)
  822. #define CHECKEDREADLOCKBLOCK(l,timeout) ReadLockBlock glue(block,__LINE__)(l)
  823. #define CHECKEDWRITELOCKBLOCK(l,timeout) WriteLockBlock glue(block,__LINE__)(l)
  824. #define CHECKEDREADLOCKENTER(l,timeout) (l).lockRead()
  825. #define CHECKEDWRITELOCKENTER(l,timeout) (l).lockWrite()
  826. #endif
  827. class CSingletonLock // a lock that will generally only be locked once (for locking singleton objects - see below for examples
  828. {
  829. volatile bool needlock;
  830. CriticalSection sect;
  831. public:
  832. inline CSingletonLock()
  833. {
  834. needlock = true;
  835. }
  836. inline bool lock()
  837. {
  838. if (needlock) {
  839. sect.enter();
  840. //prevent compiler from moving any code before the critical section (unlikely)
  841. compiler_memory_barrier();
  842. return true;
  843. }
  844. //Prevent the value of the protected object from being evaluated before the condition
  845. compiler_memory_barrier();
  846. return false;
  847. }
  848. inline void unlock()
  849. {
  850. //Ensure that no code that precedes unlock() gets moved to after needlock being cleared.
  851. compiler_memory_barrier();
  852. needlock = false;
  853. sect.leave();
  854. }
  855. };
  856. /* Usage example
  857. static void *sobj = NULL;
  858. static CSingletonLock slock;
  859. void *get()
  860. {
  861. if (slock.lock()) {
  862. if (!sobj) // required
  863. sobj = createSObj();
  864. slock.unlock();
  865. }
  866. return sobj;
  867. }
  868. */
  869. /*
  870. * A template function for implementing a singleton object. Using the same example as above would require:
  871. static std::atomic<void *> sobj;
  872. static CriticalSection slock;
  873. void *get()
  874. {
  875. return querySingleton(sobj, slock, []{ return createSObj; });
  876. }
  877. */
  878. template <typename X, typename FUNC>
  879. inline X * querySingleton(std::atomic<X *> & singleton, CriticalSection & cs, FUNC factory)
  880. {
  881. X * value = singleton.load(std::memory_order_acquire);
  882. if (value)
  883. return value; // avoid crit
  884. CriticalBlock block(cs);
  885. value = singleton.load(std::memory_order_acquire); // reload in case another thread got here first
  886. if (!value)
  887. {
  888. value = factory();
  889. singleton.store(value, std::memory_order_release);
  890. }
  891. return value;
  892. }
  893. /*
  894. * A template class for implementing a singleton object. Using the same example as above would require:
  895. static Singleton<void> sobj;
  896. void *get()
  897. {
  898. return sobj.query([]{ return createSObj; });
  899. }
  900. */
  901. template <typename X>
  902. class Singleton
  903. {
  904. public:
  905. template <typename FUNC> X * query(FUNC factory) { return querySingleton(singleton, cs, factory); }
  906. X * queryExisting() const { return singleton.load(std::memory_order_acquire); }
  907. private:
  908. std::atomic<X *> singleton = {nullptr};
  909. CriticalSection cs;
  910. };
  911. #endif