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)
return pthread_getspecific(index);
#endif
}
void SetUseAndroidOpenGLTlsSlot(bool platformTypeVulkan)
{
#if defined(ANGLE_PLATFORM_ANDROID)
gUseAndroidOpenGLTlsSlot = platformTypeVulkan;
#endif
}
......@@ -40,84 +40,6 @@ typedef pthread_key_t TLSIndex;
# error Unsupported platform.
#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
// in a destructor function pointer, to allow the thread-local storage
// to be properly deallocated upon thread exit.
......
......@@ -272,6 +272,8 @@ bool IsColorMaskedOut(const BlendState &blend)
}
} // anonymous namespace
thread_local Context *gCurrentValidContext = nullptr;
Context::Context(egl::Display *display,
const egl::Config *config,
const Context *shareContext,
......@@ -2525,6 +2527,9 @@ void Context::setContextLost()
// Stop skipping validation, since many implementation entrypoint assume they can't
// be called when lost, or with null object arguments, etc.
mSkipValidation = false;
// Make sure we update TLS.
gCurrentValidContext = nullptr;
}
GLenum Context::getGraphicsResetStatus()
......
......@@ -800,6 +800,10 @@ class Context final : public egl::LabeledObject, angle::NonCopyable, public angl
OverlayType mOverlay;
};
// Thread-local current valid context bound to the thread.
extern thread_local Context *gCurrentValidContext;
} // namespace gl
#endif // LIBANGLE_CONTEXT_H_
......@@ -563,7 +563,11 @@ Display *Display::GetDisplayFromNativeDisplay(EGLNativeDisplayType nativeDisplay
// No valid display implementation for these attributes
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);
}
......@@ -1993,5 +1997,4 @@ egl::Error Display::handleGPUSwitch()
{
return mImplementation->handleGPUSwitch();
}
} // namespace egl
......@@ -11,7 +11,11 @@
#include "libANGLE/Context.h"
#include "libANGLE/Debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/ErrorStrings.h"
namespace angle
{
bool gUseAndroidOpenGLTlsSlot;
} // namespace angle
namespace egl
{
......@@ -96,18 +100,6 @@ gl::Context *Thread::getContext() const
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
{
if (mContext)
......@@ -116,5 +108,4 @@ Display *Thread::getDisplay() const
}
return nullptr;
}
} // namespace egl
......@@ -13,6 +13,11 @@
#include "libANGLE/Debug.h"
namespace angle
{
extern bool gUseAndroidOpenGLTlsSlot;
} // namespace angle
namespace gl
{
class Context;
......@@ -47,7 +52,6 @@ class Thread : public LabeledObject
Surface *getCurrentDrawSurface() const;
Surface *getCurrentReadSurface() const;
gl::Context *getContext() const;
gl::Context *getValidContext() const;
Display *getDisplay() const;
private:
......
......@@ -15,53 +15,35 @@
#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
{
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);
static_assert(std::is_trivially_destructible<decltype(g_Mutex)>::value,
"global mutex is not trivially destructible");
Thread *AllocateCurrentThread()
void SetContextToAndroidOpenGLTLSSlot(gl::Context *value)
{
ASSERT(threadTLS != TLS_INVALID_INDEX);
if (threadTLS == TLS_INVALID_INDEX)
#if defined(ANGLE_PLATFORM_ANDROID)
if (angle::gUseAndroidOpenGLTlsSlot)
{
return nullptr;
ANGLE_ANDROID_GET_GL_TLS()[angle::kAndroidOpenGLTlsSlot] = static_cast<void *>(value);
}
#endif
}
Thread *thread = new Thread();
if (!SetTLSValue(threadTLS, thread))
{
ERR() << "Could not set thread local storage.";
return nullptr;
}
Thread *AllocateCurrentThread()
{
gCurrentThread = new Thread();
// Initialize fast TLS slot
SetContextToAndroidOpenGLTLSSlot(nullptr);
gl::gCurrentValidContext = nullptr;
return thread;
return gCurrentThread;
}
void AllocateDebug()
......@@ -88,6 +70,8 @@ void AllocateMutex()
} // anonymous namespace
thread_local Thread *gCurrentThread = nullptr;
angle::GlobalMutex &GetGlobalMutex()
{
AllocateMutex();
......@@ -96,16 +80,7 @@ angle::GlobalMutex &GetGlobalMutex()
Thread *GetCurrentThread()
{
// Create a TLS index if one has not been created for this DLL
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.
Thread *current = gCurrentThread;
return (current ? current : AllocateCurrentThread());
}
......@@ -117,26 +92,10 @@ Debug *GetDebug()
void SetContextCurrent(Thread *thread, gl::Context *context)
{
// See above comment on gGlobalContext.
// If the context is in multi-threaded mode, ignore the global 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);
ASSERT(gCurrentThread);
gCurrentThread->setCurrent(context);
SetContextToAndroidOpenGLTLSSlot(context);
gl::gCurrentValidContext = context;
}
} // namespace egl
......@@ -147,11 +106,9 @@ namespace egl
namespace
{
bool DeallocateCurrentThread()
void DeallocateCurrentThread()
{
Thread *thread = static_cast<Thread *>(GetTLSValue(threadTLS));
SafeDelete(thread);
return SetTLSValue(threadTLS, nullptr);
SafeDelete(gCurrentThread);
}
void DeallocateDebug()
......@@ -173,41 +130,15 @@ bool InitializeProcess()
{
ASSERT(g_Debug == nullptr);
AllocateDebug();
AllocateMutex();
threadTLS = CreateTLSIndex();
if (threadTLS == TLS_INVALID_INDEX)
{
return false;
}
return AllocateCurrentThread() != nullptr;
}
bool TerminateProcess()
void TerminateProcess()
{
DeallocateDebug();
DeallocateMutex();
if (!DeallocateCurrentThread())
{
return false;
}
if (threadTLS != TLS_INVALID_INDEX)
{
TLSIndex tlsCopy = threadTLS;
threadTLS = TLS_INVALID_INDEX;
if (!DestroyTLSIndex(tlsCopy))
{
return false;
}
}
return true;
DeallocateCurrentThread();
}
} // anonymous namespace
......@@ -281,10 +212,12 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID)
return static_cast<BOOL>(egl::AllocateCurrentThread() != nullptr);
case DLL_THREAD_DETACH:
return static_cast<BOOL>(egl::DeallocateCurrentThread());
egl::DeallocateCurrentThread();
break;
case DLL_PROCESS_DETACH:
return static_cast<BOOL>(egl::TerminateProcess());
egl::TerminateProcess();
break;
}
return TRUE;
......
......@@ -9,7 +9,6 @@
#ifndef LIBGLESV2_GLOBALSTATE_H_
#define LIBGLESV2_GLOBALSTATE_H_
#include "common/tls.h"
#include "libANGLE/Context.h"
#include "libANGLE/Debug.h"
#include "libANGLE/Thread.h"
......@@ -20,6 +19,69 @@
namespace angle
{
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 egl
......@@ -27,6 +89,8 @@ namespace egl
class Debug;
class Thread;
extern thread_local Thread *gCurrentThread;
angle::GlobalMutex &GetGlobalMutex();
Thread *GetCurrentThread();
Debug *GetDebug();
......@@ -38,35 +102,28 @@ void SetContextCurrent(Thread *thread, gl::Context *context);
namespace gl
{
extern Context *gSingleThreadedContext;
ANGLE_INLINE Context *GetGlobalContext()
{
#if defined(ANGLE_PLATFORM_ANDROID)
// 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
if (gSingleThreadedContext)
{
return gSingleThreadedContext;
}
egl::Thread *thread = egl::GetCurrentThread();
return thread->getContext();
ASSERT(egl::gCurrentThread);
return egl::gCurrentThread->getContext();
}
ANGLE_INLINE Context *GetValidGlobalContext()
{
#if defined(ANGLE_PLATFORM_ANDROID)
// TODO: Replace this branch with a compile time flag (http://anglebug.com/4764)
if (gUseAndroidOpenGLTlsSlot)
if (angle::gUseAndroidOpenGLTlsSlot)
{
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())
{
return context;
......@@ -74,13 +131,7 @@ ANGLE_INLINE Context *GetValidGlobalContext()
}
#endif
if (gSingleThreadedContext && !gSingleThreadedContext->isContextLost())
{
return gSingleThreadedContext;
}
egl::Thread *thread = egl::GetCurrentThread();
return thread->getValidContext();
return gCurrentValidContext;
}
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