as_thread.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /*
  2. AngelCode Scripting Library
  3. Copyright (c) 2003-2012 Andreas Jonsson
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any
  6. damages arising from the use of this software.
  7. Permission is granted to anyone to use this software for any
  8. purpose, including commercial applications, and to alter it and
  9. redistribute it freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you
  11. must not claim that you wrote the original software. If you use
  12. this software in a product, an acknowledgment in the product
  13. documentation would be appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and
  15. must not be misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source
  17. distribution.
  18. The original version of this library can be located at:
  19. http://www.angelcode.com/angelscript/
  20. Andreas Jonsson
  21. [email protected]
  22. */
  23. //
  24. // as_thread.cpp
  25. //
  26. // Functions for multi threading support
  27. //
  28. #include "as_config.h"
  29. #include "as_thread.h"
  30. #include "as_atomic.h"
  31. BEGIN_AS_NAMESPACE
  32. //=======================================================================
  33. // Singleton
  34. static asCThreadManager *threadManager = 0;
  35. //======================================================================
  36. // Global API functions
  37. extern "C"
  38. {
  39. AS_API int asThreadCleanup()
  40. {
  41. return asCThreadManager::CleanupLocalData();
  42. }
  43. AS_API void asPrepareMultithread()
  44. {
  45. asCThreadManager::Prepare();
  46. }
  47. AS_API void asUnprepareMultithread()
  48. {
  49. asCThreadManager::Unprepare();
  50. }
  51. AS_API void asAcquireExclusiveLock()
  52. {
  53. if( threadManager )
  54. ACQUIREEXCLUSIVE(threadManager->appRWLock);
  55. }
  56. AS_API void asReleaseExclusiveLock()
  57. {
  58. if( threadManager )
  59. RELEASEEXCLUSIVE(threadManager->appRWLock);
  60. }
  61. AS_API void asAcquireSharedLock()
  62. {
  63. if( threadManager )
  64. ACQUIRESHARED(threadManager->appRWLock);
  65. }
  66. AS_API void asReleaseSharedLock()
  67. {
  68. if( threadManager )
  69. RELEASESHARED(threadManager->appRWLock);
  70. }
  71. }
  72. //======================================================================
  73. asCThreadManager::asCThreadManager()
  74. {
  75. // We're already in the critical section when this function is called
  76. #ifdef AS_NO_THREADS
  77. tld = 0;
  78. #endif
  79. refCount = 1;
  80. }
  81. void asCThreadManager::Prepare()
  82. {
  83. // The critical section cannot be declared globally, as there is no
  84. // guarantee for the order in which global variables are initialized
  85. // or uninitialized.
  86. // For this reason it's not possible to prevent two threads from calling
  87. // AddRef at the same time, so there is a chance for a race condition here.
  88. // To avoid the race condition when the thread manager is first created,
  89. // the application must make sure to call the global asPrepareForMultiThread()
  90. // in the main thread before any other thread creates a script engine.
  91. if( threadManager == 0 )
  92. threadManager = asNEW(asCThreadManager);
  93. else
  94. {
  95. ENTERCRITICALSECTION(threadManager->criticalSection);
  96. threadManager->refCount++;
  97. LEAVECRITICALSECTION(threadManager->criticalSection);
  98. }
  99. }
  100. void asCThreadManager::Unprepare()
  101. {
  102. asASSERT(threadManager);
  103. if( threadManager == 0 )
  104. return;
  105. // It's necessary to protect this section so no
  106. // other thread attempts to call AddRef or Release
  107. // while clean up is in progress.
  108. ENTERCRITICALSECTION(threadManager->criticalSection);
  109. if( --threadManager->refCount == 0 )
  110. {
  111. // As the critical section will be destroyed together
  112. // with the thread manager we must first clear the global
  113. // variable in case a new thread manager needs to be created;
  114. asCThreadManager *mgr = threadManager;
  115. threadManager = 0;
  116. // Leave the critical section before it is destroyed
  117. LEAVECRITICALSECTION(mgr->criticalSection);
  118. asDELETE(mgr,asCThreadManager);
  119. }
  120. else
  121. LEAVECRITICALSECTION(threadManager->criticalSection);
  122. }
  123. asCThreadManager::~asCThreadManager()
  124. {
  125. #ifndef AS_NO_THREADS
  126. // Delete all thread local datas
  127. asSMapNode<asPWORD,asCThreadLocalData*> *cursor = 0;
  128. if( tldMap.MoveFirst(&cursor) )
  129. {
  130. do
  131. {
  132. if( tldMap.GetValue(cursor) )
  133. {
  134. asDELETE(tldMap.GetValue(cursor),asCThreadLocalData);
  135. }
  136. } while( tldMap.MoveNext(&cursor, cursor) );
  137. }
  138. #else
  139. if( tld )
  140. {
  141. asDELETE(tld,asCThreadLocalData);
  142. }
  143. tld = 0;
  144. #endif
  145. }
  146. int asCThreadManager::CleanupLocalData()
  147. {
  148. if( threadManager == 0 )
  149. return 0;
  150. #ifndef AS_NO_THREADS
  151. int r = 0;
  152. #if defined AS_POSIX_THREADS
  153. asPWORD id = (asPWORD)pthread_self();
  154. #elif defined AS_WINDOWS_THREADS
  155. asPWORD id = (asPWORD)GetCurrentThreadId();
  156. #endif
  157. ENTERCRITICALSECTION(threadManager->criticalSection);
  158. asSMapNode<asPWORD,asCThreadLocalData*> *cursor = 0;
  159. if( threadManager->tldMap.MoveTo(&cursor, id) )
  160. {
  161. asCThreadLocalData *tld = threadManager->tldMap.GetValue(cursor);
  162. // Can we really remove it at this time?
  163. if( tld->activeContexts.GetLength() == 0 )
  164. {
  165. asDELETE(tld,asCThreadLocalData);
  166. threadManager->tldMap.Erase(cursor);
  167. r = 0;
  168. }
  169. else
  170. r = asCONTEXT_ACTIVE;
  171. }
  172. LEAVECRITICALSECTION(threadManager->criticalSection);
  173. return r;
  174. #else
  175. if( threadManager->tld )
  176. {
  177. if( threadManager->tld->activeContexts.GetLength() == 0 )
  178. {
  179. asDELETE(threadManager->tld,asCThreadLocalData);
  180. threadManager->tld = 0;
  181. }
  182. else
  183. return asCONTEXT_ACTIVE;
  184. }
  185. return 0;
  186. #endif
  187. }
  188. #ifndef AS_NO_THREADS
  189. asCThreadLocalData *asCThreadManager::GetLocalData(asPWORD threadId)
  190. {
  191. // We're already in the critical section when this function is called
  192. asCThreadLocalData *tld = 0;
  193. asSMapNode<asPWORD,asCThreadLocalData*> *cursor = 0;
  194. if( threadManager->tldMap.MoveTo(&cursor, threadId) )
  195. tld = threadManager->tldMap.GetValue(cursor);
  196. return tld;
  197. }
  198. void asCThreadManager::SetLocalData(asPWORD threadId, asCThreadLocalData *tld)
  199. {
  200. // We're already in the critical section when this function is called
  201. tldMap.Insert(threadId, tld);
  202. }
  203. #endif
  204. asCThreadLocalData *asCThreadManager::GetLocalData()
  205. {
  206. if( threadManager == 0 )
  207. return 0;
  208. #ifndef AS_NO_THREADS
  209. #if defined AS_POSIX_THREADS
  210. asPWORD id = (asPWORD)pthread_self();
  211. #elif defined AS_WINDOWS_THREADS
  212. asPWORD id = (asPWORD)GetCurrentThreadId();
  213. #endif
  214. ENTERCRITICALSECTION(threadManager->criticalSection);
  215. asCThreadLocalData *tld = threadManager->GetLocalData(id);
  216. if( tld == 0 )
  217. {
  218. // Create a new tld
  219. tld = asNEW(asCThreadLocalData)();
  220. if( tld )
  221. threadManager->SetLocalData(id, tld);
  222. }
  223. LEAVECRITICALSECTION(threadManager->criticalSection);
  224. return tld;
  225. #else
  226. if( threadManager->tld == 0 )
  227. threadManager->tld = asNEW(asCThreadLocalData)();
  228. return threadManager->tld;
  229. #endif
  230. }
  231. //=========================================================================
  232. asCThreadLocalData::asCThreadLocalData()
  233. {
  234. }
  235. asCThreadLocalData::~asCThreadLocalData()
  236. {
  237. }
  238. //=========================================================================
  239. #ifndef AS_NO_THREADS
  240. asCThreadCriticalSection::asCThreadCriticalSection()
  241. {
  242. #if defined AS_POSIX_THREADS
  243. pthread_mutex_init(&cs, 0);
  244. #elif defined AS_WINDOWS_THREADS
  245. InitializeCriticalSection(&cs);
  246. #endif
  247. }
  248. asCThreadCriticalSection::~asCThreadCriticalSection()
  249. {
  250. #if defined AS_POSIX_THREADS
  251. pthread_mutex_destroy(&cs);
  252. #elif defined AS_WINDOWS_THREADS
  253. DeleteCriticalSection(&cs);
  254. #endif
  255. }
  256. void asCThreadCriticalSection::Enter()
  257. {
  258. #if defined AS_POSIX_THREADS
  259. pthread_mutex_lock(&cs);
  260. #elif defined AS_WINDOWS_THREADS
  261. EnterCriticalSection(&cs);
  262. #endif
  263. }
  264. void asCThreadCriticalSection::Leave()
  265. {
  266. #if defined AS_POSIX_THREADS
  267. pthread_mutex_unlock(&cs);
  268. #elif defined AS_WINDOWS_THREADS
  269. LeaveCriticalSection(&cs);
  270. #endif
  271. }
  272. bool asCThreadCriticalSection::TryEnter()
  273. {
  274. #if defined AS_POSIX_THREADS
  275. return !pthread_mutex_trylock(&cs);
  276. #elif defined AS_WINDOWS_THREADS
  277. return TryEnterCriticalSection(&cs) ? true : false;
  278. #else
  279. return true;
  280. #endif
  281. }
  282. asCThreadReadWriteLock::asCThreadReadWriteLock()
  283. {
  284. #if defined AS_POSIX_THREADS
  285. int r = pthread_rwlock_init(&lock, 0);
  286. asASSERT( r == 0 );
  287. #elif defined AS_WINDOWS_THREADS
  288. // Create a semaphore to allow up to maxReaders simultaneous readers
  289. readLocks = CreateSemaphore(NULL, maxReaders, maxReaders, 0);
  290. // Create a critical section to synchronize writers
  291. InitializeCriticalSection(&writeLock);
  292. #endif
  293. }
  294. asCThreadReadWriteLock::~asCThreadReadWriteLock()
  295. {
  296. #if defined AS_POSIX_THREADS
  297. pthread_rwlock_destroy(&lock);
  298. #elif defined AS_WINDOWS_THREADS
  299. DeleteCriticalSection(&writeLock);
  300. CloseHandle(readLocks);
  301. #endif
  302. }
  303. void asCThreadReadWriteLock::AcquireExclusive()
  304. {
  305. #if defined AS_POSIX_THREADS
  306. pthread_rwlock_wrlock(&lock);
  307. #elif defined AS_WINDOWS_THREADS
  308. // Synchronize writers, so only one tries to lock out the readers
  309. EnterCriticalSection(&writeLock);
  310. // Lock all reader out from the semaphore. Do this one by one,
  311. // so the lock doesn't have to wait until there are no readers at all.
  312. // If we try to lock all at once it is quite possible the writer will
  313. // never succeed.
  314. for( asUINT n = 0; n < maxReaders; n++ )
  315. WaitForSingleObject(readLocks, INFINITE);
  316. // Allow another writer to lock. It will only be able to
  317. // lock the readers when this writer releases them anyway.
  318. LeaveCriticalSection(&writeLock);
  319. #endif
  320. }
  321. void asCThreadReadWriteLock::ReleaseExclusive()
  322. {
  323. #if defined AS_POSIX_THREADS
  324. pthread_rwlock_unlock(&lock);
  325. #elif defined AS_WINDOWS_THREADS
  326. // Release all readers at once
  327. ReleaseSemaphore(readLocks, maxReaders, 0);
  328. #endif
  329. }
  330. void asCThreadReadWriteLock::AcquireShared()
  331. {
  332. #if defined AS_POSIX_THREADS
  333. pthread_rwlock_rdlock(&lock);
  334. #elif defined AS_WINDOWS_THREADS
  335. // Lock a reader slot
  336. WaitForSingleObject(readLocks, INFINITE);
  337. #endif
  338. }
  339. void asCThreadReadWriteLock::ReleaseShared()
  340. {
  341. #if defined AS_POSIX_THREADS
  342. pthread_rwlock_unlock(&lock);
  343. #elif defined AS_WINDOWS_THREADS
  344. // Release the reader slot
  345. ReleaseSemaphore(readLocks, 1, 0);
  346. #endif
  347. }
  348. #endif
  349. //========================================================================
  350. END_AS_NAMESPACE