From: "Joshua K." <j.kobschaetzki@campus.tu-berlin.de>
Hi,
I've recently started working on writing a k6[^0] adapter[^1] for 
stress-testing a server compatible with the isabelle server protocol. 
The main purpose of the adapter is to analyze the performance 
characteristics and behavior of Isabelle server with multiple users and 
sessions. During the initial testing phase, I encountered some 
unexpected behavior related to the session_stop command. It seems to 
trigger a race-condition or edge case, especially when called in quick 
succession after start_session, use_theories, and purge_theories[^2] in 
a loop. Typically, this occurs on the second to fourth iteration.
Here are the observed stack traces from the Isabelle server's stdout:
Exception in thread "event_timer"
*** Inactive prover input thread for command 
"Document.discontinue_execution"
     at isabelle.Exn$ERROR$.apply(exn.scala:25)
     at isabelle.Exn$.error(exn.scala:29)
     at isabelle.package$.$init$$$anonfun$1(ROOT.scala:9)
     at isabelle.Prover.protocol_command_raw(prover.scala:296)
     at isabelle.Prover.protocol_command_args(prover.scala:301)
     at isabelle.Prover.protocol_command(prover.scala:305)
     at isabelle.Protocol.discontinue_execution(protocol.scala:395)
     at isabelle.Protocol.discontinue_execution$(protocol.scala:306)
     at isabelle.Prover.discontinue_execution(prover.scala:71)
     at isabelle.Session.handle_raw_edits$1(session.scala:397)
     at isabelle.Session.$init$$$anonfun$3(session.scala:648)
     at 
isabelle.Consumer_Thread$.consume_single$1$$anonfun$1(consumer_thread.scala:28)
     at isabelle.Exn$.capture(exn.scala:60)
     at isabelle.Consumer_Thread$.consume_single$1(consumer_thread.scala:28)
     at isabelle.Consumer_Thread$.fork$$anonfun$2(consumer_thread.scala:34)
     at isabelle.Consumer_Thread.process(consumer_thread.scala:90)
     at isabelle.Consumer_Thread.main_loop(consumer_thread.scala:108)
     at isabelle.Consumer_Thread.$init$$$anonfun$1(consumer_thread.scala:50)
     at 
isabelle.Consumer_Thread.$init$$$anonfun$adapted$1(consumer_thread.scala:50)
     at scala.Function0.apply$mcV$sp(Function0.scala:42)
     at isabelle.Isabelle_Thread$.$anonfun$2(isabelle_thread.scala:64)
     at isabelle.Isabelle_Thread.run(isabelle_thread.scala:140)
Exception in thread "event_timer"
*** Consumer thread not active: "Isabelle.Session.manager"
     at isabelle.Exn$ERROR$.apply(exn.scala:25)
     at isabelle.Exn$.error(exn.scala:29)
     at isabelle.package$.$init$$$anonfun$1(ROOT.scala:9)
     at isabelle.Consumer_Thread.request(consumer_thread.scala:78)
     at isabelle.Consumer_Thread.send_wait(consumer_thread.scala:118)
     at isabelle.Session.update(session.scala:760)
     at 
isabelle.Headless$Resources.load_theories$$anonfun$1(headless.scala:675)
     at isabelle.Synchronized.change(synchronized.scala:67)
     at isabelle.Headless$Resources.load_theories(headless.scala:676)
     at 
isabelle.Headless$Session.check_state$1$$anonfun$3(headless.scala:338)
     at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
     at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
     at scala.Option$WithFilter.foreach(Option.scala:437)
     at isabelle.Headless$Session.check_state$1(headless.scala:338)
     at 
isabelle.Headless$Session.check_progress$lzyINIT1$1$$anonfun$1(headless.scala:347)
     at 
isabelle.Headless$Session.check_progress$lzyINIT1$1$$anonfun$adapted$1(headless.scala:349)
     at scala.Function0.apply$mcV$sp(Function0.scala:42)
     at isabelle.Event_Timer$$anon$1.run(event_timer.scala:28)
     at java.base/java.util.TimerThread.mainLoop(Timer.java:566)
     at java.base/java.util.TimerThread.run(Timer.java:516)
Interestingly, during testing, the specific theory for use_theories 
didn't seem to affect the outcome.
However, the exception doesn't occur if a delay of >2-3 seconds is 
inserted between session_stop and use_theories calls. This led me to 
suspect a race condition, but I couldn't pinpoint the exact cause in the 
Server code.
I would greatly appreciate any advice or guidance on how to address this 
behavior. Is there a way to navigate the timer issues without resorting 
to delaying the execution on the client?
Best regards,
Joshua Kobschätzki
[^0]: https://k6.io/open-source/
[^1]: https://git.tu-berlin.de/cobalt.rocks/isa-bench, a MRE, if 
requested, can be made available
[^2]: Command log -- executed after successful client<>server handshake:
session_start {"session": "HOL", "options": 
["headless_consolidate_delay=0.5", "headless_prune_delay=5", 
"show_states"]}\n
use_theories {"session_id": "{{ previously retrieved session id }}", 
"theories": ["{{ some theory, the specific theory didn't matter during 
testing }}"], "master_dir": "{{ theory path }}"}\n
purge_theories {"session_id": "{{ previously retrieved session id }}", 
"all": true}\n
session_stop {"session_id": "{{ previously retrieved session id }}"}\n
Timer already cancelled.","task": "<task id for session_close>"}
Last updated: Oct 30 2025 at 20:23 UTC