123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- /*
- AngelCode Scripting Library
- Copyright (c) 2003-2014 Andreas Jonsson
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any
- damages arising from the use of this software.
- Permission is granted to anyone to use this software for any
- purpose, including commercial applications, and to alter it and
- redistribute it freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you
- must not claim that you wrote the original software. If you use
- this software in a product, an acknowledgment in the product
- documentation would be appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and
- must not be misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source
- distribution.
- The original version of this library can be located at:
- http://www.angelcode.com/angelscript/
- Andreas Jonsson
- andreas@angelcode.com
- */
- //
- // as_thread.cpp
- //
- // Functions for multi threading support
- //
- #include "as_config.h"
- #include "as_thread.h"
- #include "as_atomic.h"
- BEGIN_AS_NAMESPACE
- //=======================================================================
- // Singleton
- static asCThreadManager *threadManager = 0;
- //======================================================================
- // Global API functions
- extern "C"
- {
- AS_API int asThreadCleanup()
- {
- return asCThreadManager::CleanupLocalData();
- }
- AS_API asIThreadManager *asGetThreadManager()
- {
- return threadManager;
- }
- AS_API int asPrepareMultithread(asIThreadManager *externalThreadMgr)
- {
- return asCThreadManager::Prepare(externalThreadMgr);
- }
- AS_API void asUnprepareMultithread()
- {
- asCThreadManager::Unprepare();
- }
- AS_API void asAcquireExclusiveLock()
- {
- if( threadManager )
- {
- ACQUIREEXCLUSIVE(threadManager->appRWLock);
- }
- }
- AS_API void asReleaseExclusiveLock()
- {
- if( threadManager )
- {
- RELEASEEXCLUSIVE(threadManager->appRWLock);
- }
- }
- AS_API void asAcquireSharedLock()
- {
- if( threadManager )
- {
- ACQUIRESHARED(threadManager->appRWLock);
- }
- }
- AS_API void asReleaseSharedLock()
- {
- if( threadManager )
- {
- RELEASESHARED(threadManager->appRWLock);
- }
- }
- }
- //======================================================================
- #if !defined(AS_NO_THREADS) && defined(_MSC_VER) && defined(AS_WINDOWS_THREADS) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
- __declspec(thread) asCThreadLocalData *asCThreadManager::tld = 0;
- #endif
- asCThreadManager::asCThreadManager()
- {
- // We're already in the critical section when this function is called
- #ifdef AS_NO_THREADS
- tld = 0;
- #else
- // Allocate the thread local storage
- #if defined AS_POSIX_THREADS
- pthread_key_t pKey;
- pthread_key_create(&pKey, 0);
- tlsKey = (asDWORD)pKey;
- #elif defined AS_WINDOWS_THREADS
- #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
- tld = 0;
- #else
- tlsKey = (asDWORD)TlsAlloc();
- #endif
- #endif
- #endif
- refCount = 1;
- }
- int asCThreadManager::Prepare(asIThreadManager *externalThreadMgr)
- {
- // Don't allow an external thread manager if there
- // is already a thread manager defined
- if( externalThreadMgr && threadManager )
- return asINVALID_ARG;
- // The critical section cannot be declared globally, as there is no
- // guarantee for the order in which global variables are initialized
- // or uninitialized.
- // For this reason it's not possible to prevent two threads from calling
- // AddRef at the same time, so there is a chance for a race condition here.
- // To avoid the race condition when the thread manager is first created,
- // the application must make sure to call the global asPrepareForMultiThread()
- // in the main thread before any other thread creates a script engine.
- if( threadManager == 0 && externalThreadMgr == 0 )
- threadManager = asNEW(asCThreadManager);
- else
- {
- // If an application uses different dlls each dll will get it's own memory
- // space for global variables. If multiple dlls then uses AngelScript's
- // global thread support functions it is then best to share the thread
- // manager to make sure all dlls use the same critical section.
- if( externalThreadMgr )
- threadManager = reinterpret_cast<asCThreadManager*>(externalThreadMgr);
- ENTERCRITICALSECTION(threadManager->criticalSection);
- threadManager->refCount++;
- LEAVECRITICALSECTION(threadManager->criticalSection);
- }
- // Success
- return 0;
- }
- void asCThreadManager::Unprepare()
- {
- asASSERT(threadManager);
- if( threadManager == 0 )
- return;
- // It's necessary to protect this section so no
- // other thread attempts to call AddRef or Release
- // while clean up is in progress.
- ENTERCRITICALSECTION(threadManager->criticalSection);
- if( --threadManager->refCount == 0 )
- {
- // Make sure the local data is destroyed, at least for the current thread
- CleanupLocalData();
- // As the critical section will be destroyed together
- // with the thread manager we must first clear the global
- // variable in case a new thread manager needs to be created;
- asCThreadManager *mgr = threadManager;
- threadManager = 0;
- // Leave the critical section before it is destroyed
- LEAVECRITICALSECTION(mgr->criticalSection);
- asDELETE(mgr,asCThreadManager);
- }
- else
- LEAVECRITICALSECTION(threadManager->criticalSection);
- }
- asCThreadManager::~asCThreadManager()
- {
- #ifndef AS_NO_THREADS
- // Deallocate the thread local storage
- #if defined AS_POSIX_THREADS
- pthread_key_delete((pthread_key_t)tlsKey);
- #elif defined AS_WINDOWS_THREADS
- #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
- tld = 0;
- #else
- TlsFree((DWORD)tlsKey);
- #endif
- #endif
- #else
- if( tld )
- {
- asDELETE(tld,asCThreadLocalData);
- }
- tld = 0;
- #endif
- }
- int asCThreadManager::CleanupLocalData()
- {
- if( threadManager == 0 )
- return 0;
- #ifndef AS_NO_THREADS
- #if defined AS_POSIX_THREADS
- asCThreadLocalData *tld = (asCThreadLocalData*)pthread_getspecific((pthread_key_t)threadManager->tlsKey);
- #elif defined AS_WINDOWS_THREADS
- #if !defined(_MSC_VER) || !(WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
- asCThreadLocalData *tld = (asCThreadLocalData*)TlsGetValue((DWORD)threadManager->tlsKey);
- #endif
- #endif
- if( tld == 0 )
- return 0;
- if( tld->activeContexts.GetLength() == 0 )
- {
- asDELETE(tld,asCThreadLocalData);
- #if defined AS_POSIX_THREADS
- pthread_setspecific((pthread_key_t)threadManager->tlsKey, 0);
- #elif defined AS_WINDOWS_THREADS
- #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
- tld = 0;
- #else
- TlsSetValue((DWORD)threadManager->tlsKey, 0);
- #endif
- #endif
- return 0;
- }
- else
- return asCONTEXT_ACTIVE;
- #else
- if( threadManager->tld )
- {
- if( threadManager->tld->activeContexts.GetLength() == 0 )
- {
- asDELETE(threadManager->tld,asCThreadLocalData);
- threadManager->tld = 0;
- }
- else
- return asCONTEXT_ACTIVE;
- }
- return 0;
- #endif
- }
- asCThreadLocalData *asCThreadManager::GetLocalData()
- {
- if( threadManager == 0 )
- return 0;
- #ifndef AS_NO_THREADS
- #if defined AS_POSIX_THREADS
- asCThreadLocalData *tld = (asCThreadLocalData*)pthread_getspecific((pthread_key_t)threadManager->tlsKey);
- if( tld == 0 )
- {
- tld = asNEW(asCThreadLocalData)();
- pthread_setspecific((pthread_key_t)threadManager->tlsKey, tld);
- }
- #elif defined AS_WINDOWS_THREADS
- #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
- if( tld == 0 )
- tld = asNEW(asCThreadLocalData)();
- #else
- asCThreadLocalData *tld = (asCThreadLocalData*)TlsGetValue((DWORD)threadManager->tlsKey);
- if( tld == 0 )
- {
- tld = asNEW(asCThreadLocalData)();
- TlsSetValue((DWORD)threadManager->tlsKey, tld);
- }
- #endif
- #endif
- return tld;
- #else
- if( threadManager->tld == 0 )
- threadManager->tld = asNEW(asCThreadLocalData)();
- return threadManager->tld;
- #endif
- }
- //=========================================================================
- asCThreadLocalData::asCThreadLocalData()
- {
- }
- asCThreadLocalData::~asCThreadLocalData()
- {
- }
- //=========================================================================
- #ifndef AS_NO_THREADS
- asCThreadCriticalSection::asCThreadCriticalSection()
- {
- #if defined AS_POSIX_THREADS
- pthread_mutex_init(&cs, 0);
- #elif defined AS_WINDOWS_THREADS
- #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
- // Only the Ex version is available on Windows Store
- InitializeCriticalSectionEx(&cs, 4000, 0);
- #else
- // Only the non-Ex version is available on WinXP and older
- // MinGW also only defines this version
- InitializeCriticalSection(&cs);
- #endif
- #endif
- }
- asCThreadCriticalSection::~asCThreadCriticalSection()
- {
- #if defined AS_POSIX_THREADS
- pthread_mutex_destroy(&cs);
- #elif defined AS_WINDOWS_THREADS
- DeleteCriticalSection(&cs);
- #endif
- }
- void asCThreadCriticalSection::Enter()
- {
- #if defined AS_POSIX_THREADS
- pthread_mutex_lock(&cs);
- #elif defined AS_WINDOWS_THREADS
- EnterCriticalSection(&cs);
- #endif
- }
- void asCThreadCriticalSection::Leave()
- {
- #if defined AS_POSIX_THREADS
- pthread_mutex_unlock(&cs);
- #elif defined AS_WINDOWS_THREADS
- LeaveCriticalSection(&cs);
- #endif
- }
- bool asCThreadCriticalSection::TryEnter()
- {
- #if defined AS_POSIX_THREADS
- return !pthread_mutex_trylock(&cs);
- #elif defined AS_WINDOWS_THREADS
- return TryEnterCriticalSection(&cs) ? true : false;
- #else
- return true;
- #endif
- }
- asCThreadReadWriteLock::asCThreadReadWriteLock()
- {
- #if defined AS_POSIX_THREADS
- int r = pthread_rwlock_init(&lock, 0);
- asASSERT( r == 0 );
- UNUSED_VAR(r);
- #elif defined AS_WINDOWS_THREADS
- #if defined(_MSC_VER) && (WINAPI_FAMILY & WINAPI_FAMILY_PHONE_APP)
- // Only the Ex versions are available on Windows Store
- // Create a semaphore to allow up to maxReaders simultaneous readers
- readLocks = CreateSemaphoreExW(NULL, maxReaders, maxReaders, 0, 0, 0);
- // Create a critical section to synchronize writers
- InitializeCriticalSectionEx(&writeLock, 4000, 0);
- #else
- readLocks = CreateSemaphoreW(NULL, maxReaders, maxReaders, 0);
- InitializeCriticalSection(&writeLock);
- #endif
- #endif
- }
- asCThreadReadWriteLock::~asCThreadReadWriteLock()
- {
- #if defined AS_POSIX_THREADS
- pthread_rwlock_destroy(&lock);
- #elif defined AS_WINDOWS_THREADS
- DeleteCriticalSection(&writeLock);
- CloseHandle(readLocks);
- #endif
- }
- void asCThreadReadWriteLock::AcquireExclusive()
- {
- #if defined AS_POSIX_THREADS
- pthread_rwlock_wrlock(&lock);
- #elif defined AS_WINDOWS_THREADS
- // Synchronize writers, so only one tries to lock out the readers
- EnterCriticalSection(&writeLock);
- // Lock all reader out from the semaphore. Do this one by one,
- // so the lock doesn't have to wait until there are no readers at all.
- // If we try to lock all at once it is quite possible the writer will
- // never succeed.
- for( asUINT n = 0; n < maxReaders; n++ )
- WaitForSingleObjectEx(readLocks, INFINITE, FALSE);
- // Allow another writer to lock. It will only be able to
- // lock the readers when this writer releases them anyway.
- LeaveCriticalSection(&writeLock);
- #endif
- }
- void asCThreadReadWriteLock::ReleaseExclusive()
- {
- #if defined AS_POSIX_THREADS
- pthread_rwlock_unlock(&lock);
- #elif defined AS_WINDOWS_THREADS
- // Release all readers at once
- ReleaseSemaphore(readLocks, maxReaders, 0);
- #endif
- }
- void asCThreadReadWriteLock::AcquireShared()
- {
- #if defined AS_POSIX_THREADS
- pthread_rwlock_rdlock(&lock);
- #elif defined AS_WINDOWS_THREADS
- // Lock a reader slot
- WaitForSingleObjectEx(readLocks, INFINITE, FALSE);
- #endif
- }
- void asCThreadReadWriteLock::ReleaseShared()
- {
- #if defined AS_POSIX_THREADS
- pthread_rwlock_unlock(&lock);
- #elif defined AS_WINDOWS_THREADS
- // Release the reader slot
- ReleaseSemaphore(readLocks, 1, 0);
- #endif
- }
- #endif
- //========================================================================
- END_AS_NAMESPACE
|