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.

519 lines
16 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: winstl/synch/process_mutex.hpp
  3. *
  4. * Purpose: Inter-process mutex, based on Windows MUTEX.
  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 winstl/synch/process_mutex.hpp
  40. *
  41. * \brief [C++ only] Definition of winstl::process_mutex class
  42. * (\ref group__library__synch "Synchronisation" Library).
  43. */
  44. #ifndef WINSTL_INCL_WINSTL_SYNCH_HPP_PROCESS_MUTEX
  45. #define WINSTL_INCL_WINSTL_SYNCH_HPP_PROCESS_MUTEX
  46. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  47. # define WINSTL_VER_WINSTL_SYNCH_HPP_PROCESS_MUTEX_MAJOR 4
  48. # define WINSTL_VER_WINSTL_SYNCH_HPP_PROCESS_MUTEX_MINOR 3
  49. # define WINSTL_VER_WINSTL_SYNCH_HPP_PROCESS_MUTEX_REVISION 1
  50. # define WINSTL_VER_WINSTL_SYNCH_HPP_PROCESS_MUTEX_EDIT 61
  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 process_mutex
  85. /** \brief This class acts as an inter-process mutex based on the Win32
  86. * mutex kernel object
  87. *
  88. * \ingroup group__library__synch
  89. */
  90. class process_mutex
  91. : public stlsoft_ns_qual(critical_section)< STLSOFT_CRITICAL_SECTION_IS_RECURSIVE
  92. , STLSOFT_CRITICAL_SECTION_IS_TRYABLE
  93. >
  94. , public stlsoft_ns_qual(synchronisable_object_tag)
  95. {
  96. /// \name Member Types
  97. /// @{
  98. public:
  99. /// This type
  100. typedef process_mutex class_type;
  101. typedef HANDLE synch_handle_type;
  102. typedef HANDLE resource_type;
  103. /// @}
  104. /// \name Construction
  105. /// @{
  106. public:
  107. /// \brief Creates an instance of the mutex
  108. process_mutex()
  109. : m_mx(create_mutex_(NULL, false, static_cast<ws_char_a_t const*>(0), m_bCreated))
  110. , m_bOwnHandle(true)
  111. , m_bAbandoned(false)
  112. {}
  113. /// \brief Conversion constructor
  114. process_mutex(HANDLE mx, ws_bool_t bTakeOwnership)
  115. : m_mx(mx)
  116. , m_bOwnHandle(bTakeOwnership)
  117. , m_bCreated(false)
  118. , m_bAbandoned(false)
  119. {
  120. WINSTL_ASSERT(NULL != mx);
  121. }
  122. /// \brief Creates an instance of the mutex
  123. ss_explicit_k process_mutex(ws_char_a_t const* name)
  124. : m_mx(create_mutex_(NULL, false, name, m_bCreated))
  125. , m_bOwnHandle(true)
  126. , m_bAbandoned(false)
  127. {}
  128. /// \brief Creates an instance of the mutex
  129. ss_explicit_k process_mutex(ws_char_w_t const* name)
  130. : m_mx(create_mutex_(NULL, false, name, m_bCreated))
  131. , m_bOwnHandle(true)
  132. , m_bAbandoned(false)
  133. {}
  134. /// \brief Creates an instance of the mutex
  135. ss_explicit_k process_mutex(ws_bool_t bInitialOwer)
  136. : m_mx(create_mutex_(NULL, bInitialOwer, static_cast<ws_char_a_t const*>(0), m_bCreated))
  137. , m_bOwnHandle(true)
  138. , m_bAbandoned(false)
  139. {}
  140. /// \brief Creates an instance of the mutex
  141. ss_explicit_k process_mutex(ws_char_a_t const* name, ws_bool_t bInitialOwer)
  142. : m_mx(create_mutex_(NULL, bInitialOwer, name, m_bCreated))
  143. , m_bOwnHandle(true)
  144. , m_bAbandoned(false)
  145. {}
  146. /// \brief Creates an instance of the mutex
  147. ss_explicit_k process_mutex(ws_char_w_t const* name, ws_bool_t bInitialOwer)
  148. : m_mx(create_mutex_(NULL, bInitialOwer, name, m_bCreated))
  149. , m_bOwnHandle(true)
  150. , m_bAbandoned(false)
  151. {}
  152. /// \brief Creates an instance of the mutex
  153. ss_explicit_k process_mutex(ws_char_a_t const* name, ws_bool_t bInitialOwer, LPSECURITY_ATTRIBUTES psa)
  154. : m_mx(create_mutex_(psa, bInitialOwer, name, m_bCreated))
  155. , m_bOwnHandle(true)
  156. , m_bAbandoned(false)
  157. {}
  158. /// \brief Creates an instance of the mutex
  159. ss_explicit_k process_mutex(ws_char_w_t const* name, ws_bool_t bInitialOwer, LPSECURITY_ATTRIBUTES psa)
  160. : m_mx(create_mutex_(psa, bInitialOwer, name, m_bCreated))
  161. , m_bOwnHandle(true)
  162. , m_bAbandoned(false)
  163. {}
  164. /// \brief Destroys an instance of the mutex
  165. ~process_mutex() stlsoft_throw_0()
  166. {
  167. if( NULL != m_mx &&
  168. m_bOwnHandle)
  169. {
  170. ::CloseHandle(m_mx);
  171. }
  172. }
  173. /// @}
  174. /// \name Operations
  175. /// @{
  176. public:
  177. /// \brief Acquires a lock on the mutex, pending the thread until the lock is aquired
  178. void lock()
  179. {
  180. WINSTL_ASSERT(NULL != m_mx);
  181. DWORD dwRes = ::WaitForSingleObject(m_mx, INFINITE);
  182. if(WAIT_ABANDONED == dwRes)
  183. {
  184. m_bAbandoned = true;
  185. }
  186. else
  187. {
  188. m_bAbandoned = false;
  189. if(WAIT_OBJECT_0 != dwRes)
  190. {
  191. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  192. STLSOFT_THROW_X(synchronisation_exception("mutex wait failed", ::GetLastError()));
  193. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  194. }
  195. }
  196. }
  197. /// \brief Acquires a lock on the mutex, pending the thread until the lock is aquired
  198. ws_bool_t lock(ws_dword_t wait)
  199. {
  200. WINSTL_ASSERT(NULL != m_mx);
  201. DWORD dwRes = ::WaitForSingleObject(m_mx, wait);
  202. if(WAIT_ABANDONED == dwRes)
  203. {
  204. m_bAbandoned = true;
  205. return true;
  206. }
  207. else
  208. {
  209. m_bAbandoned = false;
  210. if(WAIT_TIMEOUT == dwRes)
  211. {
  212. return false;
  213. }
  214. else
  215. {
  216. if(WAIT_OBJECT_0 != dwRes)
  217. {
  218. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  219. STLSOFT_THROW_X(synchronisation_exception("mutex wait failed", ::GetLastError()));
  220. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  221. }
  222. return true;
  223. }
  224. }
  225. }
  226. /// \brief Attempts to lock the mutex
  227. ///
  228. /// \return <b>true</b> if the mutex was aquired, or <b>false</b> if not
  229. ws_bool_t try_lock()
  230. {
  231. return lock(0);
  232. }
  233. /// \brief Releases an aquired lock on the mutex
  234. void unlock()
  235. {
  236. WINSTL_ASSERT(NULL != m_mx);
  237. if(!::ReleaseMutex(m_mx))
  238. {
  239. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  240. STLSOFT_THROW_X(synchronisation_exception("mutex release failed", ::GetLastError()));
  241. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  242. }
  243. }
  244. /// @}
  245. /// \name Accessors
  246. /// @{
  247. public:
  248. /// \brief The underlying kernel object handle
  249. HANDLE handle()
  250. {
  251. return m_mx;
  252. }
  253. /// \brief The underlying kernel object handle
  254. HANDLE get()
  255. {
  256. return m_mx;
  257. }
  258. /// @}
  259. /// \name Attributes
  260. /// @{
  261. public:
  262. /// \brief Indicates whether this object instance created the underlying mutex object
  263. ///
  264. /// \return true The mutex object was created by this instance
  265. /// \return false The mutex object was not created by this instance
  266. /// \note For unnamed mutexes this will always be false
  267. ws_bool_t created() const
  268. {
  269. return m_bCreated;
  270. }
  271. /// \brief Indicates whether a successful call to lock occurred because the underlying
  272. /// mutex was previously held by a thread that abandoned.
  273. ///
  274. /// \return true The mutex object was abandoned by its previous owning thread
  275. /// \return false The mutex object was not abandoned by its previous owning thread
  276. /// \note This attribute is meaningful with respect to the result of the last call to lock() or try_lock(). Subsequent calls to unlock() do not affect this attribute.
  277. ws_bool_t abandoned() const
  278. {
  279. return m_bAbandoned;
  280. }
  281. /// @}
  282. // Implementation
  283. private:
  284. static HANDLE create_mutex_(LPSECURITY_ATTRIBUTES psa, ws_bool_t bInitialOwner, ws_char_a_t const* name, ws_bool_t &bCreated)
  285. {
  286. HANDLE mx = ::CreateMutexA(psa, bInitialOwner, name);
  287. if(NULL == mx)
  288. {
  289. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  290. STLSOFT_THROW_X(synchronisation_exception("Failed to create kernel mutex object", ::GetLastError()));
  291. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  292. }
  293. bCreated = (mx != NULL && ::GetLastError() != ERROR_ALREADY_EXISTS);
  294. return mx;
  295. }
  296. static HANDLE create_mutex_(LPSECURITY_ATTRIBUTES psa, ws_bool_t bInitialOwner, ws_char_w_t const* name, ws_bool_t &bCreated)
  297. {
  298. HANDLE mx = ::CreateMutexW(psa, bInitialOwner, name);
  299. if(NULL == mx)
  300. {
  301. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  302. STLSOFT_THROW_X(synchronisation_exception("Failed to create kernel mutex object", ::GetLastError()));
  303. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  304. }
  305. bCreated = (mx != NULL && ::GetLastError() != ERROR_ALREADY_EXISTS);
  306. return mx;
  307. }
  308. static HANDLE open_mutex_(ws_dword_t access, ws_bool_t bInheritHandle, ws_char_a_t const* name, ws_bool_t &bCreated)
  309. {
  310. HANDLE mx = ::OpenMutexA(access, bInheritHandle, name);
  311. if(NULL == mx)
  312. {
  313. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  314. STLSOFT_THROW_X(synchronisation_exception("Failed to open kernel mutex object", ::GetLastError()));
  315. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  316. }
  317. bCreated = (mx != NULL && ::GetLastError() != ERROR_ALREADY_EXISTS);
  318. return mx;
  319. }
  320. static HANDLE open_mutex_(ws_dword_t access, ws_bool_t bInheritHandle, ws_char_w_t const* name, ws_bool_t &bCreated)
  321. {
  322. HANDLE mx = ::OpenMutexW(access, bInheritHandle, name);
  323. if(NULL == mx)
  324. {
  325. #ifdef STLSOFT_CF_EXCEPTION_SUPPORT
  326. STLSOFT_THROW_X(synchronisation_exception("Failed to open kernel mutex object", ::GetLastError()));
  327. #endif /* STLSOFT_CF_EXCEPTION_SUPPORT */
  328. }
  329. bCreated = (mx != NULL && ::GetLastError() != ERROR_ALREADY_EXISTS);
  330. return mx;
  331. }
  332. // Members
  333. private:
  334. HANDLE m_mx; // The underlying mutex object
  335. const ws_bool_t m_bOwnHandle; // Does the instance own the handle?
  336. ws_bool_t m_bCreated; // Did this object (thread) create the underlying mutex object?
  337. ws_bool_t m_bAbandoned; // Did the previous owner abandon the underlying mutex object?
  338. // Not to be implemented
  339. private:
  340. process_mutex(class_type const& rhs);
  341. process_mutex& operator =(class_type const& rhs);
  342. };
  343. /* /////////////////////////////////////////////////////////////////////////
  344. * Control shims
  345. */
  346. /** \brief Overload of the form of the winstl::get_synch_handle() shim for
  347. * the winstl::process_mutex type.
  348. *
  349. * \param mx The winstl::process_mutex instance
  350. *
  351. * \retval The synchronisation handle of \c mx
  352. */
  353. inline HANDLE get_synch_handle(process_mutex &mx)
  354. {
  355. return mx.get();
  356. }
  357. /** \brief Overload of the form of the winstl::get_kernel_handle() shim for
  358. * the winstl::process_mutex type.
  359. *
  360. * \ingroup group__library__shims__kernel_handle_attribute
  361. *
  362. * \param mx The winstl::process_mutex instance
  363. *
  364. * \retval The synchronisation handle of \c mx
  365. */
  366. inline HANDLE get_kernel_handle(process_mutex &mx)
  367. {
  368. return mx.get();
  369. }
  370. #ifndef _WINSTL_NO_NAMESPACE
  371. # if defined(_STLSOFT_NO_NAMESPACE) || \
  372. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  373. } // namespace winstl
  374. # else
  375. } // namespace winstl_project
  376. # endif /* _STLSOFT_NO_NAMESPACE */
  377. #endif /* !_WINSTL_NO_NAMESPACE */
  378. /** \brief This \ref group__concept__shims "control shim" aquires a lock on the given mutex
  379. *
  380. * \ingroup group__concept__shim__synchronisation_control
  381. *
  382. * \param mx The mutex on which to aquire the lock.
  383. */
  384. inline void lock_instance(winstl_ns_qual(process_mutex) &mx)
  385. {
  386. mx.lock();
  387. }
  388. /** \brief This \ref group__concept__shims "control shim" releases a lock on the given mutex
  389. *
  390. * \ingroup group__concept__shim__synchronisation_control
  391. *
  392. * \param mx The mutex on which to release the lock
  393. */
  394. inline void unlock_instance(winstl_ns_qual(process_mutex) &mx)
  395. {
  396. mx.unlock();
  397. }
  398. #ifndef _WINSTL_NO_NAMESPACE
  399. # if defined(_STLSOFT_NO_NAMESPACE) || \
  400. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  401. namespace winstl {
  402. # else
  403. namespace winstl_project {
  404. # if defined(STLSOFT_COMPILER_IS_BORLAND)
  405. using ::stlsoft::lock_instance;
  406. using ::stlsoft::unlock_instance;
  407. # endif /* compiler */
  408. # endif /* _STLSOFT_NO_NAMESPACE */
  409. #endif /* !_WINSTL_NO_NAMESPACE */
  410. /* /////////////////////////////////////////////////////////////////////////
  411. * lock_traits
  412. */
  413. // class lock_traits
  414. /** \brief Traits for the process_mutex class
  415. *
  416. * \ingroup group__library__synch
  417. */
  418. struct process_mutex_lock_traits
  419. {
  420. public:
  421. /// The lockable type
  422. typedef process_mutex lock_type;
  423. /// This type
  424. typedef process_mutex_lock_traits class_type;
  425. // Operations
  426. public:
  427. /// Lock the given process_mutex instance
  428. static void lock(process_mutex &c)
  429. {
  430. lock_instance(c);
  431. }
  432. /// Unlock the given process_mutex instance
  433. static void unlock(process_mutex &c)
  434. {
  435. unlock_instance(c);
  436. }
  437. };
  438. ////////////////////////////////////////////////////////////////////////////
  439. // Unit-testing
  440. #ifdef STLSOFT_UNITTEST
  441. # include "./unittest/process_mutex_unittest_.h"
  442. #endif /* STLSOFT_UNITTEST */
  443. /* ////////////////////////////////////////////////////////////////////// */
  444. #ifndef _WINSTL_NO_NAMESPACE
  445. # if defined(_STLSOFT_NO_NAMESPACE) || \
  446. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  447. } // namespace winstl
  448. # else
  449. } // namespace winstl_project
  450. } // namespace stlsoft
  451. # endif /* _STLSOFT_NO_NAMESPACE */
  452. #endif /* !_WINSTL_NO_NAMESPACE */
  453. /* ////////////////////////////////////////////////////////////////////// */
  454. #endif /* !WINSTL_INCL_WINSTL_SYNCH_HPP_PROCESS_MUTEX */
  455. /* ///////////////////////////// end of file //////////////////////////// */