summaryrefslogtreecommitdiff
path: root/posts/2014-01-24-guile-cooperative-repl.md
diff options
context:
space:
mode:
Diffstat (limited to 'posts/2014-01-24-guile-cooperative-repl.md')
-rw-r--r--posts/2014-01-24-guile-cooperative-repl.md73
1 files changed, 73 insertions, 0 deletions
diff --git a/posts/2014-01-24-guile-cooperative-repl.md b/posts/2014-01-24-guile-cooperative-repl.md
new file mode 100644
index 0000000..50cf39e
--- /dev/null
+++ b/posts/2014-01-24-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.