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.

431 lines
13 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: unixstl/synch/thread_mutex.hpp
  3. *
  4. * Purpose: Intra-process mutex, based on PTHREADS pthread_mutex_t.
  5. *
  6. * Created: 17th December 1996
  7. * Updated: 10th August 2009
  8. *
  9. * Home: http://stlsoft.org/
  10. *
  11. * Copyright (c) 1996-2009, 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 unixstl/synch/thread_mutex.hpp
  40. *
  41. * \brief [C++ only] Definition of the unixstl::thread_mutex class
  42. * (\ref group__library__synch "Synchronisation" Library).
  43. */
  44. #ifndef UNIXSTL_INCL_UNIXSTL_SYNCH_HPP_THREAD_MUTEX
  45. #define UNIXSTL_INCL_UNIXSTL_SYNCH_HPP_THREAD_MUTEX
  46. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  47. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_THREAD_MUTEX_MAJOR 4
  48. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_THREAD_MUTEX_MINOR 3
  49. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_THREAD_MUTEX_REVISION 3
  50. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_THREAD_MUTEX_EDIT 56
  51. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  52. /* /////////////////////////////////////////////////////////////////////////
  53. * Includes
  54. */
  55. #ifndef UNIXSTL_INCL_UNIXSTL_H_UNIXSTL
  56. # include <unixstl/unixstl.h>
  57. #endif /* !UNIXSTL_INCL_UNIXSTL_H_UNIXSTL */
  58. #ifndef UNIXSTL_INCL_UNIXSTL_SYNCH_UTIL_H_FEATURES
  59. # include <unixstl/synch/util/features.h>
  60. #endif /* !UNIXSTL_INCL_UNIXSTL_SYNCH_UTIL_H_FEATURES */
  61. #ifndef UNIXSTL_USING_PTHREADS
  62. # error unixstl/synch/thread_mutex.hpp cannot be included in non-multithreaded compilation. _REENTRANT and/or _POSIX_THREADS must be defined
  63. #endif /* !UNIXSTL_USING_PTHREADS */
  64. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  65. # ifndef UNIXSTL_INCL_UNIXSTL_SYNCH_ERROR_HPP_EXCEPTIONS
  66. # include <unixstl/synch/error/exceptions.hpp>
  67. # endif /* !UNIXSTL_INCL_UNIXSTL_SYNCH_ERROR_HPP_EXCEPTIONS */
  68. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  69. #ifndef STLSOFT_INCL_STLSOFT_SMARTPTR_HPP_SCOPED_HANDLE
  70. # include <stlsoft/smartptr/scoped_handle.hpp>
  71. #endif /* !STLSOFT_INCL_STLSOFT_SMARTPTR_HPP_SCOPED_HANDLE */
  72. #ifndef STLSOFT_INCL_STLSOFT_SYNCH_HPP_CONCEPTS
  73. # include <stlsoft/synch/concepts.hpp>
  74. #endif /* !STLSOFT_INCL_STLSOFT_SYNCH_HPP_CONCEPTS */
  75. #ifndef STLSOFT_INCL_H_ERRNO
  76. # define STLSOFT_INCL_H_ERRNO
  77. # include <errno.h>
  78. #endif /* !STLSOFT_INCL_H_ERRNO */
  79. #ifndef STLSOFT_INCL_H_PTHREAD
  80. # define STLSOFT_INCL_H_PTHREAD
  81. # include <pthread.h>
  82. #endif /* !STLSOFT_INCL_H_PTHREAD */
  83. #ifdef STLSOFT_UNITTEST
  84. # include <stdio.h>
  85. #endif /* STLSOFT_UNITTEST */
  86. /* /////////////////////////////////////////////////////////////////////////
  87. * Namespace
  88. */
  89. #ifndef _UNIXSTL_NO_NAMESPACE
  90. # if defined(_STLSOFT_NO_NAMESPACE) || \
  91. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  92. /* There is no stlsoft namespace, so must define ::unixstl */
  93. namespace unixstl
  94. {
  95. # else
  96. /* Define stlsoft::unixstl_project */
  97. namespace stlsoft
  98. {
  99. namespace unixstl_project
  100. {
  101. # endif /* _STLSOFT_NO_NAMESPACE */
  102. #endif /* !_UNIXSTL_NO_NAMESPACE */
  103. /* /////////////////////////////////////////////////////////////////////////
  104. * Classes
  105. */
  106. // class thread_mutex
  107. /** \brief This class provides an implementation of the mutex model based on
  108. * the PTHREADS mutex.
  109. *
  110. * \ingroup group__library__synch
  111. */
  112. class thread_mutex
  113. : public stlsoft_ns_qual(critical_section)< STLSOFT_CRITICAL_SECTION_IS_RECURSIVE
  114. , STLSOFT_CRITICAL_SECTION_IS_TRYABLE
  115. >
  116. {
  117. /// \name Member Types
  118. /// @{
  119. public:
  120. typedef thread_mutex class_type;
  121. typedef us_bool_t bool_type;
  122. typedef pthread_mutex_t* resource_type;
  123. /// @}
  124. /// \name Construction
  125. /// @{
  126. public:
  127. /// \brief Creates an instance of the mutex
  128. ss_explicit_k thread_mutex(bool_type bRecursive = true) stlsoft_throw_0()
  129. : m_mx(&m_mx_)
  130. , m_error(create_(&m_mx_, bRecursive))
  131. , m_bOwnHandle(true)
  132. {}
  133. /// \brief Conversion constructor
  134. ///
  135. /// \param mx The raw mutex object handle that this instance will use
  136. /// \param bTakeOwnership If true, the handle is closed when this instance is destroyed
  137. thread_mutex(pthread_mutex_t* mx, bool_type bTakeOwnership)
  138. : m_mx(mx)
  139. , m_error(0)
  140. , m_bOwnHandle(bTakeOwnership)
  141. {
  142. UNIXSTL_ASSERT(NULL != mx);
  143. }
  144. /// \brief Destroys an instance of the mutex
  145. ~thread_mutex() stlsoft_throw_0()
  146. {
  147. if( 0 == m_error &&
  148. m_bOwnHandle)
  149. {
  150. ::pthread_mutex_destroy(m_mx);
  151. }
  152. }
  153. /// @}
  154. /// \name Operations
  155. /// @{
  156. public:
  157. /// \brief Acquires a lock on the mutex, pending the thread until the lock is aquired
  158. ///
  159. /// \exception unixstl::synchronisation_exception When compiling with exception support, this will throw
  160. /// unixstl::synchronisation_exception if the lock cannot be acquired. When
  161. /// compiling absent exception support, failure to acquire the lock
  162. /// will be reflected in a non-zero return from get_error().
  163. void lock()
  164. {
  165. m_error = ::pthread_mutex_lock(m_mx);
  166. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  167. if(0 != m_error)
  168. {
  169. STLSOFT_THROW_X(synchronisation_exception("Mutex lock failed", m_error));
  170. }
  171. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  172. }
  173. /// \brief Attempts to lock the mutex
  174. ///
  175. /// \return <b>true</b> if the mutex was aquired, or <b>false</b> if not.
  176. ///
  177. /// \exception unixstl::synchronisation_exception When compiling with exception support, this will throw
  178. /// unixstl::synchronisation_exception if the lock cannot be acquired for a reason
  179. /// other than a timeout (<code>EBUSY</code>). When compiling absent
  180. /// exception support, failure to acquire the lock (for any other
  181. /// reason) will be reflected in a non-zero return from get_error().
  182. bool try_lock()
  183. {
  184. m_error = ::pthread_mutex_trylock(m_mx);
  185. if(0 == m_error)
  186. {
  187. return true;
  188. }
  189. else
  190. {
  191. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  192. if(EBUSY != m_error)
  193. {
  194. STLSOFT_THROW_X(synchronisation_exception("Mutex try-lock failed", m_error));
  195. }
  196. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  197. return false;
  198. }
  199. }
  200. /// \brief Releases an aquired lock on the mutex
  201. ///
  202. /// \exception unixstl::synchronisation_exception When compiling with exception support, this will throw
  203. /// unixstl::synchronisation_exception if the lock cannot be released. When
  204. /// compiling absent exception support, failure to release the lock
  205. /// will be reflected in a non-zero return from get_error().
  206. void unlock() stlsoft_throw_0()
  207. {
  208. m_error = ::pthread_mutex_unlock(m_mx);
  209. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  210. if(0 != m_error)
  211. {
  212. STLSOFT_THROW_X(synchronisation_exception("Mutex unlock failed", m_error));
  213. }
  214. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  215. }
  216. /// \brief Contains the last failed error code from the underlying PTHREADS API
  217. int get_error() const stlsoft_throw_0()
  218. {
  219. return m_error;
  220. }
  221. /// @}
  222. /// \name Accessors
  223. /// @{
  224. public:
  225. /// \brief The underlying kernel object handle
  226. pthread_mutex_t* handle() stlsoft_throw_0()
  227. {
  228. return m_mx;
  229. }
  230. /// \brief The underlying kernel object handle
  231. pthread_mutex_t* get() stlsoft_throw_0()
  232. {
  233. return m_mx;
  234. }
  235. /// @}
  236. /// \name Implementation
  237. /// @{
  238. private:
  239. #if defined(STLSOFT_COMPILER_IS_SUNPRO)
  240. static int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
  241. {
  242. return ::pthread_mutexattr_destroy(attr);
  243. }
  244. #endif /* compiler */
  245. static int create_(pthread_mutex_t* mx, bool_type bRecursive)
  246. {
  247. pthread_mutexattr_t attr;
  248. int res = 0;
  249. if(0 == (res = ::pthread_mutexattr_init(&attr)))
  250. {
  251. stlsoft::scoped_handle<pthread_mutexattr_t*> attr_(&attr, pthread_mutexattr_destroy);
  252. if( !bRecursive ||
  253. 0 == (res = ::pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)))
  254. {
  255. if(0 == ::pthread_mutex_init(mx, &attr))
  256. {
  257. }
  258. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  259. else
  260. {
  261. STLSOFT_THROW_X(synchronisation_exception("Failed to set initialise PTHREADS mutex", res));
  262. }
  263. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  264. }
  265. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  266. else
  267. {
  268. STLSOFT_THROW_X(synchronisation_exception("Failed to set recursive attribute to PTHREADS mutex", res));
  269. }
  270. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  271. }
  272. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  273. else
  274. {
  275. STLSOFT_THROW_X(synchronisation_exception("Failed to initialise PTHREADS mutex attributes", res));
  276. }
  277. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  278. return res;
  279. }
  280. /// @}
  281. /// \name Members
  282. /// @{
  283. private:
  284. pthread_mutex_t m_mx_; // The mutex used when created and owned by the instance
  285. pthread_mutex_t* const m_mx; // The mutex "handle"
  286. int m_error; // The last PThreads error
  287. const bool_type m_bOwnHandle; // Does the instance own the handle?
  288. /// @}
  289. /// \name Not to be implemented
  290. /// @{
  291. private:
  292. thread_mutex(class_type const& rhs);
  293. thread_mutex& operator =(class_type const& rhs);
  294. /// @}
  295. };
  296. /* /////////////////////////////////////////////////////////////////////////
  297. * Control shims
  298. */
  299. #ifndef _UNIXSTL_NO_NAMESPACE
  300. # if defined(_STLSOFT_NO_NAMESPACE) || \
  301. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  302. } // namespace unixstl
  303. # else
  304. } // namespace unixstl_project
  305. # endif /* _STLSOFT_NO_NAMESPACE */
  306. #endif /* !_UNIXSTL_NO_NAMESPACE */
  307. /** \brief This \ref group__concept__shims "control shim" aquires a lock on the given mutex
  308. *
  309. * \ingroup group__concept__shim__synchronisation_control
  310. *
  311. * \param mx The mutex on which to aquire the lock.
  312. */
  313. inline void lock_instance(unixstl_ns_qual(thread_mutex) &mx)
  314. {
  315. mx.lock();
  316. }
  317. /** \brief This \ref group__concept__shims "control shim" releases a lock on the given mutex
  318. *
  319. * \ingroup group__concept__shim__synchronisation_control
  320. *
  321. * \param mx The mutex on which to release the lock
  322. */
  323. inline void unlock_instance(unixstl_ns_qual(thread_mutex) &mx)
  324. {
  325. mx.unlock();
  326. }
  327. #ifndef _UNIXSTL_NO_NAMESPACE
  328. # if defined(_STLSOFT_NO_NAMESPACE) || \
  329. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  330. namespace unixstl
  331. {
  332. # else
  333. namespace unixstl_project
  334. {
  335. # endif /* _STLSOFT_NO_NAMESPACE */
  336. #endif /* !_UNIXSTL_NO_NAMESPACE */
  337. /* /////////////////////////////////////////////////////////////////////////
  338. * lock_traits
  339. */
  340. // class lock_traits
  341. /** \brief Traits for the thread_mutex class
  342. *
  343. * \ingroup group__library__synch
  344. */
  345. struct thread_mutex_lock_traits
  346. {
  347. public:
  348. /// The lockable type
  349. typedef thread_mutex lock_type;
  350. typedef thread_mutex_lock_traits class_type;
  351. // Operations
  352. public:
  353. /// Lock the given thread_mutex instance
  354. static void lock(thread_mutex &c)
  355. {
  356. lock_instance(c);
  357. }
  358. /// Unlock the given thread_mutex instance
  359. static void unlock(thread_mutex &c)
  360. {
  361. unlock_instance(c);
  362. }
  363. };
  364. ////////////////////////////////////////////////////////////////////////////
  365. // Unit-testing
  366. #ifdef STLSOFT_UNITTEST
  367. # include "./unittest/thread_mutex_unittest_.h"
  368. #endif /* STLSOFT_UNITTEST */
  369. /* ////////////////////////////////////////////////////////////////////// */
  370. #ifndef _UNIXSTL_NO_NAMESPACE
  371. # if defined(_STLSOFT_NO_NAMESPACE) || \
  372. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  373. } // namespace unixstl
  374. # else
  375. } // namespace unixstl_project
  376. } // namespace stlsoft
  377. # endif /* _STLSOFT_NO_NAMESPACE */
  378. #endif /* !_UNIXSTL_NO_NAMESPACE */
  379. /* ////////////////////////////////////////////////////////////////////// */
  380. #endif /* !UNIXSTL_INCL_UNIXSTL_SYNCH_HPP_THREAD_MUTEX */
  381. /* ///////////////////////////// end of file //////////////////////////// */