[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