From 74c8b028d42a8c5b080bb861e427f38cedd4ad7c Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Fri, 15 Dec 2023 18:26:14 +0100 Subject: SL-20743 Use LLMutex in LLImageBase for internal data thread-safety --- indra/llcommon/llmutex.cpp | 186 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 178 insertions(+), 8 deletions(-) (limited to 'indra/llcommon/llmutex.cpp') diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp index 0273dd5970..f56c2d08a6 100644 --- a/indra/llcommon/llmutex.cpp +++ b/indra/llcommon/llmutex.cpp @@ -29,19 +29,20 @@ #include "llthread.h" #include "lltimer.h" -//============================================================================ +//--------------------------------------------------------------------- +// +// LLMutex +// LLMutex::LLMutex() : mCount(0) { } - LLMutex::~LLMutex() { } - void LLMutex::lock() { LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD @@ -112,7 +113,7 @@ LLThread::id_t LLMutex::lockingThread() const bool LLMutex::trylock() { LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(isSelfLocked()) + if (isSelfLocked()) { //redundant lock mCount++; return true; @@ -135,19 +136,185 @@ bool LLMutex::trylock() return true; } -//============================================================================ +//--------------------------------------------------------------------- +// +// 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 lock(mLockMutex); + + return !mLockingThreads.empty(); +} + +bool LLSharedMutex::isThreadLocked() const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + LLThread::id_t current_thread = LLThread::currentID(); + std::lock_guard 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(); + if (mLockingThreads.size() == 1 && mLockingThreads.begin()->first == current_thread) + { + mLockingThreads.begin()->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 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 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 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 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 @@ -168,7 +335,10 @@ void LLCondition::broadcast() } - +//--------------------------------------------------------------------- +// +// LLMutexTrylock +// LLMutexTrylock::LLMutexTrylock(LLMutex* mutex) : mMutex(mutex), mLocked(false) -- cgit v1.2.3 From e4a1feb83079965fbebd356aa694adf100fb7ee3 Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Sun, 7 Jan 2024 14:44:52 +0100 Subject: SL-20743 Use LLMutex in LLImageBase for internal data thread-safety (update) --- indra/llcommon/llmutex.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'indra/llcommon/llmutex.cpp') diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp index f56c2d08a6..973ecbc87b 100644 --- a/indra/llcommon/llmutex.cpp +++ b/indra/llcommon/llmutex.cpp @@ -196,9 +196,18 @@ void LLSharedMutex::lockExclusive() LLThread::id_t current_thread = LLThread::currentID(); mLockMutex.lock(); - if (mLockingThreads.size() == 1 && mLockingThreads.begin()->first == current_thread) + iterator it = mLockingThreads.find(current_thread); + if (it != mLockingThreads.end()) { - mLockingThreads.begin()->second++; + 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 { -- cgit v1.2.3 From ba4e7b989b6c20a49da0eeb450bd2f945b3eefc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Thu, 8 Feb 2024 02:51:51 +0100 Subject: llcommon: BOOL (int) to real bool/LSTATUS --- indra/llcommon/llmutex.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'indra/llcommon/llmutex.cpp') diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp index 973ecbc87b..1c025c286d 100644 --- a/indra/llcommon/llmutex.cpp +++ b/indra/llcommon/llmutex.cpp @@ -57,9 +57,9 @@ void LLMutex::lock() #if MUTEX_DEBUG // Have to have the lock before we can access the debug info auto id = LLThread::currentID(); - if (mIsLocked[id] != FALSE) + if (mIsLocked[id] != false) LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = TRUE; + mIsLocked[id] = true; #endif mLockingThread = LLThread::currentID(); @@ -77,9 +77,9 @@ void LLMutex::unlock() #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; + if (mIsLocked[id] != true) + LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL; + mIsLocked[id] = false; #endif mLockingThread = LLThread::id_t(); @@ -127,9 +127,9 @@ bool LLMutex::trylock() #if MUTEX_DEBUG // Have to have the lock before we can access the debug info auto id = LLThread::currentID(); - if (mIsLocked[id] != FALSE) + if (mIsLocked[id] != false) LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = TRUE; + mIsLocked[id] = true; #endif mLockingThread = LLThread::currentID(); -- cgit v1.2.3 From 7fc5f7e649c564fa8479a72a45459d0cc427d0f8 Mon Sep 17 00:00:00 2001 From: RunitaiLinden Date: Thu, 2 May 2024 10:57:39 -0500 Subject: #1354 Make coroutines use LLCoros::Mutex instead of LLMutex (#1356) * #1354 Make coroutines use LLCoros::Mutex instead of LLMutex * #1354 Fix some more unsafe coroutine executions. * #1354 Implement changes requested by Nat --- indra/llcommon/llmutex.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'indra/llcommon/llmutex.cpp') diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp index 0273dd5970..5d93d6e72b 100644 --- a/indra/llcommon/llmutex.cpp +++ b/indra/llcommon/llmutex.cpp @@ -28,6 +28,7 @@ #include "llmutex.h" #include "llthread.h" #include "lltimer.h" +#include "llcoros.h" //============================================================================ @@ -44,7 +45,17 @@ LLMutex::~LLMutex() void LLMutex::lock() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + 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++; @@ -66,7 +77,8 @@ void LLMutex::lock() void LLMutex::unlock() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; + if (mCount > 0) { //not the root unlock mCount--; @@ -111,7 +123,7 @@ LLThread::id_t LLMutex::lockingThread() const bool LLMutex::trylock() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; if(isSelfLocked()) { //redundant lock mCount++; -- cgit v1.2.3 From f9473e8afcb624cc1b101195bf15943ec372b56f Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Mon, 6 May 2024 16:52:34 +0200 Subject: secondlife/viewer#1333 BOOL to bool conversion leftovers: ternaries --- indra/llcommon/llmutex.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/llcommon/llmutex.cpp') diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp index 1c025c286d..3cfdc8304e 100644 --- a/indra/llcommon/llmutex.cpp +++ b/indra/llcommon/llmutex.cpp @@ -57,7 +57,7 @@ void LLMutex::lock() #if MUTEX_DEBUG // Have to have the lock before we can access the debug info auto id = LLThread::currentID(); - if (mIsLocked[id] != false) + if (mIsLocked[id]) LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; mIsLocked[id] = true; #endif @@ -77,7 +77,7 @@ void LLMutex::unlock() #if MUTEX_DEBUG // Access the debug info while we have the lock auto id = LLThread::currentID(); - if (mIsLocked[id] != true) + if (!mIsLocked[id]) LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL; mIsLocked[id] = false; #endif @@ -127,7 +127,7 @@ bool LLMutex::trylock() #if MUTEX_DEBUG // Have to have the lock before we can access the debug info auto id = LLThread::currentID(); - if (mIsLocked[id] != false) + if (mIsLocked[id]) LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; mIsLocked[id] = true; #endif -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/llcommon/llmutex.cpp | 836 ++++++++++++++++++++++----------------------- 1 file changed, 418 insertions(+), 418 deletions(-) (limited to 'indra/llcommon/llmutex.cpp') diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp index a7c2817e2f..7bdc391459 100644 --- a/indra/llcommon/llmutex.cpp +++ b/indra/llcommon/llmutex.cpp @@ -1,418 +1,418 @@ -/** - * @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$ - */ - -#include "linden_common.h" - -#include "llmutex.h" -#include "llthread.h" -#include "lltimer.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(); - -#if MUTEX_DEBUG - // 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(); -} - -void LLMutex::unlock() -{ - 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]) - LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = false; -#endif - - 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; - } -} - -bool LLMutex::isSelfLocked() -{ - return mLockingThread == LLThread::currentID(); -} - -LLThread::id_t LLMutex::lockingThread() const -{ - return mLockingThread; -} - -bool LLMutex::trylock() -{ - 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]) - LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = true; -#endif - - mLockingThread = LLThread::currentID(); - return true; -} - - -//--------------------------------------------------------------------- -// -// 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 lock(mLockMutex); - - return !mLockingThreads.empty(); -} - -bool LLSharedMutex::isThreadLocked() const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - LLThread::id_t current_thread = LLThread::currentID(); - std::lock_guard 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 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 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 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 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); -} - -void LLCondition::signal() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mCond.notify_one(); -} - -void LLCondition::broadcast() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mCond.notify_all(); -} - - -//--------------------------------------------------------------------- -// -// LLMutexTrylock -// -LLMutexTrylock::LLMutexTrylock(LLMutex* mutex) - : mMutex(mutex), - mLocked(false) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (mMutex) - mLocked = mMutex->trylock(); -} - -LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms) - : mMutex(mutex), - mLocked(false) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (!mMutex) - return; - - for (U32 i = 0; i < aTries; ++i) - { - mLocked = mMutex->trylock(); - if (mLocked) - break; - ms_sleep(delay_ms); - } -} - -LLMutexTrylock::~LLMutexTrylock() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (mMutex && mLocked) - mMutex->unlock(); -} - - -//--------------------------------------------------------------------- -// -// LLScopedLock -// -LLScopedLock::LLScopedLock(std::mutex* mutex) : mMutex(mutex) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(mutex) - { - mutex->lock(); - mLocked = true; - } - else - { - mLocked = false; - } -} - -LLScopedLock::~LLScopedLock() -{ - unlock(); -} - -void LLScopedLock::unlock() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(mLocked) - { - mMutex->unlock(); - mLocked = false; - } -} - -//============================================================================ +/** + * @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$ + */ + +#include "linden_common.h" + +#include "llmutex.h" +#include "llthread.h" +#include "lltimer.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(); + +#if MUTEX_DEBUG + // 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(); +} + +void LLMutex::unlock() +{ + 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]) + LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL; + mIsLocked[id] = false; +#endif + + 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; + } +} + +bool LLMutex::isSelfLocked() +{ + return mLockingThread == LLThread::currentID(); +} + +LLThread::id_t LLMutex::lockingThread() const +{ + return mLockingThread; +} + +bool LLMutex::trylock() +{ + 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]) + LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; + mIsLocked[id] = true; +#endif + + mLockingThread = LLThread::currentID(); + return true; +} + + +//--------------------------------------------------------------------- +// +// 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 lock(mLockMutex); + + return !mLockingThreads.empty(); +} + +bool LLSharedMutex::isThreadLocked() const +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + LLThread::id_t current_thread = LLThread::currentID(); + std::lock_guard 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 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 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 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 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); +} + +void LLCondition::signal() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + mCond.notify_one(); +} + +void LLCondition::broadcast() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + mCond.notify_all(); +} + + +//--------------------------------------------------------------------- +// +// LLMutexTrylock +// +LLMutexTrylock::LLMutexTrylock(LLMutex* mutex) + : mMutex(mutex), + mLocked(false) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + if (mMutex) + mLocked = mMutex->trylock(); +} + +LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms) + : mMutex(mutex), + mLocked(false) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + if (!mMutex) + return; + + for (U32 i = 0; i < aTries; ++i) + { + mLocked = mMutex->trylock(); + if (mLocked) + break; + ms_sleep(delay_ms); + } +} + +LLMutexTrylock::~LLMutexTrylock() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + if (mMutex && mLocked) + mMutex->unlock(); +} + + +//--------------------------------------------------------------------- +// +// LLScopedLock +// +LLScopedLock::LLScopedLock(std::mutex* mutex) : mMutex(mutex) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + if(mutex) + { + mutex->lock(); + mLocked = true; + } + else + { + mLocked = false; + } +} + +LLScopedLock::~LLScopedLock() +{ + unlock(); +} + +void LLScopedLock::unlock() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD + if(mLocked) + { + mMutex->unlock(); + mLocked = false; + } +} + +//============================================================================ -- cgit v1.2.3