This latest (and most likely last) GSoC achievement I’m going to talk about was probably one of the most difficult ones, whose hardness was potentialized by the urgency of the Final Term week. Its stressful to try finding the right screws to twist when there’s a time bomb by your side. Lol
Enough of metaphors. Let’s go to what matters (maybe…).
User Input
As can be implied from the previous post, input management was one of the last big missing pieces of our Jupyter Scilab kernel and, as experienced showed, a rather troublesome one. Being the only type of message exchange (request and reply) for what the request is dispatched from kernel, and even during the processing of a previously received command, its implement breaks the wait for request -> process -> send reply control flow entirely.
input_request message specification
input_reply message specification
On the bright side, this breakage would be needed anyways, for allowing the kernel to process high priority requests (mainly shudown) during long command executions. That was solved by spamming a detached thread to wait for execution results (that can issue output or input requests), and immediately returning on the main update thread to poll for new messages:
Our HandleExecutionRequest method, from JupyterKernel class
With execution thread free to run independently, I turn my attention to two big concerns I had in this project since the beginning: how to detect when execution ends and when the console queries user for input during it.
For the second, I soon noticed the existence of a ConsoleIsWaitingForInput function on the scilab module folder. So… Nice and easy, right ?
From ConsoleIsWaitingForInput.cpp
Damn. Not quite… The function is only useful when the Java Virtual Machine is running, which is not the case when we’re not using GUI functionality. But there should be another way to do it, as Scilab actually has a CLI shell.
So… What to do ?
When the interfaces for requesting or setting values doesn’t seem that obvious, I usually I see no other option but to dig deep into codepaths of functions that supposedly do the same thing that I want my code to do. In this case, one that quickly comes to mind is Scilab’s input function:
From input.sci
You don’t need to be that used to Scilab language to think this is the relevant part for us. The name mscanf reminds of C’s scanf function for getting console input, so we should be on the right track. The mscanf is another Scilab function, implemented in C++:
From sci_mscanf.cpp
Bingo ! As setScilabCommand method is not used anywhere else, we can assume that anytime the internal variable is set to 0, which can be acessed through the correspondent isScilabCommand method, the console is waiting for user input.
Execution Management
Now, the first problem, of verifying command execution completion (or console readiness for receiving a new one), although sounding simple, was a rather annoying one. In summary, after trying to play with the ThreadManagement class methods (based on the code I was studying), and getting a lot of deadlocks in the process, I followed Clément’s suggestion to look at sci_execstr.cpp source file:
From sci_execstr.cpp
Something that I also noticed on other functions that handle code execution, but which I was neglecting so far, is the manipulation of the prompt mode. Some brief inspection of the ConfigVariable class header file showed that the value -1 represents the silent mode, defined before execution, while 2 indicates the avaibility for new commands (prompt mode).
I don’t think that considering only these two modes is enough, but they cover most of the cases, and allowed me to ditch ThreadManagement signals and waits to have a somehow working execution management:
Our HandleExecutionReply method (runs in a detached thread), from JupyterKernel class
Our HandleInputRequest method, from JupyterKernel class
Our HandleInputReply method, from JupyterKernel class
Results
With all set up, the bug of a new Jupyter prompt appearing appearing before the results of the previous command is fixed (actually, it is much less likely to happen…), and input commands work as intended. To be fair, there are still corner cases I can think of and synchronization issues that I wish to fix for sure (hopefully, without abusing mutexes), but that’ll have to come later: