jmutex.hpp 26 KB

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