Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/lib/app/RvApp/RvSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
//
#include <RvApp/CommandsModule.h>
#include <Python.h>
#include <RvApp/Options.h>
#include <RvApp/RvNodeDefinitions.h>
#include <RvApp/RvSession.h>
Expand Down Expand Up @@ -243,6 +244,22 @@ namespace Rv
// new Shader::Function -> initGLSLVersion() -> glGetString
// -> crashes because no context
IPCore::Application::instance()->loadOptionNodeDefinitions();

// Set TwkMovie's blocking callbacks to release (and re-acquire) the Python GIL while it waits for its
// plugins to initialize in separate threads. If any of them need Python, they will try to acquire the
// GIL and deadlock with the main thread.
//
thread_local PyThreadState* preloadGilState = nullptr;
TwkMovie::GenericIO::setBlockingWaitCallbacks([]() { preloadGilState = PyGILState_Check() ? PyEval_SaveThread() : nullptr; },
[]()
{
if (preloadGilState)
{
PyEval_RestoreThread(preloadGilState);
preloadGilState = nullptr;
}
});

firstTime = false;
}

Expand Down
14 changes: 14 additions & 0 deletions src/lib/image/TwkMovie/MovieIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,14 @@ namespace TwkMovie
if (reader->isFinishedLoading())
return;

if (GenericIO::m_onBlockingWaitRelease)
GenericIO::m_onBlockingWaitRelease();

std::unique_lock<std::mutex> lock(m_mainThread_mutex);
m_mainThread_cv.wait(lock, [&reader] { return reader->isFinishedLoading(); });

if (GenericIO::m_onBlockingWaitAcquire)
GenericIO::m_onBlockingWaitAcquire();
}

MovieReader* GenericIO::Preloader::getReader(std::string_view filename, const MovieInfo& info, Movie::ReadRequest& request)
Expand Down Expand Up @@ -534,6 +540,14 @@ namespace TwkMovie
bool GenericIO::m_loadedAll = false;
bool GenericIO::m_dnxhdDecodingAllowed = true;
GenericIO::Preloader GenericIO::m_preloader;
GenericIO::WaitCallback GenericIO::m_onBlockingWaitRelease;
GenericIO::WaitCallback GenericIO::m_onBlockingWaitAcquire;

void GenericIO::setBlockingWaitCallbacks(WaitCallback onRelease, WaitCallback onAcquire)
{
m_onBlockingWaitRelease = std::move(onRelease);
m_onBlockingWaitAcquire = std::move(onAcquire);
}

void GenericIO::init()
{
Expand Down
18 changes: 18 additions & 0 deletions src/lib/image/TwkMovie/TwkMovie/MovieIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>

#include <TwkFB/FrameBuffer.h>
#include <TwkFB/IO.h>
Expand Down Expand Up @@ -413,6 +414,21 @@ namespace TwkMovie

static void init();

///
/// Optional callbacks invoked in Preloader::waitForFinishedLoading
/// immediately before and after the blocking condition variable wait.
///
/// Applications that hold resouces they shared with workers (e.g. Python's
/// GIL) should set these to release their lock before the wait and reacquire
/// it afterward, so that background threads can avoid deadlocking the main thread
/// when they access the shared resources.
///
/// Both callbacks default to nullptr (no-op).
///

using WaitCallback = std::function<void()>;
static void setBlockingWaitCallbacks(WaitCallback onRelease, WaitCallback onAcquire);

///
/// Shutdown and free generic movieio plugins.
/// This needs to be called in the application main thread in main.
Expand Down Expand Up @@ -539,6 +555,8 @@ namespace TwkMovie
static bool m_loadedAll;
static bool m_dnxhdDecodingAllowed;
static Preloader m_preloader;
static WaitCallback m_onBlockingWaitRelease;
static WaitCallback m_onBlockingWaitAcquire;
};

} // namespace TwkMovie
Expand Down
Loading