bordeaux-threads.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) README
 (DIR) LICENSE
       ---
       bordeaux-threads.lisp (6348B)
       ---
            1 ;;;; -*- indent-tabs-mode: nil -*-
            2 
            3 #|
            4 Copyright 2006, 2007 Greg Pfeil
            5 
            6 Distributed under the MIT license (see LICENSE file)
            7 |#
            8 
            9 (in-package #:bordeaux-threads)
           10 
           11 (defvar *supports-threads-p* nil
           12   "This should be set to T if the running instance has thread support.")
           13 
           14 (defun mark-supported ()
           15   (setf *supports-threads-p* t)
           16   (pushnew :bordeaux-threads *features*))
           17 
           18 (define-condition bordeaux-mp-condition (error)
           19   ((message :initarg :message :reader message))
           20   (:report (lambda (condition stream)
           21              (format stream (message condition)))))
           22 
           23 (defgeneric make-threading-support-error ()
           24   (:documentation "Creates a BORDEAUX-THREADS condition which specifies
           25   whether there is no BORDEAUX-THREADS support for the implementation, no
           26   threads enabled for the system, or no support for a particular
           27   function.")
           28   (:method ()
           29     (make-condition
           30      'bordeaux-mp-condition
           31      :message (if *supports-threads-p*
           32                   "There is no support for this method on this implementation."
           33                   "There is no thread support in this instance."))))
           34 
           35 ;;; Timeouts
           36 
           37 #-sbcl
           38 (define-condition timeout (serious-condition)
           39   ((length :initform nil
           40              :initarg :length
           41              :reader timeout-length))
           42   (:report (lambda (c s)
           43              (if (timeout-length c)
           44                  (format s "A timeout set to ~A seconds occurred."
           45                          (timeout-length c))
           46                  (format s "A timeout occurred.")))))
           47 
           48 #-sbcl
           49 (defmacro with-timeout ((timeout) &body body)
           50   "Execute `BODY' and signal a condition of type TIMEOUT if the execution of
           51 BODY does not complete within `TIMEOUT' seconds. On implementations which do not
           52 support WITH-TIMEOUT natively and don't support threads either it has no effect."
           53   (declare (ignorable timeout body))
           54   #+thread-support
           55   (let ((ok-tag (gensym "OK"))
           56         (timeout-tag (gensym "TIMEOUT"))
           57         (caller (gensym "CALLER")))
           58     (once-only (timeout)
           59       `(multiple-value-prog1
           60            (catch ',ok-tag
           61              (catch ',timeout-tag
           62                (let ((,caller (current-thread)))
           63                  (make-thread #'(lambda ()
           64                                   (sleep ,timeout)
           65                                   (interrupt-thread ,caller
           66                                                     #'(lambda ()
           67                                                         (ignore-errors
           68                                                          (throw ',timeout-tag nil)))))
           69                               :name (format nil "WITH-TIMEOUT thread serving: ~S."
           70                                             (thread-name ,caller)))
           71                  (throw ',ok-tag (progn ,@body))))
           72              (error 'timeout :length ,timeout)))))
           73   #-thread-support
           74   `(error (make-threading-support-error)))
           75 
           76 ;;; Semaphores
           77 
           78 ;;; We provide this structure definition unconditionally regardless of the fact
           79 ;;; it may not be used not to prevent warnings from compiling default functions
           80 ;;; for semaphore in default-implementations.lisp.
           81 (defstruct %semaphore
           82   lock
           83   condition-variable
           84   counter)
           85 
           86 #-(or ccl sbcl)
           87 (deftype semaphore ()
           88   '%semaphore)
           89 
           90 ;;; Thread Creation
           91 
           92 ;;; See default-implementations.lisp for MAKE-THREAD.
           93 
           94 ;; Forms are evaluated in the new thread or in the calling thread?
           95 (defvar *default-special-bindings* nil
           96   "This variable holds an alist associating special variable symbols
           97   to forms to evaluate. Special variables named in this list will
           98   be locally bound in the new thread before it begins executing user code.
           99 
          100   This variable may be rebound around calls to MAKE-THREAD to
          101   add/alter default bindings. The effect of mutating this list is
          102   undefined, but earlier forms take precedence over later forms for
          103   the same symbol, so defaults may be overridden by consing to the
          104   head of the list.")
          105 
          106 (defmacro defbindings (name docstring &body initforms)
          107   (check-type docstring string)
          108   `(defparameter ,name
          109      (list
          110       ,@(loop for (special form) in initforms
          111               collect `(cons ',special ',form)))
          112      ,docstring))
          113 
          114 ;; Forms are evaluated in the new thread or in the calling thread?
          115 (defbindings *standard-io-bindings*
          116   "Standard bindings of printer/reader control variables as per CL:WITH-STANDARD-IO-SYNTAX."
          117   (*package*                   (find-package :common-lisp-user))
          118   (*print-array*               t)
          119   (*print-base*                10)
          120   (*print-case*                :upcase)
          121   (*print-circle*              nil)
          122   (*print-escape*              t)
          123   (*print-gensym*              t)
          124   (*print-length*              nil)
          125   (*print-level*               nil)
          126   (*print-lines*               nil)
          127   (*print-miser-width*         nil)
          128   (*print-pprint-dispatch*     (copy-pprint-dispatch nil))
          129   (*print-pretty*              nil)
          130   (*print-radix*               nil)
          131   (*print-readably*            t)
          132   (*print-right-margin*        nil)
          133   (*random-state*              (make-random-state t))
          134   (*read-base*                 10)
          135   (*read-default-float-format* 'single-float)
          136   (*read-eval*                 t)
          137   (*read-suppress*             nil)
          138   (*readtable*                 (copy-readtable nil)))
          139 
          140 (defun binding-default-specials (function special-bindings)
          141   "Return a closure that binds the symbols in SPECIAL-BINDINGS and calls
          142 FUNCTION."
          143   (let ((specials (remove-duplicates special-bindings :from-end t :key #'car)))
          144     (lambda ()
          145       (progv (mapcar #'car specials)
          146           (loop for (nil . form) in specials collect (eval form))
          147         (funcall function)))))
          148 
          149 ;;; FIXME: This test won't work if CURRENT-THREAD
          150 ;;;        conses a new object each time
          151 (defun signal-error-if-current-thread (thread)
          152   (when (eq thread (current-thread))
          153     (error 'bordeaux-mp-condition
          154            :message "Cannot destroy the current thread")))
          155 
          156 (defparameter *no-condition-wait-timeout-message*
          157   "CONDITION-WAIT with :TIMEOUT is not available for this Lisp implementation.")
          158 
          159 (defun signal-error-if-condition-wait-timeout (timeout)
          160   (when timeout
          161     (error 'bordeaux-mp-condition
          162            :message *no-condition-wait-timeout-message*)))
          163 
          164 (defmacro define-condition-wait-compiler-macro ()
          165   `(define-compiler-macro condition-wait
          166        (&whole whole condition-variable lock &key timeout)
          167     (declare (ignore condition-variable lock))
          168     (when timeout
          169       (simple-style-warning *no-condition-wait-timeout-message*))
          170     whole))