Coverage report: /home/ellis/.stash/quicklisp/dists/ultralisp/software/sionescu-bordeaux-threads-20250412101706/apiv1/bordeaux-threads.lisp
Kind | Covered | All | % |
expression | 3 | 56 | 5.4 |
branch | 0 | 2 | 0.0 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;;; -*- Mode: LISP; Syntax: ANSI-Common-lisp; Base: 10; Package: BORDEAUX-THREADS -*-
2
;;;; The above modeline is required for Genera. Do not change.
5
Copyright 2006, 2007 Greg Pfeil
7
Distributed under the MIT license (see LICENSE file)
10
(in-package #:bordeaux-threads)
12
(defvar *supports-threads-p* nil
13
"This should be set to T if the running instance has thread support.")
15
(defun mark-supported ()
16
(setf *supports-threads-p* t)
17
(pushnew :bordeaux-threads *features*))
19
(define-condition bordeaux-mp-condition (error)
20
((message :initarg :message :reader message))
21
(:report (lambda (condition stream)
22
(format stream (message condition)))))
24
(defgeneric make-threading-support-error ()
25
(:documentation "Creates a BORDEAUX-THREADS condition which specifies
26
whether there is no BORDEAUX-THREADS support for the implementation, no
27
threads enabled for the system, or no support for a particular
31
'bordeaux-mp-condition
32
:message (if *supports-threads-p*
33
"There is no support for this method on this implementation."
34
"There is no thread support in this instance."))))
39
(define-condition timeout (serious-condition)
40
((length :initform nil
42
:reader timeout-length))
43
(:report (lambda (c s)
44
(if (timeout-length c)
45
(format s "A timeout set to ~A seconds occurred."
47
(format s "A timeout occurred.")))))
50
(define-condition interrupt ()
51
((tag :initarg :tag :reader interrupt-tag)))
54
(defmacro with-timeout ((timeout) &body body)
55
"Execute `BODY' and signal a condition of type TIMEOUT if the execution of
56
BODY does not complete within `TIMEOUT' seconds. On implementations which do not
57
support WITH-TIMEOUT natively and don't support threads either it has no effect."
58
(declare (ignorable timeout body))
61
(with-gensyms (ok-tag interrupt-tag caller interrupt-thread c)
62
`(let (,interrupt-thread)
63
(unwind-protect-case ()
65
(let* ((,interrupt-tag (gensym "INTERRUPT-TAG-"))
66
(,caller (current-thread)))
67
(setf ,interrupt-thread
73
#'(lambda () (signal 'interrupt :tag ,interrupt-tag))))
74
:name (format nil "WITH-TIMEOUT thread serving: ~S."
75
(thread-name ,caller))))
77
((interrupt #'(lambda (,c)
78
(when (eql ,interrupt-tag (interrupt-tag ,c))
79
(error 'timeout :length ,timeout)))))
80
(throw ',ok-tag (progn ,@body)))))
82
(when (and ,interrupt-thread (thread-alive-p ,interrupt-thread))
83
;; There's a potential race condition between THREAD-ALIVE-P
84
;; and DESTROY-THREAD but calling the latter when a thread already
85
;; terminated should not be a grave matter.
86
(ignore-errors (destroy-thread ,interrupt-thread))))))))
88
`(error (make-threading-support-error)))
92
;;; We provide this structure definition unconditionally regardless of the fact
93
;;; it may not be used not to prevent warnings from compiling default functions
94
;;; for semaphore in default-implementations.lisp.
101
(deftype semaphore ()
106
;;; See default-implementations.lisp for MAKE-THREAD.
108
;; Forms are evaluated in the new thread or in the calling thread?
109
(defvar *default-special-bindings* nil
110
"This variable holds an alist associating special variable symbols
111
to forms to evaluate. Special variables named in this list will
112
be locally bound in the new thread before it begins executing user code.
114
This variable may be rebound around calls to MAKE-THREAD to
115
add/alter default bindings. The effect of mutating this list is
116
undefined, but earlier forms take precedence over later forms for
117
the same symbol, so defaults may be overridden by consing to the
120
(defmacro defbindings (name docstring &body initforms)
121
(check-type docstring string)
124
,@(loop for (special form) in initforms
125
collect `(cons ',special ',form)))
128
;; Forms are evaluated in the new thread or in the calling thread?
129
(defbindings *standard-io-bindings*
130
"Standard bindings of printer/reader control variables as per CL:WITH-STANDARD-IO-SYNTAX."
131
(*package* (find-package :common-lisp-user))
134
(*print-case* :upcase)
141
(*print-miser-width* nil)
142
(*print-pprint-dispatch* (copy-pprint-dispatch nil))
146
(*print-right-margin* nil)
147
(*random-state* (make-random-state t))
149
(*read-default-float-format* 'single-float)
151
(*read-suppress* nil)
152
(*readtable* (copy-readtable nil)))
154
(defun binding-default-specials (function special-bindings)
155
"Return a closure that binds the symbols in SPECIAL-BINDINGS and calls
157
(let ((specials (remove-duplicates special-bindings :from-end t :key #'car)))
158
(named-lambda %binding-default-specials-wrapper ()
159
(progv (mapcar #'car specials)
160
(loop for (nil . form) in specials collect (eval form))
161
(funcall function)))))
163
;;; FIXME: This test won't work if CURRENT-THREAD
164
;;; conses a new object each time
165
(defun signal-error-if-current-thread (thread)
166
(when (eq thread (current-thread))
167
(error 'bordeaux-mp-condition
168
:message "Cannot destroy the current thread")))
170
(defparameter *no-condition-wait-timeout-message*
171
"CONDITION-WAIT with :TIMEOUT is not available for this Lisp implementation.")
173
(defun signal-error-if-condition-wait-timeout (timeout)
175
(error 'bordeaux-mp-condition
176
:message *no-condition-wait-timeout-message*)))
178
(defmacro define-condition-wait-compiler-macro ()
179
`(define-compiler-macro condition-wait
180
(&whole whole condition-variable lock &key timeout)
181
(declare (ignore condition-variable lock))
183
(simple-style-warning *no-condition-wait-timeout-message*))