App: Invoke signalBeforeRecompute() on the GUI thread
Historically, `App::Document::recompute()` ran entirely on the **main**
(GUI) thread and directly emitted `signalBeforeRecompute()`.
* Add-ons like **Assembly3** and others depend on that signal for
setup/teardown hooks before any recompute work begins.
* After offloading `recompute()` into a background worker thread to keep
the UI responsive, calling `signalBeforeRecompute()` directly from the
worker would break thread-affinity rules and silently break
compatibility with those add-ons.
**Solution**
1. **Introduce a generic hook** (`PreRecomputeHook`) in
**App::Document**:
* A `std::function<void()>` that, if set, is invoked at the very
start of `recompute()`.
* Core code stays Qt-free—only knows to call a callback if one
exists.
2. **Wire up the hook in `Gui::Document`**:
* In the GUI wrapper’s constructor, install a hook that calls
`callSignalBeforeRecompute()`.
* `callSignalBeforeRecompute()` uses `QMetaObject::invokeMethod(...,
Qt::BlockingQueuedConnection)` to enqueue `signalBeforeRecompute()` on
the GUI thread and **block** the worker until it completes.
* If already on the GUI thread, it simply calls the signal directly.
3. **Maintain add-on compatibility**:
* From the add-on’s perspective nothing changes—they still receive
`signalBeforeRecompute()` on the main thread before any recompute work.
* Internally, the recompute body now runs on a worker thread,
improving UI responsiveness without breaking existing hooks.
**Result**
* **Recompute** remains fully backward-compatible for add-ons like
Assembly3.
* **UI thread** still handles all GUI-related signaling.
* **Worker thread** performs the actual heavy lifting, unblocked only
once the GUI is primed and all pre-recompute signals have been
delivered.
This commit is contained in:
committed by
Benjamin Nauck
parent
6cd828d986
commit
1f5e0f6494
@@ -2878,6 +2878,11 @@ void Document::renameObjectIdentifiers(
|
||||
}
|
||||
}
|
||||
|
||||
void Document::setPreRecomputeHook(const PreRecomputeHook& hook)
|
||||
{
|
||||
d->_preRecomputeHook = hook;
|
||||
}
|
||||
|
||||
int Document::recompute(const std::vector<DocumentObject*>& objs,
|
||||
bool force,
|
||||
bool* hasError,
|
||||
@@ -2918,7 +2923,13 @@ int Document::recompute(const std::vector<DocumentObject*>& objs,
|
||||
FC_TIME_INIT(t);
|
||||
|
||||
Base::ObjectStatusLocker<Document::Status, Document> exe(Document::Recomputing, this);
|
||||
signalBeforeRecompute(*this);
|
||||
|
||||
// This will hop into the main thread, fire signalBeforeRecompute(),
|
||||
// and *block* the worker until the main thread is done, avoiding races
|
||||
// between any running Python code and the rest of the recompute call.
|
||||
if (d->_preRecomputeHook) {
|
||||
d->_preRecomputeHook();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// FIXME Comment by Realthunder:
|
||||
|
||||
Reference in New Issue
Block a user