diff options
Diffstat (limited to 'posts/guile-cooperative-repl.md')
-rw-r--r-- | posts/guile-cooperative-repl.md | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/posts/guile-cooperative-repl.md b/posts/guile-cooperative-repl.md new file mode 100644 index 0000000..50cf39e --- /dev/null +++ b/posts/guile-cooperative-repl.md @@ -0,0 +1,73 @@ +title: A Cooperative REPL Server for Guile 2.0.10 +date: 2014-01-24 22:00 +tags: gnu, scheme, guile, wsu +summary: Guile 2.0.10 will include a new type of REPL server suited for single-threaded applications. +--- + +The next release of GNU Guile, 2.0.10, is to be released “real soon +now”. My contribution to this release is the new `(system repl +coop-server)` module. This module introduces a useful variant of the +REPL server that I’ve named the “cooperative” REPL server. It is +cooperative because it can be integrated with single-threaded programs +without the thread synchronization issues present in the ordinary REPL +server. + +Using delimited continuations and mvars (another new feature coming in +Guile 2.0.10), it was possible to create a REPL server whose clients +all ran in the context of a single thread. The cooperative server +puts the user in control of when it is safe to evaluate expressions +entered at the REPL prompt. + +Here’s an example of how to use it: + +```scheme +(use-modules (system repl coop-server)) + +(define server (spawn-coop-repl-server)) + +(while #t + (poll-coop-repl-server server) + (sleep 1)) +``` + +That’s all it takes. There are only 2 public procedures! +`spawn-coop-repl-server` creates a new cooperative REPL server +object and starts listening for clients. `poll-coop-repl-server` +applies pending operations for the server. + +Now that I’ve explained the API, onto the implementation details! +Although the REPLs run in the context of a single thread, there are +other threads needed to make it all work. To avoid thinking too much +about thread synchronization issues, I used the new `(ice-9 mvars)` +module to provide thread-safe objects for read/write operations. I +used delimited continuations in order to yield control back to the +user from inside the loop. + +When the server is spawned, a new thread is created to listen for +client connections. When a client connects, a request to create a new +REPL is put into the server’s "evaluation" mvar, the storage place for +pending server operations. The job of `poll-coop-repl-server` is to +attempt to take from the evaluation mvar and execute the operation +stored within, if any. In this case, an operation called `new-repl` +is hanging out along with the client socket. The contents of the mvar +are removed, and a new REPL is started in the main thread. + +This new REPL is run within a "prompt", Guile’s implementation of +delimited continuations. The prompt allows the REPL to be paused +temporarily while waiting for user input. Just before the prompt is +aborted, a thunk containing the logic to read user input is stored +within the client’s "read" mvar for yet another thread to use. +Without this additional thread and the use of a prompt, the main +thread would block while waiting for input, defeating the purpose of +the cooperative REPL. The reader thread waits for this thunk to +appear in the read mvar. When it appears, the thunk is applied and +the resulting expression is stored as an `eval` operation within the +evaluation mvar. When `poll-coop-repl-server` is called, the REPL +prompt is resumed. The input expression is evaluated in the context +of the main thread, the result is printed, and the process repeats +itself. + +That’s all, folks. Thanks for following along. I’m very excited to +use the cooperative REPL server in guile-2d and I hope that others +find it useful as well. Many thanks to Mark Weaver for helping me out +when I got stuck and for all of the helpful code review. |