mirror of
https://github.com/SFML/SFML.git
synced 2024-12-01 15:51:04 +08:00
Replaced TransientContextLock implementation with a more elaborate one which relies on locking a single mutex and thus avoids lock order inversion. Fixes #1165.
This commit is contained in:
parent
022f1590d8
commit
af5244d85d
@ -75,9 +75,6 @@ protected:
|
|||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
~TransientContextLock();
|
~TransientContextLock();
|
||||||
|
|
||||||
private:
|
|
||||||
Context* m_context; ///< Temporary context, in case we needed to create one
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
// Headers
|
// Headers
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
#include <SFML/Window/GlContext.hpp>
|
#include <SFML/Window/GlContext.hpp>
|
||||||
|
#include <SFML/Window/Context.hpp>
|
||||||
#include <SFML/System/ThreadLocalPtr.hpp>
|
#include <SFML/System/ThreadLocalPtr.hpp>
|
||||||
#include <SFML/System/Mutex.hpp>
|
#include <SFML/System/Mutex.hpp>
|
||||||
#include <SFML/System/Lock.hpp>
|
#include <SFML/System/Lock.hpp>
|
||||||
@ -131,18 +132,70 @@ namespace
|
|||||||
// We need to make sure that no operating system context
|
// We need to make sure that no operating system context
|
||||||
// or pixel format operations are performed simultaneously
|
// or pixel format operations are performed simultaneously
|
||||||
// This mutex is also used to protect the shared context
|
// This mutex is also used to protect the shared context
|
||||||
// from being locked on multiple threads
|
// from being locked on multiple threads and for managing
|
||||||
|
// the resource count
|
||||||
sf::Mutex mutex;
|
sf::Mutex mutex;
|
||||||
|
|
||||||
|
// OpenGL resources counter
|
||||||
|
unsigned int resourceCount = 0;
|
||||||
|
|
||||||
// This per-thread variable holds the current context for each thread
|
// This per-thread variable holds the current context for each thread
|
||||||
sf::ThreadLocalPtr<sf::priv::GlContext> currentContext(NULL);
|
sf::ThreadLocalPtr<sf::priv::GlContext> currentContext(NULL);
|
||||||
|
|
||||||
// The hidden, inactive context that will be shared with all other contexts
|
// The hidden, inactive context that will be shared with all other contexts
|
||||||
ContextType* sharedContext = NULL;
|
ContextType* sharedContext = NULL;
|
||||||
|
|
||||||
// This per-thread variable is set to point to the shared context
|
// This structure contains all the state necessary to
|
||||||
// if we had to acquire it when a TransientContextLock was required
|
// track TransientContext usage
|
||||||
sf::ThreadLocalPtr<sf::priv::GlContext> currentSharedContext(NULL);
|
struct TransientContext : private sf::NonCopyable
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Constructor
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
TransientContext() :
|
||||||
|
referenceCount (0),
|
||||||
|
context (0),
|
||||||
|
sharedContextLock(0),
|
||||||
|
useSharedContext (false)
|
||||||
|
{
|
||||||
|
if (resourceCount == 0)
|
||||||
|
{
|
||||||
|
context = new sf::Context;
|
||||||
|
}
|
||||||
|
else if (!currentContext)
|
||||||
|
{
|
||||||
|
sharedContextLock = new sf::Lock(mutex);
|
||||||
|
useSharedContext = true;
|
||||||
|
sharedContext->setActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// \brief Destructor
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
~TransientContext()
|
||||||
|
{
|
||||||
|
if (useSharedContext)
|
||||||
|
sharedContext->setActive(false);
|
||||||
|
|
||||||
|
delete sharedContextLock;
|
||||||
|
delete context;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// Member data
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
unsigned int referenceCount;
|
||||||
|
sf::Context* context;
|
||||||
|
sf::Lock* sharedContextLock;
|
||||||
|
bool useSharedContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This per-thread variable tracks if and how a transient
|
||||||
|
// context is currently being used on the current thread
|
||||||
|
sf::ThreadLocalPtr<TransientContext> transientContext(NULL);
|
||||||
|
|
||||||
// Supported OpenGL extensions
|
// Supported OpenGL extensions
|
||||||
std::vector<std::string> extensions;
|
std::vector<std::string> extensions;
|
||||||
@ -154,12 +207,21 @@ namespace sf
|
|||||||
namespace priv
|
namespace priv
|
||||||
{
|
{
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::globalInit()
|
void GlContext::initResource()
|
||||||
{
|
{
|
||||||
|
// Protect from concurrent access
|
||||||
Lock lock(mutex);
|
Lock lock(mutex);
|
||||||
|
|
||||||
|
// If this is the very first resource, trigger the global context initialization
|
||||||
|
if (resourceCount == 0)
|
||||||
|
{
|
||||||
if (sharedContext)
|
if (sharedContext)
|
||||||
|
{
|
||||||
|
// Increment the resources counter
|
||||||
|
resourceCount++;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create the shared context
|
// Create the shared context
|
||||||
sharedContext = new ContextType(NULL);
|
sharedContext = new ContextType(NULL);
|
||||||
@ -213,48 +275,70 @@ void GlContext::globalInit()
|
|||||||
|
|
||||||
// Deactivate the shared context so that others can activate it when necessary
|
// Deactivate the shared context so that others can activate it when necessary
|
||||||
sharedContext->setActive(false);
|
sharedContext->setActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the resources counter
|
||||||
|
resourceCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::globalCleanup()
|
void GlContext::cleanupResource()
|
||||||
{
|
{
|
||||||
|
// Protect from concurrent access
|
||||||
Lock lock(mutex);
|
Lock lock(mutex);
|
||||||
|
|
||||||
|
// Decrement the resources counter
|
||||||
|
resourceCount--;
|
||||||
|
|
||||||
|
// If there's no more resource alive, we can trigger the global context cleanup
|
||||||
|
if (resourceCount == 0)
|
||||||
|
{
|
||||||
if (!sharedContext)
|
if (!sharedContext)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Destroy the shared context
|
// Destroy the shared context
|
||||||
delete sharedContext;
|
delete sharedContext;
|
||||||
sharedContext = NULL;
|
sharedContext = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::acquireTransientContext()
|
void GlContext::acquireTransientContext()
|
||||||
{
|
{
|
||||||
// If a capable context is already active on this thread
|
// Protect from concurrent access
|
||||||
// there is no need to use the shared context for the operation
|
Lock lock(mutex);
|
||||||
if (currentContext)
|
|
||||||
{
|
|
||||||
currentSharedContext = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex.lock();
|
// If this is the first TransientContextLock on this thread
|
||||||
currentSharedContext = sharedContext;
|
// construct the state object
|
||||||
sharedContext->setActive(true);
|
if (!transientContext)
|
||||||
|
transientContext = new TransientContext;
|
||||||
|
|
||||||
|
// Increase the reference count
|
||||||
|
transientContext->referenceCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
void GlContext::releaseTransientContext()
|
void GlContext::releaseTransientContext()
|
||||||
{
|
{
|
||||||
if (!currentSharedContext)
|
// Protect from concurrent access
|
||||||
return;
|
Lock lock(mutex);
|
||||||
|
|
||||||
sharedContext->setActive(false);
|
// Make sure a matching acquireTransientContext() was called
|
||||||
mutex.unlock();
|
assert(transientContext);
|
||||||
|
|
||||||
|
// Decrease the reference count
|
||||||
|
transientContext->referenceCount--;
|
||||||
|
|
||||||
|
// If this is the last TransientContextLock that is released
|
||||||
|
// destroy the state object
|
||||||
|
if (transientContext->referenceCount == 0)
|
||||||
|
{
|
||||||
|
delete transientContext;
|
||||||
|
transientContext = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,28 +49,25 @@ class GlContext : NonCopyable
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Perform the global initialization
|
/// \brief Perform resource initialization
|
||||||
///
|
///
|
||||||
/// This function is called once, before the very first OpenGL
|
/// This function is called every time an OpenGL resource is
|
||||||
/// resource is created. It makes sure that everything is ready
|
/// created. When the first resource is initialized, it makes
|
||||||
/// for contexts to work properly.
|
/// sure that everything is ready for contexts to work properly.
|
||||||
/// Note: this function doesn't need to be thread-safe, as it
|
|
||||||
/// can be called only once.
|
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static void globalInit();
|
static void initResource();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Perform the global cleanup
|
/// \brief Perform resource cleanup
|
||||||
///
|
///
|
||||||
/// This function is called after the very last OpenGL resource
|
/// This function is called every time an OpenGL resource is
|
||||||
/// is destroyed. It makes sure that everything that was
|
/// destroyed. When the last resource is destroyed, it makes
|
||||||
/// created by initialize() is properly released.
|
/// sure that everything that was created by initResource()
|
||||||
/// Note: this function doesn't need to be thread-safe, as it
|
/// is properly released.
|
||||||
/// can be called only once.
|
|
||||||
///
|
///
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
static void globalCleanup();
|
static void cleanupResource();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
/// \brief Acquires a context for short-term use on the current thread
|
/// \brief Acquires a context for short-term use on the current thread
|
||||||
|
@ -27,17 +27,6 @@
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
#include <SFML/Window/GlResource.hpp>
|
#include <SFML/Window/GlResource.hpp>
|
||||||
#include <SFML/Window/GlContext.hpp>
|
#include <SFML/Window/GlContext.hpp>
|
||||||
#include <SFML/Window/Context.hpp>
|
|
||||||
#include <SFML/System/Mutex.hpp>
|
|
||||||
#include <SFML/System/Lock.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// OpenGL resources counter and its mutex
|
|
||||||
unsigned int count = 0;
|
|
||||||
sf::Mutex mutex;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
@ -45,45 +34,20 @@ namespace sf
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlResource::GlResource()
|
GlResource::GlResource()
|
||||||
{
|
{
|
||||||
// Protect from concurrent access
|
priv::GlContext::initResource();
|
||||||
Lock lock(mutex);
|
|
||||||
|
|
||||||
// If this is the very first resource, trigger the global context initialization
|
|
||||||
if (count == 0)
|
|
||||||
priv::GlContext::globalInit();
|
|
||||||
|
|
||||||
// Increment the resources counter
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlResource::~GlResource()
|
GlResource::~GlResource()
|
||||||
{
|
{
|
||||||
// Protect from concurrent access
|
priv::GlContext::cleanupResource();
|
||||||
Lock lock(mutex);
|
|
||||||
|
|
||||||
// Decrement the resources counter
|
|
||||||
count--;
|
|
||||||
|
|
||||||
// If there's no more resource alive, we can trigger the global context cleanup
|
|
||||||
if (count == 0)
|
|
||||||
priv::GlContext::globalCleanup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlResource::TransientContextLock::TransientContextLock() :
|
GlResource::TransientContextLock::TransientContextLock()
|
||||||
m_context(0)
|
|
||||||
{
|
{
|
||||||
Lock lock(mutex);
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
m_context = new Context;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv::GlContext::acquireTransientContext();
|
priv::GlContext::acquireTransientContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,12 +55,6 @@ m_context(0)
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
GlResource::TransientContextLock::~TransientContextLock()
|
GlResource::TransientContextLock::~TransientContextLock()
|
||||||
{
|
{
|
||||||
if (m_context)
|
|
||||||
{
|
|
||||||
delete m_context;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
priv::GlContext::releaseTransientContext();
|
priv::GlContext::releaseTransientContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user