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.

490 lines
16 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: unixstl/synch/process_mutex.hpp
  3. *
  4. * Purpose: Intra-process mutext, based on PTHREADS.
  5. *
  6. * Created: 15th May 2002
  7. * Updated: 10th August 2009
  8. *
  9. * Home: http://stlsoft.org/
  10. *
  11. * Copyright (c) 2002-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/process_mutex.hpp
  40. *
  41. * \brief [C++ only] Definition of the unixstl::process_mutex class
  42. * (\ref group__library__synch "Synchronisation" Library).
  43. */
  44. #ifndef UNIXSTL_INCL_UNIXSTL_SYNCH_HPP_PROCESS_MUTEX
  45. #define UNIXSTL_INCL_UNIXSTL_SYNCH_HPP_PROCESS_MUTEX
  46. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  47. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_PROCESS_MUTEX_MAJOR 4
  48. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_PROCESS_MUTEX_MINOR 6
  49. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_PROCESS_MUTEX_REVISION 3
  50. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_PROCESS_MUTEX_EDIT 73
  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/process_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 process_mutex
  107. /** \brief This class provides an implementation of the mutex model based on
  108. * the PTHREADS pthread_mutex_t.
  109. *
  110. * \ingroup group__library__synch
  111. */
  112. class process_mutex
  113. : public stlsoft_ns_qual(critical_section)< STLSOFT_CRITICAL_SECTION_ISNOT_RECURSIVE
  114. , STLSOFT_CRITICAL_SECTION_IS_TRYABLE
  115. >
  116. {
  117. /// \name Member Types
  118. /// @{
  119. public:
  120. typedef process_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. ///
  129. /// \note This creates a recursive mutex. Use the other constructor(s) to
  130. /// obtain a non-recursive mutex.
  131. ///
  132. /// \note On systems that support shared mutexes, this will be not shared. Use
  133. /// the other constructor to obtain a shared mutex
  134. process_mutex()
  135. : m_mx(&m_mx_)
  136. #if defined(_POSIX_THREAD_PROCESS_SHARED)
  137. , m_error(create_(&m_mx_, PTHREAD_PROCESS_PRIVATE, true))
  138. #else /* ? _POSIX_THREAD_PROCESS_SHARED */
  139. , m_error(create_(&m_mx_, 0, true))
  140. #endif /* _POSIX_THREAD_PROCESS_SHARED */
  141. , m_bOwnHandle(true)
  142. {}
  143. /// \brief Conversion constructor
  144. ///
  145. /// \param mx The raw mutex object handle that this instance will use
  146. /// \param bTakeOwnership If true, the handle is closed when this instance is destroyed
  147. process_mutex(pthread_mutex_t* mx, bool_type bTakeOwnership)
  148. : m_mx(mx)
  149. , m_error(0)
  150. , m_bOwnHandle(bTakeOwnership)
  151. {
  152. UNIXSTL_ASSERT(NULL != mx);
  153. }
  154. /// \brief Creates an instance of the mutex
  155. ///
  156. /// \note On systems that support shared mutexes, this will be not shared. Use
  157. /// the two-parameter constructor to obtain a shared mutex
  158. ss_explicit_k process_mutex(bool_type bRecursive)
  159. : m_mx(&m_mx_)
  160. #if defined(_POSIX_THREAD_PROCESS_SHARED)
  161. , m_error(create_(&m_mx_, PTHREAD_PROCESS_PRIVATE, bRecursive))
  162. #else /* ? _POSIX_THREAD_PROCESS_SHARED */
  163. , m_error(create_(&m_mx_, 0, bRecursive))
  164. #endif /* _POSIX_THREAD_PROCESS_SHARED */
  165. , m_bOwnHandle(true)
  166. {}
  167. #if defined(_POSIX_THREAD_PROCESS_SHARED)
  168. /// \brief Creates an instance of the mutex, optionally recursive and/or shared between processes
  169. ///
  170. /// \param pshared A value from the PTHREADS_PROCESS_* group that determines the sharing
  171. /// characteristics of the mutex.
  172. /// \param bRecursive A boolean value denoting whether the mutex should be recursive or not
  173. process_mutex(int pshared, bool_type bRecursive)
  174. : m_mx(&m_mx_)
  175. , m_error(create_(&m_mx_, pshared, bRecursive))
  176. , m_bOwnHandle(true)
  177. {}
  178. #endif /* _POSIX_THREAD_PROCESS_SHARED */
  179. /// \brief Destroys an instance of the mutex
  180. ~process_mutex() stlsoft_throw_0()
  181. {
  182. if( 0 == m_error &&
  183. m_bOwnHandle)
  184. {
  185. ::pthread_mutex_destroy(m_mx);
  186. }
  187. }
  188. /// @}
  189. /// \name Operations
  190. /// @{
  191. public:
  192. /// \brief Acquires a lock on the mutex, pending the thread until the lock is aquired
  193. ///
  194. /// \exception unixstl::synchronisation_exception When compiling with exception support, this will throw
  195. /// unixstl::synchronisation_exception if the lock cannot be acquired. When
  196. /// compiling absent exception support, failure to acquire the lock
  197. /// will be reflected in a non-zero return from get_error().
  198. void lock()
  199. {
  200. m_error = ::pthread_mutex_lock(m_mx);
  201. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  202. if(0 != m_error)
  203. {
  204. STLSOFT_THROW_X(synchronisation_exception("Mutex lock failed", m_error));
  205. }
  206. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  207. }
  208. /// \brief Attempts to lock the mutex
  209. ///
  210. /// \return <b>true</b> if the mutex was aquired, or <b>false</b> if not.
  211. ///
  212. /// \exception unixstl::synchronisation_exception When compiling with exception support, this will throw
  213. /// unixstl::synchronisation_exception if the lock cannot be acquired for a reason
  214. /// other than a timeout (<code>EBUSY</code>). When compiling absent
  215. /// exception support, failure to acquire the lock (for any other
  216. /// reason) will be reflected in a non-zero return from get_error().
  217. bool try_lock()
  218. {
  219. m_error = ::pthread_mutex_trylock(m_mx);
  220. if(0 == m_error)
  221. {
  222. return true;
  223. }
  224. else
  225. {
  226. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  227. if(EBUSY != m_error)
  228. {
  229. STLSOFT_THROW_X(synchronisation_exception("Mutex try-lock failed", m_error));
  230. }
  231. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  232. return false;
  233. }
  234. }
  235. /// \brief Releases an aquired lock on the mutex
  236. ///
  237. /// \exception unixstl::synchronisation_exception When compiling with exception support, this will throw
  238. /// unixstl::synchronisation_exception if the lock cannot be released. When
  239. /// compiling absent exception support, failure to release the lock
  240. /// will be reflected in a non-zero return from get_error().
  241. void unlock()
  242. {
  243. m_error = ::pthread_mutex_unlock(m_mx);
  244. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  245. if(0 != m_error)
  246. {
  247. STLSOFT_THROW_X(synchronisation_exception("Mutex unlock failed", m_error));
  248. }
  249. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  250. }
  251. /// \brief Contains the last failed error code from the underlying PTHREADS API
  252. int get_error() const stlsoft_throw_0()
  253. {
  254. return m_error;
  255. }
  256. /// @}
  257. /// \name Accessors
  258. /// @{
  259. public:
  260. /// \brief The underlying kernel object handle
  261. pthread_mutex_t* handle() stlsoft_throw_0()
  262. {
  263. return m_mx;
  264. }
  265. /// \brief The underlying kernel object handle
  266. pthread_mutex_t* get() stlsoft_throw_0()
  267. {
  268. return m_mx;
  269. }
  270. /// @}
  271. /// \name Implementation
  272. /// @{
  273. private:
  274. #if defined(STLSOFT_COMPILER_IS_SUNPRO)
  275. static int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
  276. {
  277. return ::pthread_mutexattr_destroy(attr);
  278. }
  279. #endif /* compiler */
  280. static int create_(pthread_mutex_t* mx, int pshared, bool_type bRecursive)
  281. {
  282. pthread_mutexattr_t attr;
  283. int res = 0;
  284. if(0 == (res = ::pthread_mutexattr_init(&attr)))
  285. {
  286. stlsoft::scoped_handle<pthread_mutexattr_t*> attr_(&attr, pthread_mutexattr_destroy);
  287. if( !bRecursive ||
  288. 0 == (res = ::pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)))
  289. {
  290. #if defined(_POSIX_THREAD_PROCESS_SHARED)
  291. if(0 != (res = ::pthread_mutexattr_setpshared(&attr, pshared)))
  292. {
  293. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  294. STLSOFT_THROW_X(synchronisation_exception("Failed to set process-sharing attribute for PTHREADS mutex", res));
  295. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  296. }
  297. else
  298. #else /* ? _POSIX_THREAD_PROCESS_SHARED */
  299. STLSOFT_SUPPRESS_UNUSED(pshared);
  300. #endif /* _POSIX_THREAD_PROCESS_SHARED */
  301. {
  302. if(0 == (res = ::pthread_mutex_init(mx, &attr)))
  303. {
  304. }
  305. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  306. else
  307. {
  308. STLSOFT_THROW_X(synchronisation_exception("Failed to set initialise PTHREADS mutex", res));
  309. }
  310. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  311. }
  312. }
  313. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  314. else
  315. {
  316. STLSOFT_THROW_X(synchronisation_exception("Failed to set recursive attribute to PTHREADS mutex", res));
  317. }
  318. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  319. }
  320. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  321. else
  322. {
  323. STLSOFT_THROW_X(synchronisation_exception("Failed to initialise PTHREADS mutex attributes", res));
  324. }
  325. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  326. return res;
  327. }
  328. /// @}
  329. /// \name Members
  330. /// @{
  331. private:
  332. pthread_mutex_t m_mx_; // The mutex used when created and owned by the instance
  333. pthread_mutex_t* const m_mx; // The mutex "handle"
  334. int m_error; // The last PThreads error
  335. const bool_type m_bOwnHandle; // Does the instance own the handle?
  336. /// @}
  337. /// \name Not to be implemented
  338. /// @{
  339. private:
  340. process_mutex(class_type const& rhs);
  341. process_mutex& operator =(class_type const& rhs);
  342. /// @}
  343. };
  344. /* /////////////////////////////////////////////////////////////////////////
  345. * Control shims
  346. */
  347. #ifndef _UNIXSTL_NO_NAMESPACE
  348. # if defined(_STLSOFT_NO_NAMESPACE) || \
  349. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  350. } // namespace unixstl
  351. # else
  352. } // namespace unixstl_project
  353. # endif /* _STLSOFT_NO_NAMESPACE */
  354. #endif /* !_UNIXSTL_NO_NAMESPACE */
  355. /** \brief This \ref group__concept__shims "control shim" aquires a lock on the given mutex
  356. *
  357. * \ingroup group__concept__shim__synchronisation_control
  358. *
  359. * \param mx The mutex on which to aquire the lock.
  360. */
  361. inline void lock_instance(unixstl_ns_qual(process_mutex) &mx)
  362. {
  363. mx.lock();
  364. }
  365. /** \brief This \ref group__concept__shims "control shim" releases a lock on the given mutex
  366. *
  367. * \ingroup group__concept__shim__synchronisation_control
  368. *
  369. * \param mx The mutex on which to release the lock
  370. */
  371. inline void unlock_instance(unixstl_ns_qual(process_mutex) &mx)
  372. {
  373. mx.unlock();
  374. }
  375. #ifndef _UNIXSTL_NO_NAMESPACE
  376. # if defined(_STLSOFT_NO_NAMESPACE) || \
  377. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  378. namespace unixstl
  379. {
  380. # else /* ? _STLSOFT_NO_NAMESPACE */
  381. namespace unixstl_project
  382. {
  383. # endif /* _STLSOFT_NO_NAMESPACE */
  384. #endif /* !_UNIXSTL_NO_NAMESPACE */
  385. /* /////////////////////////////////////////////////////////////////////////
  386. * lock_traits
  387. */
  388. // class lock_traits
  389. /** \brief Traits for the process_mutex class
  390. *
  391. * \ingroup group__library__synch
  392. */
  393. struct process_mutex_lock_traits
  394. {
  395. public:
  396. /// The lockable type
  397. typedef process_mutex lock_type;
  398. typedef process_mutex_lock_traits class_type;
  399. // Operations
  400. public:
  401. /// Lock the given process_mutex instance
  402. static void lock(process_mutex &c)
  403. {
  404. #if defined(STLSOFT_COMPILER_IS_BORLAND)
  405. // Borland requires that we explicitly qualify the shim functions, even
  406. // though they're defined in the enclosing namespace of this one.
  407. stlsoft_ns_qual(lock_instance)(c);
  408. #else /* ? compiler */
  409. lock_instance(c);
  410. #endif /* compiler */
  411. }
  412. /// Unlock the given process_mutex instance
  413. static void unlock(process_mutex &c)
  414. {
  415. #if defined(STLSOFT_COMPILER_IS_BORLAND)
  416. // Borland requires that we explicitly qualify the shim functions, even
  417. // though they're defined in the enclosing namespace of this one.
  418. stlsoft_ns_qual(unlock_instance)(c);
  419. #else /* ? compiler */
  420. unlock_instance(c);
  421. #endif /* compiler */
  422. }
  423. };
  424. ////////////////////////////////////////////////////////////////////////////
  425. // Unit-testing
  426. #ifdef STLSOFT_UNITTEST
  427. # include "./unittest/process_mutex_unittest_.h"
  428. #endif /* STLSOFT_UNITTEST */
  429. /* ////////////////////////////////////////////////////////////////////// */
  430. #ifndef _UNIXSTL_NO_NAMESPACE
  431. # if defined(_STLSOFT_NO_NAMESPACE) || \
  432. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  433. } // namespace unixstl
  434. # else
  435. } // namespace unixstl_project
  436. } // namespace stlsoft
  437. # endif /* _STLSOFT_NO_NAMESPACE */
  438. #endif /* !_UNIXSTL_NO_NAMESPACE */
  439. /* ////////////////////////////////////////////////////////////////////// */
  440. #endif /* !UNIXSTL_INCL_UNIXSTL_SYNCH_HPP_PROCESS_MUTEX */
  441. /* ///////////////////////////// end of file //////////////////////////// */