File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 66//
77//
88#include < RvApp/CommandsModule.h>
9+ #include < Python.h>
910#include < RvApp/Options.h>
1011#include < RvApp/RvNodeDefinitions.h>
1112#include < RvApp/RvSession.h>
@@ -243,6 +244,22 @@ namespace Rv
243244 // new Shader::Function -> initGLSLVersion() -> glGetString
244245 // -> crashes because no context
245246 IPCore::Application::instance ()->loadOptionNodeDefinitions ();
247+
248+ // Set TwkMovie's blocking callbacks to release (and re-acquire) the Python GIL while it waits for its
249+ // plugins to initialize in separate threads. If any of them need Python, they will try to acquire the
250+ // GIL and deadlock with the main thread.
251+ //
252+ thread_local PyThreadState* preloadGilState = nullptr ;
253+ TwkMovie::GenericIO::setBlockingWaitCallbacks ([]() { preloadGilState = PyGILState_Check () ? PyEval_SaveThread () : nullptr ; },
254+ []()
255+ {
256+ if (preloadGilState)
257+ {
258+ PyEval_RestoreThread (preloadGilState);
259+ preloadGilState = nullptr ;
260+ }
261+ });
262+
246263 firstTime = false ;
247264 }
248265
Original file line number Diff line number Diff line change @@ -212,8 +212,14 @@ namespace TwkMovie
212212 if (reader->isFinishedLoading ())
213213 return ;
214214
215+ if (GenericIO::m_onBlockingWaitRelease)
216+ GenericIO::m_onBlockingWaitRelease ();
217+
215218 std::unique_lock<std::mutex> lock (m_mainThread_mutex);
216219 m_mainThread_cv.wait (lock, [&reader] { return reader->isFinishedLoading (); });
220+
221+ if (GenericIO::m_onBlockingWaitAcquire)
222+ GenericIO::m_onBlockingWaitAcquire ();
217223 }
218224
219225 MovieReader* GenericIO::Preloader::getReader (std::string_view filename, const MovieInfo& info, Movie::ReadRequest& request)
@@ -534,6 +540,14 @@ namespace TwkMovie
534540 bool GenericIO::m_loadedAll = false ;
535541 bool GenericIO::m_dnxhdDecodingAllowed = true ;
536542 GenericIO::Preloader GenericIO::m_preloader;
543+ GenericIO::WaitCallback GenericIO::m_onBlockingWaitRelease;
544+ GenericIO::WaitCallback GenericIO::m_onBlockingWaitAcquire;
545+
546+ void GenericIO::setBlockingWaitCallbacks (WaitCallback onRelease, WaitCallback onAcquire)
547+ {
548+ m_onBlockingWaitRelease = std::move (onRelease);
549+ m_onBlockingWaitAcquire = std::move (onAcquire);
550+ }
537551
538552 void GenericIO::init ()
539553 {
Original file line number Diff line number Diff line change @@ -413,6 +413,21 @@ namespace TwkMovie
413413
414414 static void init ();
415415
416+ // /
417+ // / Optional callbacks invoked in Preloader::waitForFinishedLoading
418+ // / immediately before and after the blocking condition variable wait.
419+ // /
420+ // / Applications that hold resouces they shared with workers (e.g. Python's
421+ // / GIL) should set these to release their lock before the wait and reacquire
422+ // / it afterward, so that background threads can avoid deadlocking the main thread
423+ // / when they access the shared resources.
424+ // /
425+ // / Both callbacks default to nullptr (no-op).
426+ // /
427+
428+ using WaitCallback = std::function<void ()>;
429+ static void setBlockingWaitCallbacks (WaitCallback onRelease, WaitCallback onAcquire);
430+
416431 // /
417432 // / Shutdown and free generic movieio plugins.
418433 // / This needs to be called in the application main thread in main.
@@ -539,6 +554,8 @@ namespace TwkMovie
539554 static bool m_loadedAll;
540555 static bool m_dnxhdDecodingAllowed;
541556 static Preloader m_preloader;
557+ static WaitCallback m_onBlockingWaitRelease;
558+ static WaitCallback m_onBlockingWaitAcquire;
542559 };
543560
544561} // namespace TwkMovie
You can’t perform that action at this time.
0 commit comments