aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <chris.kcat@gmail.com>2022-12-30 20:56:37 -0800
committerChris Robinson <chris.kcat@gmail.com>2022-12-30 20:56:37 -0800
commitbeaffdda716e2063d1112cb09956d44d948f40b5 (patch)
tree1aee3242e17c5f59030e4cf26a617e4791ba3ac2
parent98ba092c9126ae5534b509a602bfe020d9cebca9 (diff)
Use a simple spinlock to protect the current global context
This will be much for efficient than a recursive mutex, given the amount of contention will be very low.
-rw-r--r--alc/alc.cpp24
-rw-r--r--alc/context.cpp8
-rw-r--r--alc/context.h1
3 files changed, 26 insertions, 7 deletions
diff --git a/alc/alc.cpp b/alc/alc.cpp
index 401a2ab6..db862a36 100644
--- a/alc/alc.cpp
+++ b/alc/alc.cpp
@@ -2492,9 +2492,14 @@ ContextRef GetContextRef(void)
context->add_ref();
else
{
- std::lock_guard<std::recursive_mutex> _{ListLock};
+ while(ALCcontext::sGlobalContextLock.exchange(true, std::memory_order_acquire)) {
+ /* Wait to make sure another thread isn't trying to change the
+ * current context and bring its refcount to 0.
+ */
+ }
context = ALCcontext::sGlobalContext.load(std::memory_order_acquire);
- if(context) context->add_ref();
+ if(context) [[likely]] context->add_ref();
+ ALCcontext::sGlobalContextLock.store(false, std::memory_order_release);
}
return ContextRef{context};
}
@@ -3385,13 +3390,18 @@ START_API_FUNC
}
/* Release this reference (if any) to store it in the GlobalContext
* pointer. Take ownership of the reference (if any) that was previously
- * stored there.
+ * stored there, and let the reference go.
*/
- ctx = ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())};
+ while(ALCcontext::sGlobalContextLock.exchange(true, std::memory_order_acquire)) {
+ /* Wait to make sure another thread isn't getting or trying to change
+ * the current context as its refcount is decremented.
+ */
+ }
+ ContextRef{ALCcontext::sGlobalContext.exchange(ctx.release())};
+ ALCcontext::sGlobalContextLock.store(false, std::memory_order_release);
- /* Reset (decrement) the previous global reference by replacing it with the
- * thread-local context. Take ownership of the thread-local context
- * reference (if any), clearing the storage to null.
+ /* Take ownership of the thread-local context reference (if any), clearing
+ * the storage to null.
*/
ctx = ContextRef{ALCcontext::getThreadContext()};
if(ctx) ALCcontext::setThreadContext(nullptr);
diff --git a/alc/context.cpp b/alc/context.cpp
index 906a160e..f9aec221 100644
--- a/alc/context.cpp
+++ b/alc/context.cpp
@@ -84,6 +84,7 @@ constexpr ALchar alExtList[] =
} // namespace
+std::atomic<bool> ALCcontext::sGlobalContextLock{false};
std::atomic<ALCcontext*> ALCcontext::sGlobalContext{nullptr};
thread_local ALCcontext *ALCcontext::sLocalContext{nullptr};
@@ -203,7 +204,14 @@ bool ALCcontext::deinit()
ALCcontext *origctx{this};
if(sGlobalContext.compare_exchange_strong(origctx, nullptr))
+ {
+ while(sGlobalContextLock.load()) {
+ /* Wait to make sure another thread didn't get the context and is
+ * trying to increment its refcount.
+ */
+ }
dec_ref();
+ }
bool ret{};
/* First make sure this context exists in the device's list. */
diff --git a/alc/context.h b/alc/context.h
index 58a70184..d93d63d6 100644
--- a/alc/context.h
+++ b/alc/context.h
@@ -148,6 +148,7 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase {
void setError(ALenum errorCode, const char *msg, ...);
/* Process-wide current context */
+ static std::atomic<bool> sGlobalContextLock;
static std::atomic<ALCcontext*> sGlobalContext;
private: