Friday, October 19, 2012

libevent for Lisp: A Signal Example

At MindPool, there are several async I/O options we've been exploring for Lisp:
As luck would have it, I started with cl-event, and it was a fun little adventure (given the fact that it hasn't been maintained). I corresponded with the very nice original author a bit, asking if there were any updates in other locations, but sadly there weren't.

I was ready to dive in and get things current, when one last Google search turned up cl-async. This little bugger was hard to find, as at that point it had not been listed on CLiki. (But it is now :-)). Andrew Lyon has done a tremendous amount of work on cl-async, with a very complete set of bindings for libevent. This is just what I had been looking for, so I jumped in immediately.

As one might imagine from the topic of this post, there's a lot to be explored, uncovered, and developed further around async programming in Lisp. I'll start off slowly with a small example, and add more over the course of time.

I also hope to cover IOlib and SBCL's SERVE-EVENT in some future posts. Time will tell... For now, let's get started with cl-async in SBCL :-)

Dependencies
In a previous post, I discussed getting an environment set up with SBCL, I the rest of this post assumes that has been read and done :-)

Getting cl-async and Setting Up an SBCL Environment for Hacking
Now let's download cl-async and install the Libevent bindings :-)

$ git clone https://github.com/orthecreedence/cl-async.git
$ cd cl-async
$ tar cvzf libevent2.tgz ./libevent2
$ start-sbcl
* (require 'asdf-install)
("ASDF-INSTALL")
* (asdf-install:install "libevent2.tgz")
Install where?
1) System-wide install:
System in /usr/lib/sbcl/site-systems/
Files in /usr/lib/sbcl/site/
2) Personal installation:
System in /home/oubiwann/.sbcl/systems/
Files in /home/oubiwann/.sbcl/site/
--> 2
view raw 01-install.txt hosted with ❤ by GitHub

With the Lisp Libevent bindings installed, we're now ready to create a Lisp image to assist us when exploring cl-async. A Lisp image saves the current state of the REPL, with all the loaded libaries, etc., allowing for rapid start-ups and script executions. Just the thing, when you're iterating on something :-)

$ start-sbcl
* (ql:quickload "cl-async")
[snip: lots of output for downloading and installing dependencies]
* (sb-ext:save-lisp-and-die "cl-async.core")

Example: Adding a Signal Handler
Let's dive into some signal handling now! Here is some code I put together as part of an effort to beef up the examples in cl-async:

(ql:quickload "cl-async")
;; define a callback for our signal
(defun sigint-cb (signal-received)
(format t "You interrupted me!~%")
(format t "I got signal `~a`~%" signal-received)
(as:exit-event-loop))
;; set up a signal handler
(defun setup-handler ()
(as:signal-handler as:+sigint+ #'sigint-cb))
;; start the event loop
(as:start-event-loop #'setup-handler)

Note that the as: is a nickname for the package namespace cl-async:.

As one might expect, there is a function to start the event loop. However, what is a little different is that one doesn't initialize the event loop directly, but with a callback. As such, one cannot set up handlers, etc., except within the scope of this callback.

We've got the setup-handler function for that, which adds a callback for a SIGINT event. Let's try it out :-)

$ sbcl --core cl-async.core --script 03-handle-signal.lisp
To load "cl-async":
Load 1 ASDF system:
cl-async
; Loading "cl-async"
Once your script has finished loading the core, you should see output like the above, with no return to the shell prompt.

When we send a SIGINT with ^C, we can watch our callback get fired:

^CYou interrupted me!
I got signal `2`
view raw 05-output.txt hosted with ❤ by GitHub

Next up, we'll take a look at other types of handlers in cl-async.

1 comment:

Unknown said...

I'm loving these common lisp posts, please keep them coming! I've just started learning cl this year and things like thus are really helpful. Cheers