Fix #219: Add threading support with mcrfpy.lock() context manager
Enables background Python threads to safely modify UI objects by
synchronizing with the render loop at frame boundaries.
Implementation:
- FrameLock class provides mutex/condvar synchronization
- GIL released during window.display() allowing background threads to run
- Safe window opens between frames for synchronized UI updates
- mcrfpy.lock() context manager blocks until safe window, then executes
- Main thread detection: lock() is a no-op when called from callbacks
or script initialization (already synchronized)
Usage:
import threading
import mcrfpy
def background_worker():
with mcrfpy.lock(): # Blocks until safe
player.x = new_x # Safe to modify UI
threading.Thread(target=background_worker).start()
The lock works transparently from any context - background threads get
actual synchronization, main thread calls (callbacks, init) get no-op.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
14a6520593
commit
257e52327b
5 changed files with 269 additions and 1 deletions
|
|
@ -25,6 +25,7 @@
|
|||
#include "PyHeightMap.h" // Procedural generation heightmap (#193)
|
||||
#include "PyBSP.h" // Procedural generation BSP (#202-206)
|
||||
#include "PyNoiseSource.h" // Procedural generation noise (#207-208)
|
||||
#include "PyLock.h" // Thread synchronization (#219)
|
||||
#include "McRogueFaceVersion.h"
|
||||
#include "GameEngine.h"
|
||||
#include "ImGuiConsole.h"
|
||||
|
|
@ -292,6 +293,17 @@ static PyMethodDef mcrfpyMethods[] = {
|
|||
{"__getattr__", mcrfpy_module_getattr, METH_VARARGS,
|
||||
"Module-level __getattr__ for dynamic properties (current_scene, scenes)"},
|
||||
|
||||
// #219: Thread synchronization
|
||||
{"lock", PyLock::lock, METH_NOARGS,
|
||||
MCRF_FUNCTION(lock,
|
||||
MCRF_SIG("()", "_LockContext"),
|
||||
MCRF_DESC("Get a context manager for thread-safe UI updates from background threads."),
|
||||
MCRF_RETURNS("_LockContext: A context manager that blocks until safe to modify UI")
|
||||
MCRF_NOTE("Use with `with mcrfpy.lock():` to safely modify UI objects from a background thread. "
|
||||
"The context manager blocks until the render loop reaches a safe point between frames. "
|
||||
"Without this, modifying UI from threads may cause visual glitches or crashes.")
|
||||
)},
|
||||
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
|
@ -479,7 +491,13 @@ PyObject* PyInit_mcrfpy()
|
|||
PyUILineType.tp_weaklistoffset = offsetof(PyUILineObject, weakreflist);
|
||||
PyUICircleType.tp_weaklistoffset = offsetof(PyUICircleObject, weakreflist);
|
||||
PyUIArcType.tp_weaklistoffset = offsetof(PyUIArcObject, weakreflist);
|
||||
|
||||
|
||||
// #219 - Initialize PyLock context manager type
|
||||
if (PyLock::init() < 0) {
|
||||
std::cout << "ERROR: PyLock::init() failed" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Process exported types - PyType_Ready AND add to module
|
||||
int i = 0;
|
||||
auto t = exported_types[i];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue