You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

505 lines
16 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: winstl/synch/semaphore.hpp
  3. *
  4. * Purpose: Semaphore class, based on Win32 kernel semaphore object.
  5. *
  6. * Created: 30th May 2006
  7. * Updated: 11th September 2011
  8. *
  9. * Home: http://stlsoft.org/
  10. *
  11. * Copyright (c) 2006-2011, Matthew Wilson and Synesis Software
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or without
  15. * modification, are permitted provided that the following conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright notice, this
  18. * list of conditions and the following disclaimer.
  19. * - Redistributions in binary form must reproduce the above copyright notice,
  20. * this list of conditions and the following disclaimer in the documentation
  21. * and/or other materials provided with the distribution.
  22. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
  23. * any contributors may be used to endorse or promote products derived from
  24. * this software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36. * POSSIBILITY OF SUCH DAMAGE.
  37. *
  38. * ////////////////////////////////////////////////////////////////////// */
  39. /** \file winstl/synch/semaphore.hpp
  40. *
  41. * \brief [C++ only] Definition of winstl::semaphore class
  42. * (\ref group__library__synch "Synchronisation" Library).
  43. */
  44. #ifndef WINSTL_INCL_WINSTL_SYNCH_HPP_SEMAPHORE
  45. #define WINSTL_INCL_WINSTL_SYNCH_HPP_SEMAPHORE
  46. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  47. # define WINSTL_VER_WINSTL_SYNCH_HPP_SEMAPHORE_MAJOR 1
  48. # define WINSTL_VER_WINSTL_SYNCH_HPP_SEMAPHORE_MINOR 3
  49. # define WINSTL_VER_WINSTL_SYNCH_HPP_SEMAPHORE_REVISION 2
  50. # define WINSTL_VER_WINSTL_SYNCH_HPP_SEMAPHORE_EDIT 23
  51. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  52. /* /////////////////////////////////////////////////////////////////////////
  53. * Includes
  54. */
  55. #ifndef WINSTL_INCL_WINSTL_H_WINSTL
  56. # include <winstl/winstl.h>
  57. #endif /* !WINSTL_INCL_WINSTL_H_WINSTL */
  58. #ifndef STLSOFT_INCL_STLSOFT_SYNCH_HPP_CONCEPTS
  59. # include <stlsoft/synch/concepts.hpp>
  60. #endif /* !STLSOFT_INCL_STLSOFT_SYNCH_HPP_CONCEPTS */
  61. #ifndef WINSTL_INCL_WINSTL_SYNCH_ERROR_HPP_EXCEPTIONS
  62. # include <winstl/synch/error/exceptions.hpp>
  63. #endif /* !WINSTL_INCL_WINSTL_SYNCH_ERROR_HPP_EXCEPTIONS */
  64. /* /////////////////////////////////////////////////////////////////////////
  65. * Namespace
  66. */
  67. #ifndef _WINSTL_NO_NAMESPACE
  68. # if defined(_STLSOFT_NO_NAMESPACE) || \
  69. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  70. /* There is no stlsoft namespace, so must define ::winstl */
  71. namespace winstl
  72. {
  73. # else
  74. /* Define stlsoft::winstl_project */
  75. namespace stlsoft
  76. {
  77. namespace winstl_project
  78. {
  79. # endif /* _STLSOFT_NO_NAMESPACE */
  80. #endif /* !_WINSTL_NO_NAMESPACE */
  81. /* /////////////////////////////////////////////////////////////////////////
  82. * Classes
  83. */
  84. // class semaphore
  85. /** This class acts as an semaphore based on the Win32
  86. * kernel semaphore object
  87. *
  88. * \ingroup group__library__synch
  89. */
  90. class semaphore
  91. : public stlsoft_ns_qual(critical_section)< STLSOFT_CRITICAL_SECTION_ISNOT_RECURSIVE
  92. , STLSOFT_CRITICAL_SECTION_IS_TRYABLE
  93. >
  94. , public stlsoft_ns_qual(synchronisable_object_tag)
  95. {
  96. public:
  97. /// This type
  98. typedef semaphore class_type;
  99. /// The synchronisation handle type
  100. typedef HANDLE synch_handle_type;
  101. /// The Boolean type
  102. typedef ws_bool_t bool_type;
  103. /// The count type
  104. typedef ws_size_t count_type;
  105. /// The resource type
  106. typedef HANDLE resource_type;
  107. private:
  108. #if 0
  109. typedef LONG underlying_count_type_;
  110. #else /* ? 0 */
  111. typedef count_type underlying_count_type_;
  112. #endif /* 0 */
  113. public:
  114. enum
  115. {
  116. maxCountValue = 0x7fffffff ///!< Borrowed from PThreads-win32
  117. };
  118. /// \name Construction
  119. /// @{
  120. public:
  121. /// Conversion constructor
  122. semaphore(synch_handle_type sem, bool_type bTakeOwnership)
  123. : m_sem(sem)
  124. , m_maxCount(0)
  125. , m_bOwnHandle(bTakeOwnership)
  126. {
  127. WINSTL_ASSERT(NULL != sem);
  128. }
  129. /// Creates an instance of the semaphore
  130. ss_explicit_k semaphore(count_type initialCount, count_type maxCount = maxCountValue)
  131. : m_sem(create_semaphore_(NULL, initialCount, maxCount, static_cast<ws_char_a_t const*>(NULL)))
  132. , m_maxCount(maxCount)
  133. , m_bOwnHandle(true)
  134. {}
  135. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  136. // This disambiguates cases where the initial count is 0
  137. ss_explicit_k semaphore(int initialCount, count_type maxCount = maxCountValue)
  138. : m_sem(create_semaphore_(NULL, static_cast<count_type>(initialCount), maxCount, static_cast<ws_char_a_t const*>(NULL)))
  139. , m_maxCount(maxCount)
  140. , m_bOwnHandle(true)
  141. {}
  142. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  143. /// Creates an instance of the semaphore
  144. ss_explicit_k semaphore(ws_char_a_t const* name, count_type initialCount, count_type maxCount = maxCountValue)
  145. : m_sem(create_semaphore_(NULL, initialCount, maxCount, name))
  146. , m_maxCount(maxCount)
  147. , m_bOwnHandle(true)
  148. {}
  149. /// Creates an instance of the semaphore
  150. ss_explicit_k semaphore(ws_char_w_t const* name, count_type initialCount, count_type maxCount = maxCountValue)
  151. : m_sem(create_semaphore_(NULL, initialCount, maxCount, name))
  152. , m_maxCount(maxCount)
  153. , m_bOwnHandle(true)
  154. {}
  155. /// Creates an instance of the semaphore
  156. ss_explicit_k semaphore(ws_char_a_t const* name, LPSECURITY_ATTRIBUTES psa, count_type initialCount, count_type maxCount = maxCountValue)
  157. : m_sem(create_semaphore_(psa, initialCount, maxCount, name))
  158. , m_maxCount(maxCount)
  159. , m_bOwnHandle(true)
  160. {}
  161. /// Creates an instance of the semaphore
  162. ss_explicit_k semaphore(ws_char_w_t const* name, LPSECURITY_ATTRIBUTES psa, count_type initialCount, count_type maxCount = maxCountValue)
  163. : m_sem(create_semaphore_(psa, initialCount, maxCount, name))
  164. , m_maxCount(maxCount)
  165. , m_bOwnHandle(true)
  166. {}
  167. /// Destroys an instance of the semaphore
  168. ~semaphore() stlsoft_throw_0()
  169. {
  170. if( NULL != m_sem &&
  171. m_bOwnHandle)
  172. {
  173. ::CloseHandle(m_sem);
  174. }
  175. }
  176. /// @}
  177. /// \name Operations
  178. /// @{
  179. public:
  180. /// Acquires a lock on the semaphore, pending the thread until the lock is aquired
  181. void lock()
  182. {
  183. WINSTL_ASSERT(NULL != m_sem);
  184. DWORD dwRes = ::WaitForSingleObject(m_sem, INFINITE);
  185. if(WAIT_OBJECT_0 != dwRes)
  186. {
  187. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  188. STLSOFT_THROW_X(synchronisation_exception("semaphore wait failed", ::GetLastError()));
  189. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  190. }
  191. }
  192. /// Acquires a lock on the semaphore, pending the thread until the lock is aquired
  193. bool_type lock(ws_dword_t wait)
  194. {
  195. WINSTL_ASSERT(NULL != m_sem);
  196. DWORD dwRes = ::WaitForSingleObject(m_sem, wait);
  197. if( WAIT_OBJECT_0 != dwRes &&
  198. WAIT_TIMEOUT != dwRes)
  199. {
  200. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  201. STLSOFT_THROW_X(synchronisation_exception("semaphore wait failed", ::GetLastError()));
  202. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  203. }
  204. return (dwRes == WAIT_OBJECT_0);
  205. }
  206. /// Attempts to lock the semaphore
  207. ///
  208. /// \return <b>true</b> if the semaphore was aquired, or <b>false</b> if not
  209. bool_type try_lock()
  210. {
  211. return lock(0);
  212. }
  213. /// Releases an aquired lock on the semaphore, increasing the
  214. /// semaphore's counter by one.
  215. ///
  216. /// \note Equivalent to \link winstl::semaphore::unlock(count_type) unlock()\endlink.
  217. void unlock()
  218. {
  219. WINSTL_ASSERT(NULL != m_sem);
  220. if(!::ReleaseSemaphore(m_sem, 1, NULL))
  221. {
  222. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  223. STLSOFT_THROW_X(synchronisation_exception("semaphore release failed", ::GetLastError()));
  224. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  225. }
  226. }
  227. /// Releases a number of aquired "locks" on the semaphore,
  228. /// increasing the semaphore's counter by the given value.
  229. ///
  230. /// \param numLocksToRelease The number by which to increment the
  231. /// semaphore's counter. If this is greater than the available
  232. /// value, the function fails. (It will throw an exception, if
  233. /// exception handling is enabled, or return -1 otherwise.)
  234. ///
  235. /// \return Returns the value of the semaphore's counter prior to the
  236. /// change effected by the call.
  237. /// \retval -1 Indicates call failure (only if exception handling is not
  238. /// enabled).
  239. ws_long_t unlock(count_type numLocksToRelease)
  240. {
  241. WINSTL_ASSERT(NULL != m_sem);
  242. WINSTL_ASSERT(numLocksToRelease > 0);
  243. WINSTL_ASSERT(static_cast<LONG>(numLocksToRelease) > 0);
  244. LONG previousCount = 0;
  245. if(!::ReleaseSemaphore(m_sem, static_cast<LONG>(numLocksToRelease), &previousCount))
  246. {
  247. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  248. STLSOFT_THROW_X(synchronisation_exception("semaphore release failed", ::GetLastError()));
  249. #else /* ? STLSOFT_CF_EXCEPTION_SUPPORT */
  250. return -1;
  251. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  252. }
  253. return static_cast<ws_long_t>(previousCount);
  254. }
  255. /// @}
  256. /// \name Accessors
  257. /// @{
  258. public:
  259. /// The underlying kernel object handle
  260. synch_handle_type handle()
  261. {
  262. return m_sem;
  263. }
  264. /// The underlying kernel object handle
  265. synch_handle_type get()
  266. {
  267. return m_sem;
  268. }
  269. /// @}
  270. // Implementation
  271. private:
  272. static HANDLE CreateSemaphore_A_arguments_adjusted_(
  273. LPSECURITY_ATTRIBUTES psa
  274. , underlying_count_type_ initialCount
  275. , underlying_count_type_ maxCount
  276. , ws_char_a_t const* name
  277. )
  278. {
  279. return STLSOFT_NS_GLOBAL(CreateSemaphoreA)(psa, static_cast<LONG>(initialCount), static_cast<LONG>(maxCount), name);
  280. }
  281. static HANDLE CreateSemaphore_W_arguments_adjusted_(
  282. LPSECURITY_ATTRIBUTES psa
  283. , underlying_count_type_ initialCount
  284. , underlying_count_type_ maxCount
  285. , ws_char_w_t const* name
  286. )
  287. {
  288. return STLSOFT_NS_GLOBAL(CreateSemaphoreW)(psa, static_cast<LONG>(initialCount), static_cast<LONG>(maxCount), name);
  289. }
  290. static synch_handle_type create_semaphore_(
  291. LPSECURITY_ATTRIBUTES psa
  292. , underlying_count_type_ initialCount
  293. , underlying_count_type_ maxCount
  294. , ws_char_a_t const* name
  295. )
  296. {
  297. WINSTL_MESSAGE_ASSERT("Maximum semaphore count must be > 0", maxCount > 0);
  298. WINSTL_ASSERT(initialCount <= maxCount);
  299. synch_handle_type sem = CreateSemaphore_A_arguments_adjusted_(psa, initialCount, maxCount, name);
  300. if(NULL == sem)
  301. {
  302. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  303. STLSOFT_THROW_X(synchronisation_exception("Failed to create kernel semaphore object", ::GetLastError()));
  304. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  305. }
  306. return sem;
  307. }
  308. static synch_handle_type create_semaphore_(
  309. LPSECURITY_ATTRIBUTES psa
  310. , underlying_count_type_ initialCount
  311. , underlying_count_type_ maxCount
  312. , ws_char_w_t const* name
  313. )
  314. {
  315. WINSTL_MESSAGE_ASSERT("Maximum semaphore count must be > 0", maxCount > 0);
  316. WINSTL_ASSERT(initialCount <= maxCount);
  317. synch_handle_type sem = CreateSemaphore_W_arguments_adjusted_(psa, initialCount, maxCount, name);
  318. if(NULL == sem)
  319. {
  320. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  321. STLSOFT_THROW_X(synchronisation_exception("Failed to create kernel semaphore object", ::GetLastError()));
  322. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  323. }
  324. return sem;
  325. }
  326. // Members
  327. private:
  328. synch_handle_type m_sem; // The underlying semaphore object
  329. const count_type m_maxCount; // Record of the maximum counter value
  330. const bool_type m_bOwnHandle; // Does the instance own the handle?
  331. // Not to be implemented
  332. private:
  333. semaphore(class_type const& rhs);
  334. semaphore& operator =(class_type const& rhs);
  335. };
  336. /* /////////////////////////////////////////////////////////////////////////
  337. * Shims
  338. */
  339. /** Overload of the form of the winstl::get_synch_handle() shim for
  340. * the winstl::semaphore type.
  341. *
  342. * \param sem The winstl::semaphore instance
  343. *
  344. * \retval The synchronisation handle of \c sem
  345. */
  346. inline HANDLE get_synch_handle(semaphore &sem)
  347. {
  348. return sem.get();
  349. }
  350. /** Overload of the form of the winstl::get_kernel_handle() shim for
  351. * the winstl::semaphore type.
  352. *
  353. * \ingroup group__library__shims__kernel_handle_attribute
  354. *
  355. * \param sem The winstl::semaphore instance
  356. *
  357. * \retval The synchronisation handle of \c sem
  358. */
  359. inline HANDLE get_kernel_handle(semaphore &sem)
  360. {
  361. return sem.get();
  362. }
  363. #ifndef _WINSTL_NO_NAMESPACE
  364. # if defined(_STLSOFT_NO_NAMESPACE) || \
  365. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  366. } // namespace winstl
  367. # else
  368. } // namespace winstl_project
  369. # endif /* _STLSOFT_NO_NAMESPACE */
  370. #endif /* !_WINSTL_NO_NAMESPACE */
  371. /** This \ref group__concept__shims "control shim" aquires a lock on the given semaphore
  372. *
  373. * \ingroup group__concept__shim__synchronisation_control
  374. *
  375. * \param sem The semaphore on which to aquire the lock.
  376. */
  377. inline void lock_instance(winstl_ns_qual(semaphore) &sem)
  378. {
  379. sem.lock();
  380. }
  381. /** This \ref group__concept__shims "control shim" releases a lock on the given semaphore
  382. *
  383. * \ingroup group__concept__shim__synchronisation_control
  384. *
  385. * \param sem The semaphore on which to release the lock
  386. */
  387. inline void unlock_instance(winstl_ns_qual(semaphore) &sem)
  388. {
  389. sem.unlock();
  390. }
  391. #ifndef _WINSTL_NO_NAMESPACE
  392. # if defined(_STLSOFT_NO_NAMESPACE) || \
  393. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  394. namespace winstl {
  395. # else
  396. namespace winstl_project {
  397. # if defined(STLSOFT_COMPILER_IS_BORLAND)
  398. using ::stlsoft::lock_instance;
  399. using ::stlsoft::unlock_instance;
  400. # endif /* compiler */
  401. # endif /* _STLSOFT_NO_NAMESPACE */
  402. #endif /* !_WINSTL_NO_NAMESPACE */
  403. /* /////////////////////////////////////////////////////////////////////////
  404. * lock_traits
  405. */
  406. // class lock_traits
  407. /** Traits for the semaphore class
  408. *
  409. * \ingroup group__library__synch
  410. */
  411. struct semaphore_lock_traits
  412. {
  413. public:
  414. /// The lockable type
  415. typedef semaphore lock_type;
  416. /// This type
  417. typedef semaphore_lock_traits class_type;
  418. // Operations
  419. public:
  420. /// Lock the given semaphore instance
  421. static void lock(semaphore &c)
  422. {
  423. lock_instance(c);
  424. }
  425. /// Unlock the given semaphore instance
  426. static void unlock(semaphore &c)
  427. {
  428. unlock_instance(c);
  429. }
  430. };
  431. ////////////////////////////////////////////////////////////////////////////
  432. // Unit-testing
  433. #ifdef STLSOFT_UNITTEST
  434. # include "./unittest/semaphore_unittest_.h"
  435. #endif /* STLSOFT_UNITTEST */
  436. /* ////////////////////////////////////////////////////////////////////// */
  437. #ifndef _WINSTL_NO_NAMESPACE
  438. # if defined(_STLSOFT_NO_NAMESPACE) || \
  439. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  440. } // namespace winstl
  441. # else
  442. } // namespace winstl_project
  443. } // namespace stlsoft
  444. # endif /* _STLSOFT_NO_NAMESPACE */
  445. #endif /* !_WINSTL_NO_NAMESPACE */
  446. /* ////////////////////////////////////////////////////////////////////// */
  447. #endif /* !WINSTL_INCL_WINSTL_SYNCH_HPP_SEMAPHORE */
  448. /* ///////////////////////////// end of file //////////////////////////// */