diff options
Diffstat (limited to 'indra/llcommon/llmutex.cpp')
-rw-r--r-- | indra/llcommon/llmutex.cpp | 391 |
1 files changed, 291 insertions, 100 deletions
diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp index 0273dd5970..be1ae89a25 100644 --- a/indra/llcommon/llmutex.cpp +++ b/indra/llcommon/llmutex.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llmutex.cpp * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -28,152 +28,343 @@ #include "llmutex.h" #include "llthread.h" #include "lltimer.h" +#include "llcoros.h" -//============================================================================ +//--------------------------------------------------------------------- +// +// LLMutex +// LLMutex::LLMutex() : mCount(0) { } - LLMutex::~LLMutex() { } - void LLMutex::lock() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(isSelfLocked()) - { //redundant lock - mCount++; - return; - } - - mMutex.lock(); - + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + + // LLMutex is not coroutine aware and should not be used from a coroutine + // If your code is running in a coroutine, you should use LLCoros::Mutex instead + // NOTE: If the stack trace you're staring at contains non-thread-safe code, + // you should use LLAppViewer::instance().postToMainThread() to shuttle execution + // back to the main loop. + // NOTE: If you got here from seeing this assert in your log and you're not seeing + // a stack trace that points here, put a breakpoint in on_main_coro and try again. + llassert(LLCoros::on_main_coro()); + + if(isSelfLocked()) + { //redundant lock + mCount++; + return; + } + + mMutex.lock(); + #if MUTEX_DEBUG - // Have to have the lock before we can access the debug info - auto id = LLThread::currentID(); - if (mIsLocked[id] != FALSE) - LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = TRUE; + // Have to have the lock before we can access the debug info + auto id = LLThread::currentID(); + if (mIsLocked[id]) + LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; + mIsLocked[id] = true; #endif - mLockingThread = LLThread::currentID(); + mLockingThread = LLThread::currentID(); } void LLMutex::unlock() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (mCount > 0) - { //not the root unlock - mCount--; - return; - } - + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + + if (mCount > 0) + { //not the root unlock + mCount--; + return; + } + #if MUTEX_DEBUG - // Access the debug info while we have the lock - auto id = LLThread::currentID(); - if (mIsLocked[id] != TRUE) - LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = FALSE; + // Access the debug info while we have the lock + auto id = LLThread::currentID(); + if (!mIsLocked[id]) + LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL; + mIsLocked[id] = false; #endif - mLockingThread = LLThread::id_t(); - mMutex.unlock(); + mLockingThread = LLThread::id_t(); + mMutex.unlock(); } bool LLMutex::isLocked() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (!mMutex.try_lock()) - { - return true; - } - else - { - mMutex.unlock(); - return false; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + if (!mMutex.try_lock()) + { + return true; + } + else + { + mMutex.unlock(); + return false; + } } bool LLMutex::isSelfLocked() { - return mLockingThread == LLThread::currentID(); + return mLockingThread == LLThread::currentID(); } LLThread::id_t LLMutex::lockingThread() const { - return mLockingThread; + return mLockingThread; } bool LLMutex::trylock() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(isSelfLocked()) - { //redundant lock - mCount++; - return true; - } - - if (!mMutex.try_lock()) - { - return false; - } - + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + if (isSelfLocked()) + { //redundant lock + mCount++; + return true; + } + + if (!mMutex.try_lock()) + { + return false; + } + #if MUTEX_DEBUG - // Have to have the lock before we can access the debug info - auto id = LLThread::currentID(); - if (mIsLocked[id] != FALSE) - LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = TRUE; + // Have to have the lock before we can access the debug info + auto id = LLThread::currentID(); + if (mIsLocked[id]) + LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; + mIsLocked[id] = true; #endif - mLockingThread = LLThread::currentID(); - return true; + mLockingThread = LLThread::currentID(); + return true; } -//============================================================================ -LLCondition::LLCondition() : - LLMutex() +//--------------------------------------------------------------------- +// +// LLSharedMutex +// +LLSharedMutex::LLSharedMutex() +: mLockingThreads(2) // Reserve 2 slots in the map hash table +, mIsShared(false) { } +bool LLSharedMutex::isLocked() const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + std::lock_guard<std::mutex> lock(mLockMutex); -LLCondition::~LLCondition() + return !mLockingThreads.empty(); +} + +bool LLSharedMutex::isThreadLocked() const { + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + LLThread::id_t current_thread = LLThread::currentID(); + std::lock_guard<std::mutex> lock(mLockMutex); + + const_iterator it = mLockingThreads.find(current_thread); + return it != mLockingThreads.end(); } +void LLSharedMutex::lockShared() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + LLThread::id_t current_thread = LLThread::currentID(); + + mLockMutex.lock(); + iterator it = mLockingThreads.find(current_thread); + if (it != mLockingThreads.end()) + { + it->second++; + } + else + { + // Acquire the mutex immediately if the mutex is not locked exclusively + // or enter a locking state if the mutex is already locked exclusively + mLockMutex.unlock(); + mSharedMutex.lock_shared(); + mLockMutex.lock(); + // Continue after acquiring the mutex + mLockingThreads.emplace(std::make_pair(current_thread, 1)); + mIsShared = true; + } + mLockMutex.unlock(); +} + +void LLSharedMutex::lockExclusive() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + LLThread::id_t current_thread = LLThread::currentID(); + + mLockMutex.lock(); + iterator it = mLockingThreads.find(current_thread); + if (it != mLockingThreads.end()) + { + if (mIsShared) + { + // The mutex is already locked in the current thread + // but this lock is SHARED (not EXCLISIVE) + // We can't lock it again, the lock stays shared + // This can lead to a collision (theoretically) + llassert_always(!"The current thread is already locked SHARED and can't be locked EXCLUSIVE"); + } + it->second++; + } + else + { + // Acquire the mutex immediately if mLockingThreads is empty + // or enter a locking state if mLockingThreads is not empty + mLockMutex.unlock(); + mSharedMutex.lock(); + mLockMutex.lock(); + // Continue after acquiring the mutex (and possible quitting the locking state) + mLockingThreads.emplace(std::make_pair(current_thread, 1)); + mIsShared = false; + } + mLockMutex.unlock(); +} + +bool LLSharedMutex::trylockShared() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + LLThread::id_t current_thread = LLThread::currentID(); + std::lock_guard<std::mutex> lock(mLockMutex); + + iterator it = mLockingThreads.find(current_thread); + if (it != mLockingThreads.end()) + { + it->second++; + } + else + { + if (!mSharedMutex.try_lock_shared()) + return false; + + mLockingThreads.emplace(std::make_pair(current_thread, 1)); + mIsShared = true; + } + + return true; +} + +bool LLSharedMutex::trylockExclusive() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + LLThread::id_t current_thread = LLThread::currentID(); + std::lock_guard<std::mutex> lock(mLockMutex); + + if (mLockingThreads.size() == 1 && mLockingThreads.begin()->first == current_thread) + { + mLockingThreads.begin()->second++; + } + else + { + if (!mSharedMutex.try_lock()) + return false; + + mLockingThreads.emplace(std::make_pair(current_thread, 1)); + mIsShared = false; + } + + return true; +} + +void LLSharedMutex::unlockShared() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + LLThread::id_t current_thread = LLThread::currentID(); + std::lock_guard<std::mutex> lock(mLockMutex); + + iterator it = mLockingThreads.find(current_thread); + if (it != mLockingThreads.end()) + { + if (it->second > 1) + { + it->second--; + } + else + { + mLockingThreads.erase(it); + mSharedMutex.unlock_shared(); + } + } +} + +void LLSharedMutex::unlockExclusive() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + LLThread::id_t current_thread = LLThread::currentID(); + std::lock_guard<std::mutex> lock(mLockMutex); + + iterator it = mLockingThreads.find(current_thread); + if (it != mLockingThreads.end()) + { + if (it->second > 1) + { + it->second--; + } + else + { + mLockingThreads.erase(it); + mSharedMutex.unlock(); + } + } +} + + +//--------------------------------------------------------------------- +// +// LLCondition +// +LLCondition::LLCondition() : + LLMutex() +{ +} + +LLCondition::~LLCondition() +{ +} void LLCondition::wait() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - std::unique_lock< std::mutex > lock(mMutex); - mCond.wait(lock); + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + std::unique_lock< std::mutex > lock(mMutex); + mCond.wait(lock); } void LLCondition::signal() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mCond.notify_one(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + mCond.notify_one(); } void LLCondition::broadcast() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mCond.notify_all(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + mCond.notify_all(); } - +//--------------------------------------------------------------------- +// +// LLMutexTrylock +// LLMutexTrylock::LLMutexTrylock(LLMutex* mutex) : mMutex(mutex), mLocked(false) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; if (mMutex) mLocked = mMutex->trylock(); } @@ -182,7 +373,7 @@ LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms) : mMutex(mutex), mLocked(false) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; if (!mMutex) return; @@ -197,7 +388,7 @@ LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms) LLMutexTrylock::~LLMutexTrylock() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; if (mMutex && mLocked) mMutex->unlock(); } @@ -209,31 +400,31 @@ LLMutexTrylock::~LLMutexTrylock() // LLScopedLock::LLScopedLock(std::mutex* mutex) : mMutex(mutex) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(mutex) - { - mutex->lock(); - mLocked = true; - } - else - { - mLocked = false; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + if(mutex) + { + mutex->lock(); + mLocked = true; + } + else + { + mLocked = false; + } } LLScopedLock::~LLScopedLock() { - unlock(); + unlock(); } void LLScopedLock::unlock() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(mLocked) - { - mMutex->unlock(); - mLocked = false; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + if(mLocked) + { + mMutex->unlock(); + mLocked = false; + } } //============================================================================ |