aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2021-12-20 10:27:39 -0800
committerChris Robinson <[email protected]>2021-12-20 10:27:39 -0800
commit633c332deee0500b85927906be1084606a286ac9 (patch)
tree31d178782d6ed65163c4f9f945ccace31a1c0ad1
parenta88803f21e1b44633de1aa5e068df8e9f371c2c2 (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.cpp14
-rw-r--r--alc/context.cpp7
-rw-r--r--alc/context.h15
-rw-r--r--router/al.cpp12
-rw-r--r--router/alc.cpp18
-rw-r--r--router/router.cpp5
-rw-r--r--router/router.h11
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};