Commit cb286073 by Jamie Madill Committed by Commit Bot

Use c++ thread_local for current thread.

This solves a race condition using the global context. Sadly we can't use an unprotected variable safely because it can be written/read from multiple threads doing MakeCurrent. Has about a 2 ns regression when tested in Linux/Release per call. "null" benchmark test went from 27 -> 29 ns/iteration. Fixes a TSAN warning that popped up in angle_end2end_tests. Test: DrawCallPerfBenchmark.Run/vulkan_null Test: EGLContextASANTest.DestroyContextInUse/ES3_Vulkan Bug: b/168744561 Change-Id: Ic56f3faae81c1087b942a3cfc0e011b9ab439e0f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2419641Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent ab0b4b1d
...@@ -157,10 +157,3 @@ void *GetTLSValue(TLSIndex index) ...@@ -157,10 +157,3 @@ void *GetTLSValue(TLSIndex index)
return pthread_getspecific(index); return pthread_getspecific(index);
#endif #endif
} }
void SetUseAndroidOpenGLTlsSlot(bool platformTypeVulkan)
{
#if defined(ANGLE_PLATFORM_ANDROID)
gUseAndroidOpenGLTlsSlot = platformTypeVulkan;
#endif
}
...@@ -40,84 +40,6 @@ typedef pthread_key_t TLSIndex; ...@@ -40,84 +40,6 @@ typedef pthread_key_t TLSIndex;
# error Unsupported platform. # error Unsupported platform.
#endif #endif
#if defined(ANGLE_PLATFORM_ANDROID)
// The following ASM variant provides a much more performant store/retrieve interface
// compared to those provided by the pthread library. These have been derived from code
// in the bionic module of Android ->
// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/platform/bionic/tls.h;l=30
# if defined(__aarch64__)
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
void **__val; \
__asm__("mrs %0, tpidr_el0" : "=r"(__val)); \
__val; \
})
# elif defined(__arm__)
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
void **__val; \
__asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__val)); \
__val; \
})
# elif defined(__mips__)
// On mips32r1, this goes via a kernel illegal instruction trap that's
// optimized for v1
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
register void **__val asm("v1"); \
__asm__( \
".set push\n" \
".set mips32r2\n" \
"rdhwr %0,$29\n" \
".set pop\n" \
: "=r"(__val)); \
__val; \
})
# elif defined(__i386__)
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
void **__val; \
__asm__("movl %%gs:0, %0" : "=r"(__val)); \
__val; \
})
# elif defined(__x86_64__)
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
void **__val; \
__asm__("mov %%fs:0, %0" : "=r"(__val)); \
__val; \
})
# else
# error unsupported architecture
# endif
#endif // ANGLE_PLATFORM_ANDROID
// - TLS_SLOT_OPENGL and TLS_SLOT_OPENGL_API: These two aren't used by bionic
// itself, but allow the graphics code to access TLS directly rather than
// using the pthread API.
//
// Choose the TLS_SLOT_OPENGL TLS slot with the value that matches value in the header file in
// bionic(tls_defines.h)
constexpr TLSIndex kAndroidOpenGLTlsSlot = 3;
extern bool gUseAndroidOpenGLTlsSlot;
ANGLE_INLINE bool SetContextToAndroidOpenGLTLSSlot(gl::Context *value)
{
#if defined(ANGLE_PLATFORM_ANDROID)
if (gUseAndroidOpenGLTlsSlot)
{
ANGLE_ANDROID_GET_GL_TLS()[kAndroidOpenGLTlsSlot] = static_cast<void *>(value);
return true;
}
#endif
return false;
}
void SetUseAndroidOpenGLTlsSlot(bool platformTypeVulkan);
// TODO(kbr): for POSIX platforms this will have to be changed to take // TODO(kbr): for POSIX platforms this will have to be changed to take
// in a destructor function pointer, to allow the thread-local storage // in a destructor function pointer, to allow the thread-local storage
// to be properly deallocated upon thread exit. // to be properly deallocated upon thread exit.
......
...@@ -272,6 +272,8 @@ bool IsColorMaskedOut(const BlendState &blend) ...@@ -272,6 +272,8 @@ bool IsColorMaskedOut(const BlendState &blend)
} }
} // anonymous namespace } // anonymous namespace
thread_local Context *gCurrentValidContext = nullptr;
Context::Context(egl::Display *display, Context::Context(egl::Display *display,
const egl::Config *config, const egl::Config *config,
const Context *shareContext, const Context *shareContext,
...@@ -2525,6 +2527,9 @@ void Context::setContextLost() ...@@ -2525,6 +2527,9 @@ void Context::setContextLost()
// Stop skipping validation, since many implementation entrypoint assume they can't // Stop skipping validation, since many implementation entrypoint assume they can't
// be called when lost, or with null object arguments, etc. // be called when lost, or with null object arguments, etc.
mSkipValidation = false; mSkipValidation = false;
// Make sure we update TLS.
gCurrentValidContext = nullptr;
} }
GLenum Context::getGraphicsResetStatus() GLenum Context::getGraphicsResetStatus()
......
...@@ -800,6 +800,10 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl ...@@ -800,6 +800,10 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
OverlayType mOverlay; OverlayType mOverlay;
}; };
// Thread-local current valid context bound to the thread.
extern thread_local Context *gCurrentValidContext;
} // namespace gl } // namespace gl
#endif // LIBANGLE_CONTEXT_H_ #endif // LIBANGLE_CONTEXT_H_
...@@ -563,7 +563,11 @@ Display *Display::GetDisplayFromNativeDisplay(EGLNativeDisplayType nativeDisplay ...@@ -563,7 +563,11 @@ Display *Display::GetDisplayFromNativeDisplay(EGLNativeDisplayType nativeDisplay
// No valid display implementation for these attributes // No valid display implementation for these attributes
return nullptr; return nullptr;
} }
SetUseAndroidOpenGLTlsSlot(displayType == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
#if defined(ANGLE_PLATFORM_ANDROID)
angle::gUseAndroidOpenGLTlsSlot = displayType == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
#endif // defined(ANGLE_PLATFORM_ANDROID)
display->setupDisplayPlatform(impl); display->setupDisplayPlatform(impl);
} }
...@@ -1993,5 +1997,4 @@ egl::Error Display::handleGPUSwitch() ...@@ -1993,5 +1997,4 @@ egl::Error Display::handleGPUSwitch()
{ {
return mImplementation->handleGPUSwitch(); return mImplementation->handleGPUSwitch();
} }
} // namespace egl } // namespace egl
...@@ -11,7 +11,11 @@ ...@@ -11,7 +11,11 @@
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Debug.h" #include "libANGLE/Debug.h"
#include "libANGLE/Error.h" #include "libANGLE/Error.h"
#include "libANGLE/ErrorStrings.h"
namespace angle
{
bool gUseAndroidOpenGLTlsSlot;
} // namespace angle
namespace egl namespace egl
{ {
...@@ -96,18 +100,6 @@ gl::Context *Thread::getContext() const ...@@ -96,18 +100,6 @@ gl::Context *Thread::getContext() const
return mContext; return mContext;
} }
gl::Context *Thread::getValidContext() const
{
if (mContext && mContext->isContextLost())
{
mContext->handleError(GL_OUT_OF_MEMORY, gl::err::kContextLost, __FILE__, ANGLE_FUNCTION,
__LINE__);
return nullptr;
}
return mContext;
}
Display *Thread::getDisplay() const Display *Thread::getDisplay() const
{ {
if (mContext) if (mContext)
...@@ -116,5 +108,4 @@ Display *Thread::getDisplay() const ...@@ -116,5 +108,4 @@ Display *Thread::getDisplay() const
} }
return nullptr; return nullptr;
} }
} // namespace egl } // namespace egl
...@@ -13,6 +13,11 @@ ...@@ -13,6 +13,11 @@
#include "libANGLE/Debug.h" #include "libANGLE/Debug.h"
namespace angle
{
extern bool gUseAndroidOpenGLTlsSlot;
} // namespace angle
namespace gl namespace gl
{ {
class Context; class Context;
...@@ -47,7 +52,6 @@ class Thread : public LabeledObject ...@@ -47,7 +52,6 @@ class Thread : public LabeledObject
Surface *getCurrentDrawSurface() const; Surface *getCurrentDrawSurface() const;
Surface *getCurrentReadSurface() const; Surface *getCurrentReadSurface() const;
gl::Context *getContext() const; gl::Context *getContext() const;
gl::Context *getValidContext() const;
Display *getDisplay() const; Display *getDisplay() const;
private: private:
......
...@@ -15,53 +15,35 @@ ...@@ -15,53 +15,35 @@
#include <atomic> #include <atomic>
namespace gl
{
// In single-threaded cases we can avoid a TLS lookup for the current Context.
//
// Let a global single-threaded context have 3 states: unset, set, and multi-threaded.
// Initially it is unset. Then, on MakeCurrent:
//
// * if the ST context is unset -> set the global context.
// * if the ST context is set and matches the TLS -> set the global context.
// * if the ST context is set and does not match TLS -> set multi-threaded mode.
// * if in multi-threaded mode, unset and subsequently ignore the global context.
//
// Implementation-wise we can use a pointer and a boolean to represent the three modes.
Context *gSingleThreadedContext = nullptr;
bool gIsMultiThreadedContext = false;
} // namespace gl
namespace egl namespace egl
{ {
namespace namespace
{ {
static TLSIndex threadTLS = TLS_INVALID_INDEX; Debug *g_Debug = nullptr;
Debug *g_Debug = nullptr;
ANGLE_REQUIRE_CONSTANT_INIT std::atomic<angle::GlobalMutex *> g_Mutex(nullptr); ANGLE_REQUIRE_CONSTANT_INIT std::atomic<angle::GlobalMutex *> g_Mutex(nullptr);
static_assert(std::is_trivially_destructible<decltype(g_Mutex)>::value, static_assert(std::is_trivially_destructible<decltype(g_Mutex)>::value,
"global mutex is not trivially destructible"); "global mutex is not trivially destructible");
Thread *AllocateCurrentThread() void SetContextToAndroidOpenGLTLSSlot(gl::Context *value)
{ {
ASSERT(threadTLS != TLS_INVALID_INDEX); #if defined(ANGLE_PLATFORM_ANDROID)
if (threadTLS == TLS_INVALID_INDEX) if (angle::gUseAndroidOpenGLTlsSlot)
{ {
return nullptr; ANGLE_ANDROID_GET_GL_TLS()[angle::kAndroidOpenGLTlsSlot] = static_cast<void *>(value);
} }
#endif
}
Thread *thread = new Thread(); Thread *AllocateCurrentThread()
if (!SetTLSValue(threadTLS, thread)) {
{ gCurrentThread = new Thread();
ERR() << "Could not set thread local storage.";
return nullptr;
}
// Initialize fast TLS slot // Initialize fast TLS slot
SetContextToAndroidOpenGLTLSSlot(nullptr); SetContextToAndroidOpenGLTLSSlot(nullptr);
gl::gCurrentValidContext = nullptr;
return thread; return gCurrentThread;
} }
void AllocateDebug() void AllocateDebug()
...@@ -88,6 +70,8 @@ void AllocateMutex() ...@@ -88,6 +70,8 @@ void AllocateMutex()
} // anonymous namespace } // anonymous namespace
thread_local Thread *gCurrentThread = nullptr;
angle::GlobalMutex &GetGlobalMutex() angle::GlobalMutex &GetGlobalMutex()
{ {
AllocateMutex(); AllocateMutex();
...@@ -96,16 +80,7 @@ angle::GlobalMutex &GetGlobalMutex() ...@@ -96,16 +80,7 @@ angle::GlobalMutex &GetGlobalMutex()
Thread *GetCurrentThread() Thread *GetCurrentThread()
{ {
// Create a TLS index if one has not been created for this DLL Thread *current = gCurrentThread;
if (threadTLS == TLS_INVALID_INDEX)
{
threadTLS = CreateTLSIndex();
}
Thread *current = static_cast<Thread *>(GetTLSValue(threadTLS));
// ANGLE issue 488: when the dll is loaded after thread initialization,
// thread local storage (current) might not exist yet.
return (current ? current : AllocateCurrentThread()); return (current ? current : AllocateCurrentThread());
} }
...@@ -117,26 +92,10 @@ Debug *GetDebug() ...@@ -117,26 +92,10 @@ Debug *GetDebug()
void SetContextCurrent(Thread *thread, gl::Context *context) void SetContextCurrent(Thread *thread, gl::Context *context)
{ {
// See above comment on gGlobalContext. ASSERT(gCurrentThread);
// If the context is in multi-threaded mode, ignore the global context. gCurrentThread->setCurrent(context);
if (!gl::gIsMultiThreadedContext)
{
// If the global context is unset or matches the current TLS, set the global context.
if (gl::gSingleThreadedContext == nullptr ||
gl::gSingleThreadedContext == thread->getContext())
{
gl::gSingleThreadedContext = context;
}
else
{
// If the global context is set and does not match TLS, set multi-threaded mode.
gl::gSingleThreadedContext = nullptr;
gl::gIsMultiThreadedContext = true;
}
}
thread->setCurrent(context);
SetContextToAndroidOpenGLTLSSlot(context); SetContextToAndroidOpenGLTLSSlot(context);
gl::gCurrentValidContext = context;
} }
} // namespace egl } // namespace egl
...@@ -147,11 +106,9 @@ namespace egl ...@@ -147,11 +106,9 @@ namespace egl
namespace namespace
{ {
bool DeallocateCurrentThread() void DeallocateCurrentThread()
{ {
Thread *thread = static_cast<Thread *>(GetTLSValue(threadTLS)); SafeDelete(gCurrentThread);
SafeDelete(thread);
return SetTLSValue(threadTLS, nullptr);
} }
void DeallocateDebug() void DeallocateDebug()
...@@ -173,41 +130,15 @@ bool InitializeProcess() ...@@ -173,41 +130,15 @@ bool InitializeProcess()
{ {
ASSERT(g_Debug == nullptr); ASSERT(g_Debug == nullptr);
AllocateDebug(); AllocateDebug();
AllocateMutex(); AllocateMutex();
threadTLS = CreateTLSIndex();
if (threadTLS == TLS_INVALID_INDEX)
{
return false;
}
return AllocateCurrentThread() != nullptr; return AllocateCurrentThread() != nullptr;
} }
bool TerminateProcess() void TerminateProcess()
{ {
DeallocateDebug(); DeallocateDebug();
DeallocateMutex(); DeallocateMutex();
DeallocateCurrentThread();
if (!DeallocateCurrentThread())
{
return false;
}
if (threadTLS != TLS_INVALID_INDEX)
{
TLSIndex tlsCopy = threadTLS;
threadTLS = TLS_INVALID_INDEX;
if (!DestroyTLSIndex(tlsCopy))
{
return false;
}
}
return true;
} }
} // anonymous namespace } // anonymous namespace
...@@ -281,10 +212,12 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID) ...@@ -281,10 +212,12 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID)
return static_cast<BOOL>(egl::AllocateCurrentThread() != nullptr); return static_cast<BOOL>(egl::AllocateCurrentThread() != nullptr);
case DLL_THREAD_DETACH: case DLL_THREAD_DETACH:
return static_cast<BOOL>(egl::DeallocateCurrentThread()); egl::DeallocateCurrentThread();
break;
case DLL_PROCESS_DETACH: case DLL_PROCESS_DETACH:
return static_cast<BOOL>(egl::TerminateProcess()); egl::TerminateProcess();
break;
} }
return TRUE; return TRUE;
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#ifndef LIBGLESV2_GLOBALSTATE_H_ #ifndef LIBGLESV2_GLOBALSTATE_H_
#define LIBGLESV2_GLOBALSTATE_H_ #define LIBGLESV2_GLOBALSTATE_H_
#include "common/tls.h"
#include "libANGLE/Context.h" #include "libANGLE/Context.h"
#include "libANGLE/Debug.h" #include "libANGLE/Debug.h"
#include "libANGLE/Thread.h" #include "libANGLE/Thread.h"
...@@ -20,6 +19,69 @@ ...@@ -20,6 +19,69 @@
namespace angle namespace angle
{ {
using GlobalMutex = std::recursive_mutex; using GlobalMutex = std::recursive_mutex;
// - TLS_SLOT_OPENGL and TLS_SLOT_OPENGL_API: These two aren't used by bionic
// itself, but allow the graphics code to access TLS directly rather than
// using the pthread API.
//
// Choose the TLS_SLOT_OPENGL TLS slot with the value that matches value in the header file in
// bionic(tls_defines.h)
constexpr size_t kAndroidOpenGLTlsSlot = 3;
#if defined(ANGLE_PLATFORM_ANDROID)
// The following ASM variant provides a much more performant store/retrieve interface
// compared to those provided by the pthread library. These have been derived from code
// in the bionic module of Android ->
// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/platform/bionic/tls.h;l=30
# if defined(__aarch64__)
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
void **__val; \
__asm__("mrs %0, tpidr_el0" : "=r"(__val)); \
__val; \
})
# elif defined(__arm__)
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
void **__val; \
__asm__("mrc p15, 0, %0, c13, c0, 3" : "=r"(__val)); \
__val; \
})
# elif defined(__mips__)
// On mips32r1, this goes via a kernel illegal instruction trap that's
// optimized for v1
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
register void **__val asm("v1"); \
__asm__( \
".set push\n" \
".set mips32r2\n" \
"rdhwr %0,$29\n" \
".set pop\n" \
: "=r"(__val)); \
__val; \
})
# elif defined(__i386__)
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
void **__val; \
__asm__("movl %%gs:0, %0" : "=r"(__val)); \
__val; \
})
# elif defined(__x86_64__)
# define ANGLE_ANDROID_GET_GL_TLS() \
({ \
void **__val; \
__asm__("mov %%fs:0, %0" : "=r"(__val)); \
__val; \
})
# else
# error unsupported architecture
# endif
#endif // ANGLE_PLATFORM_ANDROID
} // namespace angle } // namespace angle
namespace egl namespace egl
...@@ -27,6 +89,8 @@ namespace egl ...@@ -27,6 +89,8 @@ namespace egl
class Debug; class Debug;
class Thread; class Thread;
extern thread_local Thread *gCurrentThread;
angle::GlobalMutex &GetGlobalMutex(); angle::GlobalMutex &GetGlobalMutex();
Thread *GetCurrentThread(); Thread *GetCurrentThread();
Debug *GetDebug(); Debug *GetDebug();
...@@ -38,35 +102,28 @@ void SetContextCurrent(Thread *thread, gl::Context *context); ...@@ -38,35 +102,28 @@ void SetContextCurrent(Thread *thread, gl::Context *context);
namespace gl namespace gl
{ {
extern Context *gSingleThreadedContext;
ANGLE_INLINE Context *GetGlobalContext() ANGLE_INLINE Context *GetGlobalContext()
{ {
#if defined(ANGLE_PLATFORM_ANDROID) #if defined(ANGLE_PLATFORM_ANDROID)
// TODO: Replace this branch with a compile time flag (http://anglebug.com/4764) // TODO: Replace this branch with a compile time flag (http://anglebug.com/4764)
if (gUseAndroidOpenGLTlsSlot) if (angle::gUseAndroidOpenGLTlsSlot)
{ {
return static_cast<gl::Context *>(ANGLE_ANDROID_GET_GL_TLS()[kAndroidOpenGLTlsSlot]); return static_cast<gl::Context *>(ANGLE_ANDROID_GET_GL_TLS()[angle::kAndroidOpenGLTlsSlot]);
} }
#endif #endif
if (gSingleThreadedContext) ASSERT(egl::gCurrentThread);
{ return egl::gCurrentThread->getContext();
return gSingleThreadedContext;
}
egl::Thread *thread = egl::GetCurrentThread();
return thread->getContext();
} }
ANGLE_INLINE Context *GetValidGlobalContext() ANGLE_INLINE Context *GetValidGlobalContext()
{ {
#if defined(ANGLE_PLATFORM_ANDROID) #if defined(ANGLE_PLATFORM_ANDROID)
// TODO: Replace this branch with a compile time flag (http://anglebug.com/4764) // TODO: Replace this branch with a compile time flag (http://anglebug.com/4764)
if (gUseAndroidOpenGLTlsSlot) if (angle::gUseAndroidOpenGLTlsSlot)
{ {
Context *context = Context *context =
static_cast<gl::Context *>(ANGLE_ANDROID_GET_GL_TLS()[kAndroidOpenGLTlsSlot]); static_cast<gl::Context *>(ANGLE_ANDROID_GET_GL_TLS()[angle::kAndroidOpenGLTlsSlot]);
if (context && !context->isContextLost()) if (context && !context->isContextLost())
{ {
return context; return context;
...@@ -74,13 +131,7 @@ ANGLE_INLINE Context *GetValidGlobalContext() ...@@ -74,13 +131,7 @@ ANGLE_INLINE Context *GetValidGlobalContext()
} }
#endif #endif
if (gSingleThreadedContext && !gSingleThreadedContext->isContextLost()) return gCurrentValidContext;
{
return gSingleThreadedContext;
}
egl::Thread *thread = egl::GetCurrentThread();
return thread->getValidContext();
} }
ANGLE_INLINE std::unique_lock<angle::GlobalMutex> GetShareGroupLock(const Context *context) ANGLE_INLINE std::unique_lock<angle::GlobalMutex> GetShareGroupLock(const Context *context)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment