[education/rkward] rkward: Fix interrupting nested commands; add test
Thomas Friedrichsmeier
null at kde.org
Sun Sep 14 19:26:16 BST 2025
Git commit 44a00060a6b8c97deb688ed5b844063e1c7d7a39 by Thomas Friedrichsmeier.
Committed on 11/09/2025 at 17:10.
Pushed by tfry into branch 'master'.
Fix interrupting nested commands; add test
M +26 -0 rkward/autotests/core_test.cpp
M +1 -3 rkward/rbackend/rkrbackend.cpp
M +4 -1 rkward/rbackend/rkrsupport.h
https://invent.kde.org/education/rkward/-/commit/44a00060a6b8c97deb688ed5b844063e1c7d7a39
diff --git a/rkward/autotests/core_test.cpp b/rkward/autotests/core_test.cpp
index 4611b7225..8dbb4ed32 100644
--- a/rkward/autotests/core_test.cpp
+++ b/rkward/autotests/core_test.cpp
@@ -475,6 +475,32 @@ class RKWardCoreTest : public QObject {
testLog("%d out of %d commands were actually cancelled", cancelled_commands, commands_out);
}
+ void cancelNestedCommandTest() {
+ auto dotest = [this](int command_flags) {
+ // Create a command that spawns a subcommand, then cancel both
+ auto c = new RCommand(QStringLiteral("print('outerpre')\nrk.call.plugin('rkward::testing_run_code', 'codetorun.text'='print(\\\'innerpre\\\');Sys.sleep(5);print(\\\'innerpost\\\')', submit.mode='submit')\nSys.sleep(5)\nprint('outerpost')"), command_flags);
+ connect(c->notifier(), &RCommandNotifier::commandOutput, this, [](RCommand *, const ROutput &out) {
+ if (out.output.contains(u"innerpre"_s)) RInterface::instance()->cancelAll();
+ });
+ runCommandAsync(c, nullptr, [](RCommand *command) {
+ QVERIFY(command->fullOutput().contains(u"outerpre"_s));
+ QVERIFY(command->fullOutput().contains(u"innerpre"_s));
+ QVERIFY(!command->fullOutput().contains(u"innerpost"_s));
+ QVERIFY(!command->fullOutput().contains(u"outerpost"_s));
+ QVERIFY(command->failed());
+ QVERIFY(command->wasCanceled());
+ });
+ if (!waitForAllFinished()) {
+ waitForAllFinished(10000); // prevent crash in case of test failure
+ QVERIFY(false);
+ }
+ };
+ dotest(RCommand::User);
+ dotest(RCommand::App);
+ dotest(RCommand::Plugin);
+ dotest(RCommand::PriorityCommand);
+ }
+
void priorityCommandTest() {
// This test runs much faster when silencing the log window. Running faster also seems to help triggering the bug.
ScopeHandler sup([]() { RKSettingsModuleWatch::forTestingSuppressOutput(true); }, []() { RKSettingsModuleWatch::forTestingSuppressOutput(false); });
diff --git a/rkward/rbackend/rkrbackend.cpp b/rkward/rbackend/rkrbackend.cpp
index 7f59f540e..28fea25ea 100644
--- a/rkward/rbackend/rkrbackend.cpp
+++ b/rkward/rbackend/rkrbackend.cpp
@@ -188,7 +188,7 @@ void RKRBackend::handleDeferredInterrupts() {
}
}
if (intr) {
- RK_scheduleIntr();
+ RK_doIntr();
}
}
@@ -1339,7 +1339,6 @@ void doPendingPriorityCommands() {
req.command = command;
RKRBackend::this_pointer->handleRequest(&req);
}
- RKRBackend::this_pointer->handleDeferredInterrupts();
}
}
@@ -1487,7 +1486,6 @@ RCommandProxy *RKRBackend::handleRequest2(RBackendRequest *request, bool mayHand
while (command) {
runCommand(command);
commandFinished(false);
- handleDeferredInterrupts();
command = fetchNextCommand();
};
diff --git a/rkward/rbackend/rkrsupport.h b/rkward/rbackend/rkrsupport.h
index 46756f0da..1535bc6b8 100644
--- a/rkward/rbackend/rkrsupport.h
+++ b/rkward/rbackend/rkrsupport.h
@@ -39,7 +39,10 @@ SEXP getRKVariablesEnv(); /**< retries the rkward:::.rk.variables environment. N
/** Replacement for BEGIN_SUSPEND_INTERRUPTS-macro that we cannot easily use */
class InterruptSuspension {
public:
- InterruptSuspension() { old_susp = ROb(R_interrupts_suspended); }
+ InterruptSuspension() {
+ old_susp = ROb(R_interrupts_suspended);
+ ROb(R_interrupts_suspended) = (Rboolean)1;
+ }
~InterruptSuspension() {
ROb(R_interrupts_suspended) = old_susp;
if (ROb(R_interrupts_pending)) RFn::Rf_onintr();
More information about the rkward-tracker
mailing list