Category Archives: Lisp

SBCL: with-timeout Is a Nice Undocumented Feature

SBCL has a nice undocumented feature, (sb-ext:with-timeout expires &body body). As far as I can tell, this is exactly analogous to the same feature in Bordeaux Threads, but that seems undocumented too.

It does not appear in the SBCL manual as of 1.3.14, but it does have a documentation string.

"Execute the body, asynchronously interrupting it and signalling a TIMEOUT
condition after at least EXPIRES seconds have passed.

Note that it is never safe to unwind from an asynchronous condition. Consider:

  (defun call-with-foo (function)
    (let (foo)
      (unwind-protect
         (progn
           (setf foo (get-foo))
           (funcall function foo))
       (when foo
         (release-foo foo)))))

If TIMEOUT occurs after GET-FOO has executed, but before the assignment, then
RELEASE-FOO will be missed. While individual sites like this can be made proof
against asynchronous unwinds, this doesn't solve the fundamental issue, as all
the frames potentially unwound through need to be proofed, which includes both
system and application code -- and in essence proofing everything will make
the system uninterruptible."

Here is a little demonstration on how to use it.

(handler-case
    (sb-ext:with-timeout 3
      (format t "Hello, world.~%")
      (sleep 5)
      (format t "Goodbye, world.~%"))
  (sb-ext:timeout (e)
    (format t "~a~%" e)))

This will print out

Hello, world.
Timeout occurred.

Enjoy.

SBCL: Testsuites Cannot Prevent All Possible Bugs

On OS X, SBCL as of 1.3.14 can’t sleep after fork. The following simple program exits with an error.

(require 'sb-posix)

(let ((pid (sb-posix:fork)))
  (if (= 0 pid)
      (progn
        (format t "Child: Sleeping for 10 seconds.~%")
        (sleep 10)
        (format t "Child: I woke up.~%"))
    (format t "Parent, exiting.~%")))

When the above script is run on OS X, it fails with the weird error we see below. Note that it works perfectly on Linux.

% sbcl --script sleep.cl
Parent, exiting.
Child: Sleeping for 10 seconds.
fatal error encountered in SBCL pid 16145:
(ipc/send) invalid destination port

The point of this, is that there is no reasonable way a testsuite will catch this kind of a bug. Testsuites, no matter how comprehensive, will never prevent bugs 100%. At most, they prevent the same bug from reappearing.

Hopefully the SBCL team will fix this bug soon.

Building SBCL on OS X Yosemite

A month or two ago, it did not work to build Steel Banks Common Lisp on OS X Yosemite. Or at least it never worked for me.

This has been fixed now, at least as of recent git checkout, and quite possibly SBCL 1.3.10.

If you get an error like the following,

ld: library not found for -lgcc_s.10.4

then you can try this (as far as I know) totally undocumented switch

SBCL_MACOSX_VERSION_MIN=10.10 sh make.sh

and the build will succeed.

Happy lisping with SBCL!

When SBCL Is Buggy, and CFFI Is Undocumented

There are at least two good ways to create C strings (or alien strings) in Lisp. The most often used is CFFI‘s foreign-string-alloc and the other is SBCL‘s make-alien-string.

The SBCL routine make-alien-string is documented to return both the alien pointer and the length of the string. However, it doesn’t.

Today, I reported this bug so by the time you read this the following may actually work; but as of SBCL 1.3.9 it doesn’t.

  (multiple-value-bind (buffer length)
      (make-alien-string "foo")
    (format t "buffer: ~a~%length: ~a~%" buffer length))

And this will print something like

  buffer: #<SB-ALIEN-INTERNALS:ALIEN-VALUE :SAP #X00400190
                                           :TYPE (* (SB-ALIEN:SIGNED 8))>
  length: NIL

On the other hand, the CFFI routine foreign-string-alloc is not documented (as of this writing) to return an extra length value, but actually does.

  (multiple-value-bind (buffer length)
      (cffi:foreign-string-alloc "foo")
    (format t "buffer: ~a~%length: ~a~%" buffer length))

Which will print something like

  buffer: #.(SB-SYS:INT-SAP #X00600050)
  length: 4

Note that the result is by default zero terminated, and hence the four bytes.

Hopefully the CFFI documentation will be updated just as quickly as SBCL is patched.

Have fun, and enjoy the Lisp world because it’s full of weird stuff.

PostgreSQL’s ~/.pgpass Parser in Common Lisp

Here is a quick and dirty PostgreSQL ~/.pgpass parser, in Common Lisp.

It depends on Split Sequence, conveniently loaded with QuickLisp, like so.

(ql:quickload :split-sequence :silent t)

And here is the actual code. It’s not wrapped in a function, and the other connection parameters are hard coded. This can use some refactoring; particularly with relevance to error handling.

(defvar pguser "user")
(defvar pgdb "database")
(defvar pghost "hostname")
(defvar pgport 5432)

(defvar pgpass
  (handler-case
      (with-open-file 
          (.pgpass "~/.pgpass"
		   :direction :input
		   :if-does-not-exist nil)
	(flet ((wildcard-string= (a b)
		 (or (string= a "*")
		     (string= a (princ-to-string b)))))

	  (loop for line = (read-line .pgpass nil)
	    while line
	    when (destructuring-bind (host port db user passwd)
		   (split-sequence:split-sequence #\: line)
		   (if (and (wildcard-string= host pghost)
			    (wildcard-string= port pgport)
			    (wildcard-string= db pgdb)
			    (wildcard-string= user pguser))
		       passwd))
	    return it)))
    (file-error (e)
		(format *error-output* "Unable to load password file; ~A~%"
			e)
		nil)))

(format t "Password: ~A~%" pgpass)

Licenses

Split-Sequence is part of CL-Utilities and Public Domain.

QuickLisp is licensed under an MIT-style license.

The example code above is © 2015 Johann ‘Myrkraverk’ Oskarsson and licensed under the Two-clause BSD license.