diff options
author | Chris Robinson <[email protected]> | 2021-12-20 10:27:39 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2021-12-20 10:27:39 -0800 |
commit | 633c332deee0500b85927906be1084606a286ac9 (patch) | |
tree | 31d178782d6ed65163c4f9f945ccace31a1c0ad1 | |
parent | a88803f21e1b44633de1aa5e068df8e9f371c2c2 (diff) |
Work around a MinGW thread_local bug
MinGW-w64 generates bad code when accessing extern thread_local objects.
Wrapper functions are used to ensure it only accesses them from the same place
they're defined. This unfortunately adds a bit of overhead for what should be a
relatively simple thing.
These functions are inlined for non-MinGW targets, avoiding the overhead on
non-affected targets.
-rw-r--r-- | alc/alc.cpp | 14 | ||||
-rw-r--r-- | alc/context.cpp | 7 | ||||
-rw-r--r-- | alc/context.h | 15 | ||||
-rw-r--r-- | router/al.cpp | 12 | ||||
-rw-r--r-- | router/alc.cpp | 18 | ||||
-rw-r--r-- | router/router.cpp | 5 | ||||
-rw-r--r-- | router/router.h | 11 |
7 files changed, 60 insertions, 22 deletions
diff --git a/alc/alc.cpp b/alc/alc.cpp index c6ac6508..b63f0f82 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -2218,7 +2218,7 @@ ContextRef VerifyContext(ALCcontext *context) /** Returns a new reference to the currently active context for this thread. */ ContextRef GetContextRef(void) { - ALCcontext *context{ALCcontext::sLocalContext}; + ALCcontext *context{ALCcontext::getThreadContext()}; if(context) context->add_ref(); else @@ -3062,7 +3062,7 @@ END_API_FUNC ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) START_API_FUNC { - ALCcontext *Context{ALCcontext::sLocalContext}; + ALCcontext *Context{ALCcontext::getThreadContext()}; if(!Context) Context = ALCcontext::sGlobalContext.load(); return Context; } @@ -3071,7 +3071,7 @@ END_API_FUNC /** Returns the currently active thread-local context. */ ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) START_API_FUNC -{ return ALCcontext::sLocalContext; } +{ return ALCcontext::getThreadContext(); } END_API_FUNC ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) @@ -3098,8 +3098,8 @@ START_API_FUNC * thread-local context. Take ownership of the thread-local context * reference (if any), clearing the storage to null. */ - ctx = ContextRef{ALCcontext::sLocalContext}; - if(ctx) ALCcontext::sThreadContext.set(nullptr); + ctx = ContextRef{ALCcontext::getThreadContext()}; + if(ctx) ALCcontext::setThreadContext(nullptr); /* Reset (decrement) the previous thread-local reference. */ return ALC_TRUE; @@ -3122,8 +3122,8 @@ START_API_FUNC } } /* context's reference count is already incremented */ - ContextRef old{ALCcontext::sLocalContext}; - ALCcontext::sThreadContext.set(ctx.release()); + ContextRef old{ALCcontext::getThreadContext()}; + ALCcontext::setThreadContext(ctx.release()); return ALC_TRUE; } diff --git a/alc/context.cpp b/alc/context.cpp index 1dff00bc..c2d3e351 100644 --- a/alc/context.cpp +++ b/alc/context.cpp @@ -96,6 +96,13 @@ thread_local ALCcontext::ThreadCtx ALCcontext::sThreadContext; ALeffect ALCcontext::sDefaultEffect; +#ifdef __MINGW32__ +ALCcontext *ALCcontext::getThreadContext() noexcept +{ return sLocalContext; } +void ALCcontext::setThreadContext(ALCcontext *context) noexcept +{ sThreadContext.set(context); } +#endif + ALCcontext::ALCcontext(al::intrusive_ptr<ALCdevice> device) : ContextBase{device.get()}, mALDevice{std::move(device)} { diff --git a/alc/context.h b/alc/context.h index d0afbdd9..87754235 100644 --- a/alc/context.h +++ b/alc/context.h @@ -131,8 +131,10 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase { /* Process-wide current context */ static std::atomic<ALCcontext*> sGlobalContext; +private: /* Thread-local current context. */ static thread_local ALCcontext *sLocalContext; + /* Thread-local context handling. This handles attempting to release the * context which may have been left current when the thread is destroyed. */ @@ -143,6 +145,19 @@ struct ALCcontext : public al::intrusive_ref<ALCcontext>, ContextBase { }; static thread_local ThreadCtx sThreadContext; +public: + /* HACK: MinGW generates bad code when accessing an extern thread_local + * object. Add a wrapper function for it that only accesses it where it's + * defined. + */ +#ifdef __MINGW32__ + static ALCcontext *getThreadContext() noexcept; + static void setThreadContext(ALCcontext *context) noexcept; +#else + static ALCcontext *getThreadContext() noexcept { return sLocalContext; } + static void setThreadContext(ALCcontext *context) noexcept { sThreadContext.set(context); } +#endif + /* Default effect that applies to sources that don't have an effect on send 0. */ static ALeffect sDefaultEffect; diff --git a/router/al.cpp b/router/al.cpp index 5e273236..db18206e 100644 --- a/router/al.cpp +++ b/router/al.cpp @@ -11,31 +11,31 @@ std::atomic<DriverIface*> CurrentCtxDriver{nullptr}; #define DECL_THUNK1(R,n,T1) AL_API R AL_APIENTRY n(T1 a) \ { \ - DriverIface *iface = ThreadCtxDriver; \ + DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a); \ } #define DECL_THUNK2(R,n,T1,T2) AL_API R AL_APIENTRY n(T1 a, T2 b) \ { \ - DriverIface *iface = ThreadCtxDriver; \ + DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b); \ } #define DECL_THUNK3(R,n,T1,T2,T3) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c) \ { \ - DriverIface *iface = ThreadCtxDriver; \ + DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b, c); \ } #define DECL_THUNK4(R,n,T1,T2,T3,T4) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d) \ { \ - DriverIface *iface = ThreadCtxDriver; \ + DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b, c, d); \ } #define DECL_THUNK5(R,n,T1,T2,T3,T4,T5) AL_API R AL_APIENTRY n(T1 a, T2 b, T3 c, T4 d, T5 e) \ { \ - DriverIface *iface = ThreadCtxDriver; \ + DriverIface *iface = GetThreadDriver(); \ if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); \ return iface->n(a, b, c, d, e); \ } @@ -46,7 +46,7 @@ std::atomic<DriverIface*> CurrentCtxDriver{nullptr}; */ AL_API ALenum AL_APIENTRY alGetError(void) { - DriverIface *iface = ThreadCtxDriver; + DriverIface *iface = GetThreadDriver(); if(!iface) iface = CurrentCtxDriver.load(std::memory_order_acquire); return iface ? iface->alGetError() : AL_NO_ERROR; } diff --git a/router/alc.cpp b/router/alc.cpp index d43c78ce..5532c047 100644 --- a/router/alc.cpp +++ b/router/alc.cpp @@ -469,21 +469,21 @@ ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) */ if(idx < 0) { - DriverIface *oldiface = ThreadCtxDriver; + DriverIface *oldiface = GetThreadDriver(); if(oldiface) oldiface->alcSetThreadContext(nullptr); oldiface = CurrentCtxDriver.exchange(nullptr); if(oldiface) oldiface->alcMakeContextCurrent(nullptr); } else { - DriverIface *oldiface = ThreadCtxDriver; + DriverIface *oldiface = GetThreadDriver(); if(oldiface && oldiface != &DriverList[idx]) oldiface->alcSetThreadContext(nullptr); oldiface = CurrentCtxDriver.exchange(&DriverList[idx]); if(oldiface && oldiface != &DriverList[idx]) oldiface->alcMakeContextCurrent(nullptr); } - ThreadCtxDriver = nullptr; + SetThreadDriver(nullptr); return ALC_TRUE; } @@ -526,7 +526,7 @@ ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) { - DriverIface *iface = ThreadCtxDriver; + DriverIface *iface = GetThreadDriver(); if(!iface) iface = CurrentCtxDriver.load(); return iface ? iface->alcGetCurrentContext() : nullptr; } @@ -932,10 +932,10 @@ ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) if(!context) { - DriverIface *oldiface = ThreadCtxDriver; + DriverIface *oldiface = GetThreadDriver(); if(oldiface && !oldiface->alcSetThreadContext(nullptr)) return ALC_FALSE; - ThreadCtxDriver = nullptr; + SetThreadDriver(nullptr); return ALC_TRUE; } @@ -944,10 +944,10 @@ ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) { if(DriverList[idx].alcSetThreadContext(context)) { - DriverIface *oldiface = ThreadCtxDriver; + DriverIface *oldiface = GetThreadDriver(); if(oldiface != &DriverList[idx]) { - ThreadCtxDriver = &DriverList[idx]; + SetThreadDriver(&DriverList[idx]); if(oldiface) oldiface->alcSetThreadContext(nullptr); } return ALC_TRUE; @@ -960,7 +960,7 @@ ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) { - DriverIface *iface = ThreadCtxDriver; + DriverIface *iface = GetThreadDriver(); if(iface) return iface->alcGetThreadContext(); return nullptr; } diff --git a/router/router.cpp b/router/router.cpp index 731db063..7eb0bddc 100644 --- a/router/router.cpp +++ b/router/router.cpp @@ -24,6 +24,11 @@ thread_local DriverIface *ThreadCtxDriver; enum LogLevel LogLevel = LogLevel_Error; FILE *LogFile; +#ifdef __MINGW32__ +DriverIface *GetThreadDriver() noexcept { return ThreadCtxDriver; } +void SetThreadDriver(DriverIface *driver) noexcept { ThreadCtxDriver = driver; } +#endif + static void LoadDriverList(void); diff --git a/router/router.h b/router/router.h index 253494de..f9ebd4a4 100644 --- a/router/router.h +++ b/router/router.h @@ -172,6 +172,17 @@ extern std::vector<DriverIface> DriverList; extern thread_local DriverIface *ThreadCtxDriver; extern std::atomic<DriverIface*> CurrentCtxDriver; +/* HACK: MinGW generates bad code when accessing an extern thread_local object. + * Add a wrapper function for it that only accesses it where it's defined. + */ +#ifdef __MINGW32__ +DriverIface *GetThreadDriver() noexcept; +void SetThreadDriver(DriverIface *driver) noexcept; +#else +inline DriverIface *GetThreadDriver() noexcept { return ThreadCtxDriver; } +inline void SetThreadDriver(DriverIface *driver) noexcept { ThreadCtxDriver = driver; } +#endif + class PtrIntMap { void **mKeys{nullptr}; |