File: timeout-interrupt.lisp

package info (click to toggle)
bordeaux-threads 0.9.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 672 kB
  • sloc: lisp: 4,765; makefile: 2
file content (46 lines) | stat: -rw-r--r-- 2,208 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
;;;; -*- Mode: LISP; Syntax: ANSI-Common-lisp; Base: 10; Package: BORDEAUX-THREADS-2 -*-
;;;; The above modeline is required for Genera. Do not change.

(in-package :bordeaux-threads-2)

#-(or allegro clisp cmu genera sbcl)
(define-condition interrupt ()
  ((tag :initarg :tag :reader interrupt-tag)))

#-(or allegro clisp cmu genera sbcl)
(defmacro with-timeout ((timeout) &body body)
  "Execute `BODY' and signal a condition of type TIMEOUT if the execution of
BODY does not complete within `TIMEOUT' seconds. On implementations which do not
support WITH-TIMEOUT natively and don't support threads either it signals a
condition of type `NOT-IMPLEMENTED`."
  (declare (ignorable timeout body))
  #+thread-support
  (once-only (timeout)
    (with-gensyms (ok-tag interrupt-tag caller interrupt-thread c)
      `(let (,interrupt-thread)
         (unwind-protect-case ()
            (catch ',ok-tag
              (let* ((,interrupt-tag (gensym "INTERRUPT-TAG-"))
                     (,caller (current-thread)))
                (setf ,interrupt-thread
                       (make-thread
                        #'(lambda ()
                            (sleep ,timeout)
                            (interrupt-thread
                             ,caller
                             #'(lambda () (signal 'interrupt :tag ,interrupt-tag))))
                        :name (format nil "WITH-TIMEOUT thread serving: ~S."
                                      (thread-name ,caller))))
                (handler-bind
                    ((interrupt #'(lambda (,c)
                                    (when (eql ,interrupt-tag (interrupt-tag ,c))
                                      (error 'timeout :length ,timeout)))))
                  (throw ',ok-tag (progn ,@body)))))
           (:normal
            (when (and ,interrupt-thread (thread-alive-p ,interrupt-thread))
              ;; There's a potential race condition between THREAD-ALIVE-P
              ;; and DESTROY-THREAD but calling the latter when a thread already
              ;; terminated should not be a grave matter.
              (ignore-errors (destroy-thread ,interrupt-thread))))))))
  #-thread-support
  `(signal-not-implemented 'with-timeout))