1 /*
2 AngelCode Scripting Library
3 Copyright (c) 2003-2014 Andreas Jonsson
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any
7 damages arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any
10 purpose, including commercial applications, and to alter it and
11 redistribute it freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you
14 must not claim that you wrote the original software. If you use
15 this software in a product, an acknowledgment in the product
16 documentation would be appreciated but is not required.
17
18 2. Altered source versions must be plainly marked as such, and
19 must not be misrepresented as being the original software.
20
21 3. This notice may not be removed or altered from any source
22 distribution.
23
24 The original version of this library can be located at:
25 http://www.angelcode.com/angelscript/
26
27 Andreas Jonsson
28 andreas@angelcode.com
29 */
30
31
32
33 //
34 // as_thread.cpp
35 //
36 // Functions for multi threading support
37 //
38
39 #include "as_config.h"
40 #include "as_thread.h"
41 #include "as_atomic.h"
42
43 BEGIN_AS_NAMESPACE
44
45 //=======================================================================
46
47 // Singleton
48 static asCThreadManager *threadManager = 0;
49
50 //======================================================================
51
52 // Global API functions
53 extern "C"
54 {
55
asThreadCleanup()56 AS_API int asThreadCleanup()
57 {
58 return asCThreadManager::CleanupLocalData();
59 }
60
asGetThreadManager()61 AS_API asIThreadManager *asGetThreadManager()
62 {
63 return threadManager;
64 }
65
asPrepareMultithread(asIThreadManager * externalThreadMgr)66 AS_API int asPrepareMultithread(asIThreadManager *externalThreadMgr)
67 {
68 return asCThreadManager::Prepare(externalThreadMgr);
69 }
70
asUnprepareMultithread()71 AS_API void asUnprepareMultithread()
72 {
73 asCThreadManager::Unprepare();
74 }
75
asAcquireExclusiveLock()76 AS_API void asAcquireExclusiveLock()
77 {
78 if( threadManager )
79 {
80 ACQUIREEXCLUSIVE(threadManager->appRWLock);
81 }
82 }
83
asReleaseExclusiveLock()84 AS_API void asReleaseExclusiveLock()
85 {
86 if( threadManager )
87 {
88 RELEASEEXCLUSIVE(threadManager->appRWLock);
89 }
90 }
91
asAcquireSharedLock()92 AS_API void asAcquireSharedLock()
93 {
94 if( threadManager )
95 {
96 ACQUIRESHARED(threadManager->appRWLock);
97 }
98 }
99
asReleaseSharedLock()100 AS_API void asReleaseSharedLock()
101 {
102 if( threadManager )
103 {
104 RELEASESHARED(threadManager->appRWLock);
105 }
106 }
107
108 }
109
110 //======================================================================
111
112 #if !defined(AS_NO_THREADS) && defined(_MSC_VER) && defined(AS_WINDOWS_THREADS) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
113 __declspec(thread) asCThreadLocalData *asCThreadManager::tld = 0;
114 #endif
115
asCThreadManager()116 asCThreadManager::asCThreadManager()
117 {
118 // We're already in the critical section when this function is called
119
120 #ifdef AS_NO_THREADS
121 tld = 0;
122 #else
123 // Allocate the thread local storage
124 #if defined AS_POSIX_THREADS
125 pthread_key_t pKey;
126 pthread_key_create(&pKey, 0);
127 tlsKey = (asDWORD)pKey;
128 #elif defined AS_WINDOWS_THREADS
129 #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
130 tld = 0;
131 #else
132 tlsKey = (asDWORD)TlsAlloc();
133 #endif
134 #endif
135 #endif
136 refCount = 1;
137 }
138
Prepare(asIThreadManager * externalThreadMgr)139 int asCThreadManager::Prepare(asIThreadManager *externalThreadMgr)
140 {
141 // Don't allow an external thread manager if there
142 // is already a thread manager defined
143 if( externalThreadMgr && threadManager )
144 return asINVALID_ARG;
145
146 // The critical section cannot be declared globally, as there is no
147 // guarantee for the order in which global variables are initialized
148 // or uninitialized.
149
150 // For this reason it's not possible to prevent two threads from calling
151 // AddRef at the same time, so there is a chance for a race condition here.
152
153 // To avoid the race condition when the thread manager is first created,
154 // the application must make sure to call the global asPrepareForMultiThread()
155 // in the main thread before any other thread creates a script engine.
156 if( threadManager == 0 && externalThreadMgr == 0 )
157 threadManager = asNEW(asCThreadManager);
158 else
159 {
160 // If an application uses different dlls each dll will get it's own memory
161 // space for global variables. If multiple dlls then uses AngelScript's
162 // global thread support functions it is then best to share the thread
163 // manager to make sure all dlls use the same critical section.
164 if( externalThreadMgr )
165 threadManager = reinterpret_cast<asCThreadManager*>(externalThreadMgr);
166
167 ENTERCRITICALSECTION(threadManager->criticalSection);
168 threadManager->refCount++;
169 LEAVECRITICALSECTION(threadManager->criticalSection);
170 }
171
172 // Success
173 return 0;
174 }
175
Unprepare()176 void asCThreadManager::Unprepare()
177 {
178 asASSERT(threadManager);
179
180 if( threadManager == 0 )
181 return;
182
183 // It's necessary to protect this section so no
184 // other thread attempts to call AddRef or Release
185 // while clean up is in progress.
186 ENTERCRITICALSECTION(threadManager->criticalSection);
187 if( --threadManager->refCount == 0 )
188 {
189 // Make sure the local data is destroyed, at least for the current thread
190 CleanupLocalData();
191
192 // As the critical section will be destroyed together
193 // with the thread manager we must first clear the global
194 // variable in case a new thread manager needs to be created;
195 asCThreadManager *mgr = threadManager;
196 threadManager = 0;
197
198 // Leave the critical section before it is destroyed
199 LEAVECRITICALSECTION(mgr->criticalSection);
200
201 asDELETE(mgr,asCThreadManager);
202 }
203 else
204 LEAVECRITICALSECTION(threadManager->criticalSection);
205 }
206
~asCThreadManager()207 asCThreadManager::~asCThreadManager()
208 {
209 #ifndef AS_NO_THREADS
210 // Deallocate the thread local storage
211 #if defined AS_POSIX_THREADS
212 pthread_key_delete((pthread_key_t)tlsKey);
213 #elif defined AS_WINDOWS_THREADS
214 #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
215 tld = 0;
216 #else
217 TlsFree((DWORD)tlsKey);
218 #endif
219 #endif
220 #else
221 if( tld )
222 {
223 asDELETE(tld,asCThreadLocalData);
224 }
225 tld = 0;
226 #endif
227 }
228
CleanupLocalData()229 int asCThreadManager::CleanupLocalData()
230 {
231 if( threadManager == 0 )
232 return 0;
233
234 #ifndef AS_NO_THREADS
235 #if defined AS_POSIX_THREADS
236 asCThreadLocalData *tld = (asCThreadLocalData*)pthread_getspecific((pthread_key_t)threadManager->tlsKey);
237 #elif defined AS_WINDOWS_THREADS
238 #if !defined(_MSC_VER) || !(WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
239 asCThreadLocalData *tld = (asCThreadLocalData*)TlsGetValue((DWORD)threadManager->tlsKey);
240 #endif
241 #endif
242
243 if( tld == 0 )
244 return 0;
245
246 if( tld->activeContexts.GetLength() == 0 )
247 {
248 asDELETE(tld,asCThreadLocalData);
249 #if defined AS_POSIX_THREADS
250 pthread_setspecific((pthread_key_t)threadManager->tlsKey, 0);
251 #elif defined AS_WINDOWS_THREADS
252 #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
253 tld = 0;
254 #else
255 TlsSetValue((DWORD)threadManager->tlsKey, 0);
256 #endif
257 #endif
258 return 0;
259 }
260 else
261 return asCONTEXT_ACTIVE;
262
263 #else
264 if( threadManager->tld )
265 {
266 if( threadManager->tld->activeContexts.GetLength() == 0 )
267 {
268 asDELETE(threadManager->tld,asCThreadLocalData);
269 threadManager->tld = 0;
270 }
271 else
272 return asCONTEXT_ACTIVE;
273 }
274 return 0;
275 #endif
276 }
277
GetLocalData()278 asCThreadLocalData *asCThreadManager::GetLocalData()
279 {
280 if( threadManager == 0 )
281 return 0;
282
283 #ifndef AS_NO_THREADS
284 #if defined AS_POSIX_THREADS
285 asCThreadLocalData *tld = (asCThreadLocalData*)pthread_getspecific((pthread_key_t)threadManager->tlsKey);
286 if( tld == 0 )
287 {
288 tld = asNEW(asCThreadLocalData)();
289 pthread_setspecific((pthread_key_t)threadManager->tlsKey, tld);
290 }
291 #elif defined AS_WINDOWS_THREADS
292 #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
293 if( tld == 0 )
294 tld = asNEW(asCThreadLocalData)();
295 #else
296 asCThreadLocalData *tld = (asCThreadLocalData*)TlsGetValue((DWORD)threadManager->tlsKey);
297 if( tld == 0 )
298 {
299 tld = asNEW(asCThreadLocalData)();
300 TlsSetValue((DWORD)threadManager->tlsKey, tld);
301 }
302 #endif
303 #endif
304
305 return tld;
306 #else
307 if( threadManager->tld == 0 )
308 threadManager->tld = asNEW(asCThreadLocalData)();
309
310 return threadManager->tld;
311 #endif
312 }
313
314 //=========================================================================
315
asCThreadLocalData()316 asCThreadLocalData::asCThreadLocalData()
317 {
318 }
319
~asCThreadLocalData()320 asCThreadLocalData::~asCThreadLocalData()
321 {
322 }
323
324 //=========================================================================
325
326 #ifndef AS_NO_THREADS
asCThreadCriticalSection()327 asCThreadCriticalSection::asCThreadCriticalSection()
328 {
329 #if defined AS_POSIX_THREADS
330 pthread_mutex_init(&cs, 0);
331 #elif defined AS_WINDOWS_THREADS
332 #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
333 // Only the Ex version is available on Windows Store
334 InitializeCriticalSectionEx(&cs, 4000, 0);
335 #else
336 // Only the non-Ex version is available on WinXP and older
337 // MinGW also only defines this version
338 InitializeCriticalSection(&cs);
339 #endif
340 #endif
341 }
342
~asCThreadCriticalSection()343 asCThreadCriticalSection::~asCThreadCriticalSection()
344 {
345 #if defined AS_POSIX_THREADS
346 pthread_mutex_destroy(&cs);
347 #elif defined AS_WINDOWS_THREADS
348 DeleteCriticalSection(&cs);
349 #endif
350 }
351
Enter()352 void asCThreadCriticalSection::Enter()
353 {
354 #if defined AS_POSIX_THREADS
355 pthread_mutex_lock(&cs);
356 #elif defined AS_WINDOWS_THREADS
357 EnterCriticalSection(&cs);
358 #endif
359 }
360
Leave()361 void asCThreadCriticalSection::Leave()
362 {
363 #if defined AS_POSIX_THREADS
364 pthread_mutex_unlock(&cs);
365 #elif defined AS_WINDOWS_THREADS
366 LeaveCriticalSection(&cs);
367 #endif
368 }
369
TryEnter()370 bool asCThreadCriticalSection::TryEnter()
371 {
372 #if defined AS_POSIX_THREADS
373 return !pthread_mutex_trylock(&cs);
374 #elif defined AS_WINDOWS_THREADS
375 return TryEnterCriticalSection(&cs) ? true : false;
376 #else
377 return true;
378 #endif
379 }
380
asCThreadReadWriteLock()381 asCThreadReadWriteLock::asCThreadReadWriteLock()
382 {
383 #if defined AS_POSIX_THREADS
384 int r = pthread_rwlock_init(&lock, 0);
385 asASSERT( r == 0 );
386 UNUSED_VAR(r);
387 #elif defined AS_WINDOWS_THREADS
388 #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
389 // Only the Ex versions are available on Windows Store
390
391 // Create a semaphore to allow up to maxReaders simultaneous readers
392 readLocks = CreateSemaphoreExW(NULL, maxReaders, maxReaders, 0, 0, 0);
393 // Create a critical section to synchronize writers
394 InitializeCriticalSectionEx(&writeLock, 4000, 0);
395 #else
396 readLocks = CreateSemaphoreW(NULL, maxReaders, maxReaders, 0);
397 InitializeCriticalSection(&writeLock);
398 #endif
399 #endif
400 }
401
~asCThreadReadWriteLock()402 asCThreadReadWriteLock::~asCThreadReadWriteLock()
403 {
404 #if defined AS_POSIX_THREADS
405 pthread_rwlock_destroy(&lock);
406 #elif defined AS_WINDOWS_THREADS
407 DeleteCriticalSection(&writeLock);
408 CloseHandle(readLocks);
409 #endif
410 }
411
AcquireExclusive()412 void asCThreadReadWriteLock::AcquireExclusive()
413 {
414 #if defined AS_POSIX_THREADS
415 pthread_rwlock_wrlock(&lock);
416 #elif defined AS_WINDOWS_THREADS
417 // Synchronize writers, so only one tries to lock out the readers
418 EnterCriticalSection(&writeLock);
419
420 // Lock all reader out from the semaphore. Do this one by one,
421 // so the lock doesn't have to wait until there are no readers at all.
422 // If we try to lock all at once it is quite possible the writer will
423 // never succeed.
424 for( asUINT n = 0; n < maxReaders; n++ )
425 WaitForSingleObjectEx(readLocks, INFINITE, FALSE);
426
427 // Allow another writer to lock. It will only be able to
428 // lock the readers when this writer releases them anyway.
429 LeaveCriticalSection(&writeLock);
430 #endif
431 }
432
ReleaseExclusive()433 void asCThreadReadWriteLock::ReleaseExclusive()
434 {
435 #if defined AS_POSIX_THREADS
436 pthread_rwlock_unlock(&lock);
437 #elif defined AS_WINDOWS_THREADS
438 // Release all readers at once
439 ReleaseSemaphore(readLocks, maxReaders, 0);
440 #endif
441 }
442
AcquireShared()443 void asCThreadReadWriteLock::AcquireShared()
444 {
445 #if defined AS_POSIX_THREADS
446 pthread_rwlock_rdlock(&lock);
447 #elif defined AS_WINDOWS_THREADS
448 // Lock a reader slot
449 WaitForSingleObjectEx(readLocks, INFINITE, FALSE);
450 #endif
451 }
452
ReleaseShared()453 void asCThreadReadWriteLock::ReleaseShared()
454 {
455 #if defined AS_POSIX_THREADS
456 pthread_rwlock_unlock(&lock);
457 #elif defined AS_WINDOWS_THREADS
458 // Release the reader slot
459 ReleaseSemaphore(readLocks, 1, 0);
460 #endif
461 }
462
463 #endif
464
465 //========================================================================
466
467 END_AS_NAMESPACE
468
469