diff options
author | Chris Robinson <chris.kcat@gmail.com> | 2022-12-30 20:56:37 -0800 |
---|---|---|
committer | Chris Robinson <chris.kcat@gmail.com> | 2022-12-30 20:56:37 -0800 |
commit | beaffdda716e2063d1112cb09956d44d948f40b5 (patch) | |
tree | 1aee3242e17c5f59030e4cf26a617e4791ba3ac2 | |
parent | 98ba092c9126ae5534b509a602bfe020d9cebca9 (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.cpp | 24 | ||||
-rw-r--r-- | alc/context.cpp | 8 | ||||
-rw-r--r-- | alc/context.h | 1 |
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: |