* @brief Runs blocking work on a worker thread; a main-thread reentrancy guard makes a * re-entrant fs tool fail fast rather than spin a second nested event loop. */
| 583 | * re-entrant fs tool fail fast rather than spin a second nested event loop. |
| 584 | */ |
| 585 | static QJsonObject runOffMainThread(const std::function<QJsonObject()>& work) |
| 586 | { |
| 587 | if (QThread::currentThread() != QCoreApplication::instance()->thread()) |
| 588 | return work(); |
| 589 | |
| 590 | static bool s_inNestedLoop = false; |
| 591 | if (s_inNestedLoop) { |
| 592 | QJsonObject out; |
| 593 | out[QStringLiteral("ok")] = false; |
| 594 | out[QStringLiteral("error")] = QStringLiteral("fs_tool_busy"); |
| 595 | out[QStringLiteral("hint")] = QStringLiteral("Another filesystem tool is still running. Wait " |
| 596 | "for it to finish, then retry this call."); |
| 597 | return out; |
| 598 | } |
| 599 | |
| 600 | s_inNestedLoop = true; |
| 601 | const QScopeGuard clearGuard([] { s_inNestedLoop = false; }); |
| 602 | |
| 603 | QJsonObject result; |
| 604 | QEventLoop loop; |
| 605 | QThread* worker = QThread::create([&]() { result = work(); }); |
| 606 | QObject::connect(worker, &QThread::finished, &loop, &QEventLoop::quit); |
| 607 | worker->start(); |
| 608 | loop.exec(); |
| 609 | worker->wait(); |
| 610 | delete worker; |
| 611 | return result; |
| 612 | } |
| 613 | |
| 614 | /** |
| 615 | * @brief Routes an fs.* tool call to the FileSandbox primitive. |
no test coverage detected