Lockable.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /* Copyright The kNet Project.
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License. */
  11. #pragma once
  12. /** @file Lockable.h
  13. @brief The Lock<T> and Lockable<T> template classes. */
  14. // Modified by Lasse Oorni for Urho3D
  15. #ifdef KNET_USE_BOOST
  16. #include <boost/thread/recursive_mutex.hpp>
  17. #include <boost/thread/thread.hpp>
  18. #elif defined(WIN32)
  19. // Urho3D: windows.h in lowercase to fix MinGW cross-compiling on a case-sensitive system
  20. #include <windows.h>
  21. #else
  22. #include <pthread.h>
  23. #endif
  24. #include <assert.h>
  25. #include "PolledTimer.h"
  26. #include "NetworkLogging.h"
  27. namespace kNet
  28. {
  29. template<typename T>
  30. class Lockable;
  31. /// @internal Wraps mutex-lock acquisition and releasing into a RAII-style object that automatically releases the lock when the scope
  32. /// is exited. Lock operates in an std::auto_ptr style in deciding which object has the ownership of the lock.
  33. template<typename T>
  34. class Lock
  35. {
  36. public:
  37. explicit Lock(Lockable<T> *lockedObject_)
  38. :lockedObject(lockedObject_), value(&lockedObject->LockGet())
  39. {
  40. }
  41. Lock(const Lock<T> &rhs)
  42. :lockedObject(rhs.lockedObject), value(rhs.value)
  43. {
  44. assert(this != &rhs);
  45. rhs.TearDown();
  46. }
  47. Lock<T> &operator=(const Lock<T> &rhs)
  48. {
  49. if (&rhs == this)
  50. return *this;
  51. lockedObject = rhs.lockedObject();
  52. value = rhs.value;
  53. rhs.TearDown();
  54. }
  55. ~Lock()
  56. {
  57. Unlock();
  58. }
  59. void Unlock()
  60. {
  61. if (lockedObject)
  62. {
  63. lockedObject->Unlock();
  64. lockedObject = 0;
  65. }
  66. }
  67. T *operator ->() const { return value; }
  68. T &operator *() { return *value; }
  69. private:
  70. mutable Lockable<T> *lockedObject;
  71. mutable T *value;
  72. /// Clears the pointer to the object this Lock points to. This function is const to allow std::auto_ptr -like
  73. /// lock ownership passing in copy-ctor.
  74. void TearDown() const { lockedObject = 0; value = 0; }
  75. };
  76. /// @internal Wraps mutex-lock acquisition and releasing to const data into a RAII-style object
  77. /// that automatically releases the lock when the scope is exited.
  78. template<typename T>
  79. class ConstLock
  80. {
  81. public:
  82. explicit ConstLock(const Lockable<T> *lockedObject_)
  83. :lockedObject(lockedObject_), value(&lockedObject->LockGet())
  84. {
  85. }
  86. ConstLock(const ConstLock<T> &rhs)
  87. :lockedObject(rhs.lockedObject), value(rhs.value)
  88. {
  89. assert(this != &rhs);
  90. rhs.TearDown();
  91. }
  92. ConstLock<T> &operator=(const ConstLock<T> &rhs)
  93. {
  94. if (&rhs == this)
  95. return *this;
  96. lockedObject = rhs.lockedObject();
  97. value = rhs.value;
  98. rhs.TearDown();
  99. }
  100. ~ConstLock()
  101. {
  102. if (lockedObject)
  103. lockedObject->Unlock();
  104. }
  105. const T *operator ->() const { return value; }
  106. const T &operator *() const { return *value; }
  107. private:
  108. mutable const Lockable<T> *lockedObject;
  109. mutable const T *value;
  110. /// Clears the pointer to the object this Lock points to. This function is const to allow std::auto_ptr -like
  111. /// lock ownership passing in copy-ctor.
  112. void TearDown() const { lockedObject = 0; value = 0; }
  113. };
  114. /// Stores an object of type T behind a mutex-locked shield. To access the object, one has to acquire a lock to it first, and remember
  115. /// to free the lock when done. Use @see Lock and @see ConstLock to manage the locks in a RAII-style manner.
  116. template<typename T>
  117. class Lockable
  118. {
  119. public:
  120. typedef Lock<T> LockType;
  121. typedef ConstLock<T> ConstLockType;
  122. Lockable()
  123. {
  124. #ifndef KNET_USE_BOOST
  125. #ifdef WIN32
  126. InitializeCriticalSection(&lockObject);
  127. #else
  128. pthread_mutexattr_t attr;
  129. pthread_mutexattr_init(&attr);
  130. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  131. pthread_mutex_init(&mutex, &attr);
  132. #endif
  133. #endif
  134. }
  135. /* Lockable objects are noncopyable. If thread-safe copying were to be supported, it should be implemented something like this:
  136. Lockable(const Lockable<T> &other)
  137. {
  138. InitializeCriticalSection(&lockObject);
  139. value = other.Lock();
  140. other.Unlock();
  141. }
  142. */
  143. explicit Lockable(const T &value_)
  144. :value(value_)
  145. {
  146. #ifndef KNET_USE_BOOST
  147. #ifdef WIN32
  148. InitializeCriticalSection(&lockObject);
  149. #else
  150. pthread_mutexattr_t attr;
  151. pthread_mutexattr_init(&attr);
  152. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  153. pthread_mutex_init(&mutex, &attr);
  154. #endif
  155. #endif
  156. }
  157. ~Lockable()
  158. {
  159. #ifndef KNET_USE_BOOST
  160. #ifdef WIN32
  161. DeleteCriticalSection(&lockObject);
  162. #else
  163. pthread_mutex_destroy(&mutex);
  164. #endif
  165. #endif
  166. }
  167. /* Lockable objects are nonassignable. If thread-safe copying were to be supported, it should be implemented something like this:
  168. Lockable &operator=(const Lockable<T> &other)
  169. {
  170. if (this == &other)
  171. return *this;
  172. this->Lock();
  173. value = other.Lock();
  174. other.Unlock();
  175. this->Unlock();
  176. }
  177. */
  178. T &LockGet()
  179. {
  180. #ifdef KNET_USE_BOOST
  181. boostMutex.lock();
  182. #elif defined(WIN32)
  183. EnterCriticalSection(&lockObject);
  184. #else
  185. pthread_mutex_lock(&mutex);
  186. #endif
  187. return value;
  188. }
  189. const T &LockGet() const
  190. {
  191. #ifdef KNET_USE_BOOST
  192. boostMutex.lock();
  193. #elif defined(WIN32)
  194. EnterCriticalSection(&lockObject);
  195. #else
  196. pthread_mutex_lock(&mutex);
  197. #endif
  198. return value;
  199. }
  200. void Unlock() const
  201. {
  202. #ifdef KNET_USE_BOOST
  203. boostMutex.unlock();
  204. #elif defined(WIN32)
  205. LeaveCriticalSection(&lockObject);
  206. #else
  207. pthread_mutex_unlock(&mutex);
  208. #endif
  209. }
  210. LockType Acquire()
  211. {
  212. return LockType(this);
  213. }
  214. ConstLockType Acquire() const
  215. {
  216. return ConstLockType(this);
  217. }
  218. /// Ignores the mutex guard and directly returns a reference to the locked value.
  219. /// Warning: This is unsafe for threading. Call only when the other threads accessing
  220. /// the data have been finished, or if you can guarantee by other means that the data
  221. /// will not be accessed.
  222. T &UnsafeGetValue()
  223. {
  224. return value;
  225. }
  226. /// Ignores the mutex guard and directly returns a reference to the locked value.
  227. /// Warning: This is unsafe for threading. Call only when the other threads accessing
  228. /// the data have been finished, or if you can guarantee by other means that the data
  229. /// will not be accessed.
  230. const T &UnsafeGetValue() const
  231. {
  232. return value;
  233. }
  234. #ifdef KNET_USE_BOOST
  235. mutable boost::recursive_mutex boostMutex;
  236. #elif defined(WIN32)
  237. mutable CRITICAL_SECTION lockObject;
  238. #else
  239. mutable pthread_mutex_t mutex;
  240. #endif
  241. private:
  242. T value;
  243. void operator=(const Lockable<T> &);
  244. Lockable(const Lockable<T> &);
  245. };
  246. } // ~kNet