1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

OpenGL: Avoid occasional deadlock when destroying contexts

This commit is contained in:
reuk 2022-09-08 18:14:06 +01:00
parent 18aaa86761
commit ae3bfdb1d2
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C

View file

@ -191,7 +191,7 @@ public:
void pause()
{
signalJobShouldExit();
contextsWaitingForFlush->cancelFlush (this);
contextsWaitingForFlush->deregisterContext (this);
messageManagerLock.abort();
if (renderThread != nullptr)
@ -345,7 +345,6 @@ public:
OpenGLContext::deactivateCurrentContext();
}
if (! shouldExit())
contextsWaitingForFlush->flush (this);
return true;
@ -551,6 +550,8 @@ public:
return ThreadPoolJob::jobHasFinished;
}
contextsWaitingForFlush->registerContext (this);
hasInitialised = true;
while (! shouldExit())
@ -777,6 +778,19 @@ public:
class ContextsWaitingForFlush : private AsyncUpdater
{
public:
void registerContext (CachedImage* ctx)
{
const std::lock_guard<std::mutex> lock (mutex);
needsRender.insert_or_assign (ctx, false);
}
void deregisterContext (CachedImage* ctx)
{
const std::lock_guard<std::mutex> lock (mutex);
needsRender.erase (ctx);
condvar.notify_all();
}
/* Ask to swap the CachedImage's buffers on the main thread.
Will block until the buffers have been swapped, or until the swap has been cancelled.
@ -786,8 +800,8 @@ public:
{
const std::lock_guard<std::mutex> lock (mutex);
if (find (ctx) == contexts.cend())
contexts.push_back (ctx);
if (const auto iter = needsRender.find (ctx); iter != needsRender.cend())
iter->second = true;
}
if (MessageManager::getInstance()->isThisTheMessageThread())
@ -799,21 +813,15 @@ public:
triggerAsyncUpdate();
std::unique_lock<std::mutex> lock { mutex };
condvar.wait (lock, [this, ctx] { return find (ctx) == contexts.cend(); });
}
}
/* When a context is destroyed, this function must be called so that flush() can return. */
void cancelFlush (CachedImage* ctx)
condvar.wait (lock, [this, ctx]
{
const std::lock_guard<std::mutex> lock (mutex);
// If the context is still registered, continue once it no longer needs rendering.
if (const auto iter = needsRender.find (ctx); iter != needsRender.cend())
return ! iter->second;
const auto iter = find (ctx);
if (iter != contexts.cend())
{
contexts.erase (iter);
condvar.notify_all();
// If the context is no longer registered, continue so that the thread can be destroyed.
return true;
});
}
}
@ -822,40 +830,35 @@ public:
cancelPendingUpdate();
// There definitely shouldn't still be active CachedImages if this object is being destroyed!
jassert (contexts.empty());
jassert (needsRender.empty());
}
private:
std::mutex mutex;
std::condition_variable condvar;
std::vector<CachedImage*> contexts;
/* Precondition: mutex is locked. */
std::vector<CachedImage*>::const_iterator find (CachedImage* ctx) const
{
// Linear search here because the number of OpenGL contexts probably won't
// ever be bigger than double/triple digits
return std::find (contexts.cbegin(), contexts.cend(), ctx);
}
std::map<CachedImage*, bool> needsRender;
/* Swaps the buffers for each of the pending contexts, then notifies all rendering threads
that they may continue.
*/
void handleAsyncUpdate() override
{
std::unique_lock<std::mutex> lock (mutex);
const std::unique_lock<std::mutex> lock (mutex);
for (auto* ctx : contexts)
for (auto& [ctx, shouldSwap] : needsRender)
{
if (! shouldSwap)
continue;
auto& native = *ctx->nativeContext;
OpenGLContext::NativeContext::Locker nativeContextLocker (native);
const OpenGLContext::NativeContext::Locker nativeContextLocker { native };
native.makeActive();
native.swapBuffers();
native.deactivateCurrentContext();
shouldSwap = false;
}
contexts.clear();
condvar.notify_all();
}
};