16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-18 00:49:19 +02:00

Potential fix for hanging python threads

This commit is contained in:
2026-05-01 23:35:35 +02:00
parent 302eed9403
commit da67acc993

View File

@@ -312,15 +312,37 @@ void python_taskqueue::exit()
m_condition.notify_all(); m_condition.notify_all();
// let them free up their shit before we proceed // let them free up their shit before we proceed
m_workers = {}; m_workers = {};
// get rid of the leftover tasks // drop any pending render/upload tasks. the workers are joined at this
// with the workers dead we don't have to worry about concurrent access anymore // point so the locks are strictly defensive, but they cost nothing and
for (auto task : m_tasks.data) // document intent. clearing the deque also actually releases the tasks,
// which the previous code did not do (cancel() is a no-op stub).
{ {
task->cancel(); std::lock_guard<std::mutex> lock(m_tasks.mutex);
for (auto &task : m_tasks.data)
{
task->cancel();
}
m_tasks.data.clear();
} }
// take a bow {
std::lock_guard<std::mutex> lock(m_uploadtasks.mutex);
m_uploadtasks.data.clear();
}
// reclaim cached python objects while the interpreter is still alive,
// so no Py_DECREF lands on a finalized interpreter during later teardown
acquire_lock(); acquire_lock();
for (auto &entry : m_renderers)
{
Py_XDECREF(entry.second);
}
m_renderers.clear();
Py_XDECREF(m_stderr);
m_stderr = nullptr;
Py_XDECREF(m_main);
m_main = nullptr;
// take a bow
Py_Finalize(); Py_Finalize();
m_initialized = false;
} }
// adds specified task along with provided collection of data to the work queue. returns true on success // adds specified task along with provided collection of data to the work queue. returns true on success
@@ -518,8 +540,10 @@ void python_taskqueue::run(GLFWwindow *Context, rendertask_sequence &Tasks, uplo
// TBD, TODO: add some idle time between tasks in case we're on a single thread cpu? // TBD, TODO: add some idle time between tasks in case we're on a single thread cpu?
} while (task != nullptr); } while (task != nullptr);
// if there's nothing left to do wait until there is // if there's nothing left to do wait until there is
// but check every now and then on your own to minimize potential deadlock situations // short timeout: notify_all() in exit() can race with the worker's
Condition.wait_for(std::chrono::seconds(5)); // transition into the wait, and the run loop also drives prompt
// shutdown checks
Condition.wait_for(std::chrono::milliseconds(250));
} }
// clean up thread state data // clean up thread state data
PyEval_AcquireLock(); PyEval_AcquireLock();
@@ -527,6 +551,12 @@ void python_taskqueue::run(GLFWwindow *Context, rendertask_sequence &Tasks, uplo
PyThreadState_Clear(threadstate); PyThreadState_Clear(threadstate);
PyThreadState_Delete(threadstate); PyThreadState_Delete(threadstate);
PyEval_ReleaseLock(); PyEval_ReleaseLock();
// detach the GL context before the worker terminates; some drivers
// (NVIDIA on X11, certain Mesa/Wayland configs) hang in process teardown
// if a context is still current on a dying thread
if (Context)
glfwMakeContextCurrent(nullptr);
} }
void python_taskqueue::update() void python_taskqueue::update()