Lockable.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  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. return *this; // Urho3D
  55. }
  56. ~Lock()
  57. {
  58. Unlock();
  59. }
  60. void Unlock()
  61. {
  62. if (lockedObject)
  63. {
  64. lockedObject->Unlock();
  65. lockedObject = 0;
  66. }
  67. }
  68. T *operator ->() const { return value; }
  69. T &operator *() { return *value; }
  70. private:
  71. mutable Lockable<T> *lockedObject;
  72. mutable T *value;
  73. /// Clears the pointer to the object this Lock points to. This function is const to allow std::auto_ptr -like
  74. /// lock ownership passing in copy-ctor.
  75. void TearDown() const { lockedObject = 0; value = 0; }
  76. };
  77. /// @internal Wraps mutex-lock acquisition and releasing to const data into a RAII-style object
  78. /// that automatically releases the lock when the scope is exited.
  79. template<typename T>
  80. class ConstLock
  81. {
  82. public:
  83. explicit ConstLock(const Lockable<T> *lockedObject_)
  84. :lockedObject(lockedObject_), value(&lockedObject->LockGet())
  85. {
  86. }
  87. ConstLock(const ConstLock<T> &rhs)
  88. :lockedObject(rhs.lockedObject), value(rhs.value)
  89. {
  90. assert(this != &rhs);
  91. rhs.TearDown();
  92. }
  93. ConstLock<T> &operator=(const ConstLock<T> &rhs)
  94. {
  95. if (&rhs == this)
  96. return *this;
  97. lockedObject = rhs.lockedObject();
  98. value = rhs.value;
  99. rhs.TearDown();
  100. return *this; // Urho3D
  101. }
  102. ~ConstLock()
  103. {
  104. if (lockedObject)
  105. lockedObject->Unlock();
  106. }
  107. const T *operator ->() const { return value; }
  108. const T &operator *() const { return *value; }
  109. private:
  110. mutable const Lockable<T> *lockedObject;
  111. mutable const T *value;
  112. /// Clears the pointer to the object this Lock points to. This function is const to allow std::auto_ptr -like
  113. /// lock ownership passing in copy-ctor.
  114. void TearDown() const { lockedObject = 0; value = 0; }
  115. };
  116. /// 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
  117. /// to free the lock when done. Use @see Lock and @see ConstLock to manage the locks in a RAII-style manner.
  118. template<typename T>
  119. class Lockable
  120. {
  121. public:
  122. typedef Lock<T> LockType;
  123. typedef ConstLock<T> ConstLockType;
  124. Lockable()
  125. {
  126. #ifndef KNET_USE_BOOST
  127. #ifdef WIN32
  128. InitializeCriticalSection(&lockObject);
  129. #else
  130. pthread_mutexattr_t attr;
  131. pthread_mutexattr_init(&attr);
  132. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  133. pthread_mutex_init(&mutex, &attr);
  134. #endif
  135. #endif
  136. }
  137. /* Lockable objects are noncopyable. If thread-safe copying were to be supported, it should be implemented something like this:
  138. Lockable(const Lockable<T> &other)
  139. {
  140. InitializeCriticalSection(&lockObject);
  141. value = other.Lock();
  142. other.Unlock();
  143. }
  144. */
  145. explicit Lockable(const T &value_)
  146. :value(value_)
  147. {
  148. #ifndef KNET_USE_BOOST
  149. #ifdef WIN32
  150. InitializeCriticalSection(&lockObject);
  151. #else
  152. pthread_mutexattr_t attr;
  153. pthread_mutexattr_init(&attr);
  154. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  155. pthread_mutex_init(&mutex, &attr);
  156. #endif
  157. #endif
  158. }
  159. ~Lockable()
  160. {
  161. #ifndef KNET_USE_BOOST
  162. #ifdef WIN32
  163. DeleteCriticalSection(&lockObject);
  164. #else
  165. pthread_mutex_destroy(&mutex);
  166. #endif
  167. #endif
  168. }
  169. /* Lockable objects are nonassignable. If thread-safe copying were to be supported, it should be implemented something like this:
  170. Lockable &operator=(const Lockable<T> &other)
  171. {
  172. if (this == &other)
  173. return *this;
  174. this->Lock();
  175. value = other.Lock();
  176. other.Unlock();
  177. this->Unlock();
  178. }
  179. */
  180. T &LockGet()
  181. {
  182. #ifdef KNET_USE_BOOST
  183. boostMutex.lock();
  184. #elif defined(WIN32)
  185. EnterCriticalSection(&lockObject);
  186. #else
  187. pthread_mutex_lock(&mutex);
  188. #endif
  189. return value;
  190. }
  191. const T &LockGet() const
  192. {
  193. #ifdef KNET_USE_BOOST
  194. boostMutex.lock();
  195. #elif defined(WIN32)
  196. EnterCriticalSection(&lockObject);
  197. #else
  198. pthread_mutex_lock(&mutex);
  199. #endif
  200. return value;
  201. }
  202. void Unlock() const
  203. {
  204. #ifdef KNET_USE_BOOST
  205. boostMutex.unlock();
  206. #elif defined(WIN32)
  207. LeaveCriticalSection(&lockObject);
  208. #else
  209. pthread_mutex_unlock(&mutex);
  210. #endif
  211. }
  212. LockType Acquire()
  213. {
  214. return LockType(this);
  215. }
  216. ConstLockType Acquire() const
  217. {
  218. return ConstLockType(this);
  219. }
  220. /// Ignores the mutex guard and directly returns a reference to the locked value.
  221. /// Warning: This is unsafe for threading. Call only when the other threads accessing
  222. /// the data have been finished, or if you can guarantee by other means that the data
  223. /// will not be accessed.
  224. T &UnsafeGetValue()
  225. {
  226. return value;
  227. }
  228. /// Ignores the mutex guard and directly returns a reference to the locked value.
  229. /// Warning: This is unsafe for threading. Call only when the other threads accessing
  230. /// the data have been finished, or if you can guarantee by other means that the data
  231. /// will not be accessed.
  232. const T &UnsafeGetValue() const
  233. {
  234. return value;
  235. }
  236. #ifdef KNET_USE_BOOST
  237. mutable boost::recursive_mutex boostMutex;
  238. #elif defined(WIN32)
  239. mutable CRITICAL_SECTION lockObject;
  240. #else
  241. mutable pthread_mutex_t mutex;
  242. #endif
  243. private:
  244. T value;
  245. void operator=(const Lockable<T> &);
  246. Lockable(const Lockable<T> &);
  247. };
  248. } // ~kNet