`
#### See also:
[**abnormal-exit-condition**](../abnormal-exit-condition),
[**join-thread**](../join-thread)
bordeaux-threads-0.9.4/docs/content/threads/abnormal-exit.md000066400000000000000000000006471463556250700241310ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Condition type ABNORMAL-EXIT'
weight: 16
---
#### Class Precedence List:
abnormal-exit, bordeaux-threads-error, error, serious-condition,
condition, t
#### Description:
The error **abnormal-exit** represents the condition of a thread not
having terminated successfully.
#### See also:
[**abnormal-exit-condition**](../abnormal-exit-condition),
[**join-thread**](../join-thread)
bordeaux-threads-0.9.4/docs/content/threads/bordeaux-thread-error.md000066400000000000000000000005671463556250700255750ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Condition type BORDEAUX-THREADS-ERROR'
weight: 15
---
#### Class Precedence List:
bordeaux-threads-error, error, serious-condition, condition, t
#### Description:
The type **bordeaux-threads-error** consists of error conditions that
are related to thread operations.
#### See also:
[**abnormal-exit**](../abnormal-exit-condition)
bordeaux-threads-0.9.4/docs/content/threads/class-thread.md000066400000000000000000000003521463556250700237320ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: Class THREAD
weight: 1
---
#### Class precedence list:
[thread](.), [t](http://www.lispworks.com/documentation/HyperSpec/Body/t_t.htm#t)
#### Description:
A wrapper for host thread instances.
bordeaux-threads-0.9.4/docs/content/threads/current-all-threads.md000066400000000000000000000012031463556250700252340ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function CURRENT-THREAD, ALL-THREADS'
weight: 6
---
#### Syntax:
**current-thread** => thread\
**all-threads** => threads
#### Arguments and values:
*thread* -> a [thread](../class-thread) object.\
*threads* -> a list of [thread](../class-thread) objects.
#### Description:
**current-thread** returns the thread object representing the calling
thread.
**all-threads** returns a [fresh
list](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#fresh)
of all running threads.
#### Exceptional situations:
None.
#### See also:
[**make-thread**](../make-thread)
#### Notes:
None.
bordeaux-threads-0.9.4/docs/content/threads/default-special-bindings.md000066400000000000000000000047431463556250700262250ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Variable *DEFAULT-SPECIAL-BINDINGS*'
weight: 5
---
#### Value type:
an
[alist](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_a.htm#alist)
mapping symbol names to forms to evaluate.
#### Initial value:
[nil](http://www.lispworks.com/documentation/HyperSpec/Body/a_nil.htm#nil).
#### Description:
Variables named in this list are locally bound in the new thread,
before it begins executing user code, by calling
[eval](http://www.lispworks.com/documentation/HyperSpec/Body/f_eval.htm#eval)
on its associated form.
This variable may be rebound around calls to [make-thread](../make-thread)
to add/alter default bindings. The effect of mutating this list is
undefined, but earlier forms take precedence over later forms for the
same symbol, so defaults may be overridden by consing to the head of the
list.
The bindings in `*default-special-bindings*` are used to determine the
initial bindings of a new thread, and take precedence over a default
list of I/O bindings. The list of initial I/O bindings is not
modifiable by the user and it was chosen to avoid potential
implementation-defined differences in
[with-standard-io-syntax](http://www.lispworks.com/documentation/HyperSpec/Body/m_w_std_.htm#with-standard-io-syntax).
```
*package* (find-package :common-lisp-user)
*print-array* t
*print-base* 10
*print-case* :upcase
*print-circle* nil
*print-escape* t
*print-gensym* t
*print-length* nil
*print-level* nil
*print-lines* nil
*print-miser-width* nil
*print-pprint-dispatch* (copy-pprint-dispatch nil)
*print-pretty* nil
*print-radix* nil
*print-readably* t
*print-right-margin* nil
*random-state* (make-random-state t)
*read-base* 10
*read-default-float-format* 'double-float
*read-eval* nil
*read-suppress* nil
*readtable* (copy-readtable nil)
```
#### Examples:
```
;;; Make a thread read integers in base 7.
(let* ((bt2:*default-special-bindings*
(acons '*read-base* 7
bt2:*default-special-bindings*))
(thread (bt2:make-thread (lambda () (read-from-string "10")))))
(bt2:join-thread thread))
```
=> 7, 2
#### See also:
[**make-thread**](../make-thread)
#### Notes:
The binding code does not check whether a symbol is indeed declared
special or not.
bordeaux-threads-0.9.4/docs/content/threads/destroy-thread.md000066400000000000000000000011631463556250700243170ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function DESTROY-THREAD'
weight: 13
---
#### Syntax:
**destroy-thread** thread => thread
#### Arguments and values:
*thread* -> a [thread](../class-thread) object.
#### Description:
Terminates the thread `thread`.
#### Exceptional situations:
Signals [bordeaux-threads-error](../bordeaux-threads-error) if
attempting to destroy the calling thread, or a thread that already
terminated.
#### See also:
[**join-thread**](../join-thread)
#### Notes:
This should be used with caution: it is implementation-defined whether
the thread runs cleanup forms or releases its locks first.
bordeaux-threads-0.9.4/docs/content/threads/interrupt-thread.md000066400000000000000000000024421463556250700246630ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function INTERRUPT-THREAD'
weight: 11
---
#### Syntax:
**interrupt-thread** thread function *&rest* arguments => thread
#### Arguments and values:
*thread* -> a [thread](../class-thread) object.\
*function* -> a function object.\
*arguments* -> values.
#### Description:
Interrupt `thread` and apply `function` to `arguments` within its
dynamic context, then continue with the interrupted path of execution.
Returns the thread object it acted on.
#### Exceptional situations:
An error of
[type](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_t.htm#type)
[**type-error**](http://www.lispworks.com/documentation/HyperSpec/Body/e_tp_err.htm#type-error)
will be signaled if `thread` is not a [**thread**](../class-thread) object.\
An error of
[type](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_t.htm#type)
[**type-error**](http://www.lispworks.com/documentation/HyperSpec/Body/e_tp_err.htm#type-error)
will be signaled if `function` is not a [function
designator](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#function_designator).
#### See also:
[**make-thread**](../make-thread), [**join-thread**](../join-thread)
#### Notes:
This may not be a good idea if `thread` is holding locks or doing
anything important.
bordeaux-threads-0.9.4/docs/content/threads/join-thread.md000066400000000000000000000027241463556250700235710ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function JOIN-THREAD'
weight: 7
---
#### Syntax:
**join-thread** thread => multiple values
#### Arguments and values:
*thread* -> a [thread](../class-thread) object.
#### Description
Wait until `thread` terminates, or if it has already terminated,
return immediately.
The return values of the thread function are returned.
#### Examples
```
(let ((thread (bt2:make-thread
(lambda () (values 1 2 3)))))
(bt2:join-thread thread))
```
=> 1, 2, 3
#### Exceptional situations:
If a thread is terminated by an unhandled condition, or by
[**destroy-thread**](../destroy-thread), then the condition
[**abnormal-exit**](../abnormal-exit) is signaled.
#### See also:
[**make-thread**](./make-thread),
[**abnormal-exit**](../abnormal-exit)
#### Notes:
Due to how **join-thread** interacts with the dynamic environment
established by **make-thread**, it is not safe to join with a thread
that was created outside Bordeaux-Threads. For example, the following
code has undefined behaviour and might very well corrupt the image:
```
(mapcar #'bt2:join-thread (bt2:all-threads))
```
Bordeaux-Threads can only record instances of thread termination due
to unhandled conditions or the use of
[**destroy-thread**](../destroy-thread). In case of other ways to
terminate a thread, such as throwing to an implementation-specific tag
defined in the dynamic environment of the thread function, the
behaviour of **join-thread** is undefined.
bordeaux-threads-0.9.4/docs/content/threads/make-thread.md000066400000000000000000000063141463556250700235460ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function: MAKE-THREAD'
weight: 4
---
#### Syntax:
**make-thread** function *&key* name initial-bindings trap-conditions => thread
#### Arguments and values:
*function* -> a [function
designator](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#function_designator).\
*name* -> a
[string](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#string)
or
[nil](http://www.lispworks.com/documentation/HyperSpec/Body/a_nil.htm#nil).\
*initial-bindings* -> an alist mapping special variable names to
values. Defaults to [\*default-special-bindings\*](default-special-bindings).\
*trap-conditions* -> if
[true](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_t.htm#true),
wrap the thread function in a handler-case.
#### Description:
Creates and returns a thread named `name`, which will call the
function `function` with no arguments: when `function` returns, the
thread terminates.
The interaction between threads and dynamic variables is in some cases
complex, and depends on whether the variable has only a global binding
(as established by
e.g. [defvar](http://www.lispworks.com/documentation/HyperSpec/Body/m_defpar.htm)/[defparameter](http://www.lispworks.com/documentation/HyperSpec/Body/m_defpar.htm)/top-level
[setq](http://www.lispworks.com/documentation/HyperSpec/Body/s_setq.htm))
or has been bound locally (e.g. with
[let](http://www.lispworks.com/documentation/HyperSpec/Body/s_let_l.htm)
or
[let*](http://www.lispworks.com/documentation/HyperSpec/Body/s_let_l.htm))
in the calling thread.
- Global bindings are shared between threads: the initial value of a
global variable in the new thread will be the same as in the
parent, and an assignment to such a variable in any thread will be
visible to all threads in which the global binding is visible.
- Local bindings, such as the ones introduced by `initial-bindings`,
are local to the thread they are introduced in, except that
- Local bindings in the the caller of [make-thread](.) may or may not
be shared with the new thread that it creates: this is
implementation-defined. Portable code should not depend on
particular behaviour in this case, nor should it assign to such
variables without first rebinding them in the new thread.
#### Exceptional situations:
An error of
[type](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_t.htm#type)
[**type-error**](http://www.lispworks.com/documentation/HyperSpec/Body/e_tp_err.htm#type-error)
will be signaled if `function` is not a [function
designator](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#function_designator).\
An error of
[type](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_t.htm#type)
[**type-error**](http://www.lispworks.com/documentation/HyperSpec/Body/e_tp_err.htm#type-error)
will be signaled if `name` is anything other than
[nil](http://www.lispworks.com/documentation/HyperSpec/Body/a_nil.htm#nil)
or a [string](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#string).
#### Affected by:
[**\*default-special-bindings\***](../default-special-bindings).
#### See also:
[**join-thread**](../join-thread)
#### Notes:
The threading model is implementation-dependent.
bordeaux-threads-0.9.4/docs/content/threads/signal-in-thread.md000066400000000000000000000017431463556250700245130ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function SIGNAL-IN-THREAD, WARN-IN-THREAD, ERROR-IN-THREAD'
weight: 12
---
#### Syntax:
**signal-in-thread** thread datum *&rest* arguments => thread\
**warn-in-thread** thread datum *&rest* arguments => thread\
**error-in-thread** thread datum *&rest* arguments => thread
#### Arguments and values:
*thread* -> a [thread](../class-thread) object.\
*datum, arguments* -> designators for a condition.
#### Description:
Interrupt `thread` and apply `signal/warn/error` passing `datum` and
`arguments`.
#### Exceptional situations:
None.
#### See also:
[**interrupt-thread**](../interrupt-thread),
[**error**](http://www.lispworks.com/documentation/HyperSpec/Body/f_error.htm),
[**signal**](http://www.lispworks.com/documentation/HyperSpec/Body/f_signal.htm),
[**warn**](http://www.lispworks.com/documentation/HyperSpec/Body/f_warn.htm)
#### Notes:
These functions are currently implemented on top of
[**interrupt-thread**](../interrupt-thread).
bordeaux-threads-0.9.4/docs/content/threads/start-multiprocessing.md000066400000000000000000000007121463556250700257420ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function START-MULTIPROCESSING'
weight: 10
---
#### Syntax:
**start-multiprocessing** => No values.
#### Arguments and values:
Returns no values.
#### Description:
If the host implementation uses user-level threads, start the
scheduler and multiprocessing, otherwise do nothing. It is safe to
call repeatedly.
#### Exceptional situations:
None.
#### Notes:
Only has an effect on Allegro, CMUCL and Lispworks.
bordeaux-threads-0.9.4/docs/content/threads/thread-alive-p.md000066400000000000000000000012011463556250700241540ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function THREAD-ALIVE-P'
weight: 14
---
#### Syntax:
**thread-alive-p** thread => generalized-boolean
#### Arguments and values:
*thread* -> a [thread](../class-thread) object.\
*generalized-boolean* -> a [generalized
boolean](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_g.htm#generalized_boolean).
#### Description:
Returns true if `thread` has not finished or
[**destroy-thread**](../destroy-thread) has not been called on it.
#### Exceptional situations:
Signals a type error if `thread` is not a [thread](../class-thread)
object.
#### See also:
None.
#### Notes:
None.
bordeaux-threads-0.9.4/docs/content/threads/thread-readers.md000066400000000000000000000012021463556250700242450ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function THREAD-NAME, THREAD-NATIVE-THREAD'
weight: 2
---
#### Syntax:
**thread-name** thread => name\
**thread-native-thread** thread => native-thread
#### Arguments and values:
*thread* -> an instance of class [**thread**](../class-thread).\
*name* -> a
[string](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_s.htm#string)
or
[nil](http://www.lispworks.com/documentation/HyperSpec/Body/a_nil.htm#nil)\
*native-thread* -> a host thread instance.
#### Description:
These accessors return the public slots of class [**thread**](../class-thread).
#### Exceptional situations:
None.
bordeaux-threads-0.9.4/docs/content/threads/thread-yield.md000066400000000000000000000010401463556250700237260ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function THREAD-YIELD'
weight: 9
---
#### Syntax:
**thread-yield** => No values.
#### Arguments and values:
Returns no values.
#### Description
Causes the calling thread to relinquish the CPU to allow other threads
to run.
#### Exceptional situations:
None.
#### Notes:
On modern implementations that use native OS (SMP) threads, this
function is of little use. On some older implementations where threads
are scheduled in user space, it may be necessary or desirable to call
this periodically.
bordeaux-threads-0.9.4/docs/content/threads/threadp.md000066400000000000000000000014671463556250700230170ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: 'Function: THREADP'
weight: 3
---
#### Syntax:
**threadp** object => generalized-boolean
#### Arguments and values:
*object* -> an
[object](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_o.htm#object).\
*generalized-boolean* -> a [generalized
boolean](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_g.htm#generalized_boolean).
#### Description:
Returns
[true](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_t.htm#true)
if `object` is of
[type](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_t.htm#type)
[**thread**](../class-thread), otherwise
[false](http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_f.htm#false).
#### Exceptional situations:
None.
#### Notes:
`(threadp object) == (typep object 'thread)`
bordeaux-threads-0.9.4/docs/content/timeouts/000077500000000000000000000000001463556250700212555ustar00rootroot00000000000000bordeaux-threads-0.9.4/docs/content/timeouts/_index.md000066400000000000000000000002221463556250700230410ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: Timeouts dictionary
weight: 7
---
##### [Class TIMEOUT](timeout)
##### [Macro WITH-TIMEOUT](with-timeout)
bordeaux-threads-0.9.4/docs/content/timeouts/timeout.md000066400000000000000000000004711463556250700232670ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: Class TIMEOUT
weight: 1
---
#### Class precedence list:
timeout, t.
#### Description:
This class represents the condition of a body of code not completing
execution within a certain amount of time.
#### See also:
[**with-timeout**](../with-timeout)
#### Notes:
None.
bordeaux-threads-0.9.4/docs/content/timeouts/with-timeout.md000066400000000000000000000014411463556250700242360ustar00rootroot00000000000000---
date: 2022-01-07T08:00:00Z
title: Macro WITH-TIMEOUT
weight: 2
---
#### Syntax:
**with-timeout** (timeout) declaration\* forms\* => results
#### Arguments and values:
*timeout* -> a non-negative real number.\
*declaration* -> a declare expression; not evaluated.\
*forms* -> an implicit progn.\
*results* -> the values returned by the forms.
#### Description:
Execute `forms` and signal a condition of type
[**timeout**](../timeout) if the execution of `forms` does not
complete within `timeout` seconds.
#### Exceptional situations:
[**timeout**](../timeout), **not-implemented**
#### See also:
[**timeout**](../timeout)
#### Notes:
On implementations which do not support **with-timeout** natively and
don't support threads either it signals a condition of type
**not-implemented**.
bordeaux-threads-0.9.4/docs/themes/000077500000000000000000000000001463556250700172175ustar00rootroot00000000000000bordeaux-threads-0.9.4/docs/themes/techdoc/000077500000000000000000000000001463556250700206305ustar00rootroot00000000000000bordeaux-threads-0.9.4/site/000077500000000000000000000000001463556250700157465ustar00rootroot00000000000000bordeaux-threads-0.9.4/site/index.html000066400000000000000000000037121463556250700177460ustar00rootroot00000000000000
Bordeaux Threads project
Based on an original proposal by Dan Barlow (Bordeaux-MP) this
library is meant to make writing portable multi-threaded apps
simple.
Read the current API documentation.
Supports all major Common Lisp implementations: SBCL, CCL,
Lispworks, Allegro, ABCL, ECL, Clisp.
The MKCL, Corman,
MCL and Scieneer backends are not tested frequently(if ever) and
might not work.
For discussion, use the mailing
list bordeaux-threads-devel
or the #lisp IRC channel on Freenode.
Source repository
Bordeaux-threads is developed
at Github. The
repository is also mirrored
to Gitlab
and Bitbucket.
bordeaux-threads-0.9.4/site/style.css000066400000000000000000000026631463556250700176270ustar00rootroot00000000000000/*
Copyright 2006,2007 Greg Pfeil
Distributed under the MIT license (see LICENSE file)
*/
tbody {
border-top: thin dotted black;
}
.failure {
background-color: #ff0;
}
.nonexistant {
background-color: #ccc;
}
.perfect {
background-color: #0f0;
}
.error {
background-color: #f00;
}
.header {
font-size: medium;
background-color:#336699;
color:#ffffff;
border-style:solid;
border-width: 5px;
border-color:#002244;
padding: 1mm 1mm 1mm 5mm;
}
.footer {
font-size: small;
font-style: italic;
text-align: right;
background-color:#336699;
color:#ffffff;
border-style:solid;
border-width: 2px;
border-color:#002244;
padding: 1mm 1mm 1mm 1mm;
}
.footer a:link {
font-weight:bold;
color:#ffffff;
background-color: #336699;
text-decoration:underline;
}
.footer a:visited {
font-weight:bold;
color:#ffffff;
background-color: #336699;
text-decoration:underline;
}
.footer a:hover {
font-weight:bold;
color:#002244;
background-color: #336699;
text-decoration:underline; }
.check {font-size: x-small;
text-align:right;}
.check a:link { font-weight:bold;
color:#a0a0ff;
background-color: #FFFFFF;
text-decoration:underline; }
.check a:visited { font-weight:bold;
color:#a0a0ff;
background-color: #FFFFFF;
text-decoration:underline; }
.check a:hover { font-weight:bold;
color:#000000;
background-color: #FFFFFF;
text-decoration:underline; }
bordeaux-threads-0.9.4/test/000077500000000000000000000000001463556250700157615ustar00rootroot00000000000000bordeaux-threads-0.9.4/test/not-implemented.lisp000066400000000000000000000074671463556250700217710ustar00rootroot00000000000000;;;; -*- Mode: LISP; Syntax: ANSI-Common-lisp; Base: 10; Package: BORDEAUX-THREADS-2/TEST -*-
;;;; The above modeline is required for Genera. Do not change.
(in-package :bordeaux-threads-2/test)
(in-suite :bordeaux-threads-2)
(test not-implemented.whole-function
(let ((*not-implemented* (make-hash-table :test #'equal))
(op 'acquire-lock)
(feature :some-feature))
(is-true (implemented-p op))
(is-true (implemented-p op feature))
(mark-not-implemented op)
(is-false (implemented-p op))
(is-false (implemented-p op feature))))
(test not-implemented.one-feature
(let ((*not-implemented* (make-hash-table :test #'equal))
(op 'acquire-lock)
(feature :timeout))
(is-true (implemented-p op))
(is-true (implemented-p op feature))
(mark-not-implemented op feature)
(is-true (implemented-p op))
(is-false (implemented-p op :feature))))
;;;
;;; Threads
;;;
(test make-thread.not-implemented
(if (implemented-p 'bt2:make-thread)
(pass)
(signals not-implemented (make-thread (lambda ())))))
(test join-thread.not-implemented
(if (implemented-p 'bt2:join-thread)
(pass)
(signals not-implemented (join-thread (make-thread (lambda ()))))))
(test current-thread.not-implemented
(if (implemented-p 'bt2:current-thread)
(pass)
(signals not-implemented (current-thread))))
(test thread-yield.not-implemented
(if (implemented-p 'bt2:thread-yield)
(pass)
(signals not-implemented (thread-yield))))
(test all-threads.not-implemented
(if (implemented-p 'bt2:all-threads)
(pass)
(signals not-implemented (all-threads))))
(test interrupt-thread.not-implemented
(if (implemented-p 'bt2:interrupt-thread)
(pass)
(signals not-implemented
(let ((thread (make-thread (lambda () (sleep 5)))))
(interrupt-thread thread (lambda ()))))))
(test destroy-thread.not-implemented
(if (implemented-p 'bt2:destroy-thread)
(pass)
(signals not-implemented
(destroy-thread (make-thread (lambda ()))))))
(test thread-alive-p.not-implemented
(if (implemented-p 'bt2:thread-alive-p)
(pass)
(signals not-implemented
(thread-alive-p (make-thread (lambda ()))))))
;;;
;;; Locks
;;;
(test make-lock.not-implemented
(if (implemented-p 'bt2:make-lock)
(pass)
(signals not-implemented (make-lock))))
(test acquire-lock.not-implemented
(if (implemented-p 'bt2:acquire-lock)
(pass)
(signals not-implemented
(acquire-lock (make-lock)))))
(test release-lock.not-implemented
(if (implemented-p 'bt2:release-lock)
(pass)
(signals not-implemented
(let ((lock (make-lock)))
(acquire-lock lock)
(release-lock lock)))))
(test with-lock-held.not-implemented
(if (implemented-p 'bt2:with-lock-held)
(pass)
(signals not-implemented
(let ((lock (make-lock)))
(with-lock-held (lock))))))
(test make-recursive-lock.not-implemented
(if (implemented-p 'bt2:make-recursive-lock)
(pass)
(signals not-implemented (make-recursive-lock))))
(test acquire-recursive-lock.not-implemented
(if (implemented-p 'bt2:acquire-recursive-lock)
(pass)
(signals not-implemented
(acquire-recursive-lock (make-recursive-lock)))))
(test release-recursive-lock.not-implemented
(if (implemented-p 'bt2:release-recursive-lock)
(pass)
(signals not-implemented
(let ((lock (make-recursive-lock)))
(acquire-recursive-lock lock)
(release-recursive-lock lock)))))
(test with-recursive-lock-held.not-implemented
(if (implemented-p 'bt2:with-recursive-lock-held)
(pass)
(signals not-implemented
(let ((lock (make-recursive-lock)))
(with-recursive-lock-held (lock))))))
;;;
;;; Condition variables
;;;
;;;
;;; Semaphores
;;;
bordeaux-threads-0.9.4/test/pkgdcl.lisp000066400000000000000000000014141463556250700201160ustar00rootroot00000000000000;;;; -*- Mode: LISP; Syntax: ANSI-Common-lisp; Base: 10; Package: CL-USER -*-
;;;; The above modeline is required for Genera. Do not change.
(defpackage :bordeaux-threads-2/test
(:use :common-lisp :alexandria :bordeaux-threads-2 :fiveam)
(:import-from :bordeaux-threads-2
#:mark-not-implemented #:*not-implemented*
#:implemented-p #:implemented-p*)
(:shadow #:is))
(in-package :bordeaux-threads-2/test)
(def-suite :bordeaux-threads-2)
(defmacro is (test &rest reason-args)
(with-gensyms (c)
`(handler-case
(5am:is ,test ,@reason-args)
((or bt2::operation-not-implemented
bt2::keyarg-not-implemented) (,c)
(declare (ignore ,c))
(5am:skip "Skipping operations that are not implemented")))))
bordeaux-threads-0.9.4/test/tests-v1.lisp000066400000000000000000000227501463556250700203460ustar00rootroot00000000000000;;;; -*- Mode: LISP; Syntax: ANSI-Common-lisp; Base: 10; Package: CL-USER -*-
;;;; The above modeline is required for Genera. Do not change.
#|
Copyright 2006,2007 Greg Pfeil
Distributed under the MIT license (see LICENSE file)
|#
(defpackage bordeaux-threads/test
(:use #:cl #:bordeaux-threads #:fiveam)
(:shadow #:with-timeout))
(in-package #:bordeaux-threads/test)
(def-suite :bordeaux-threads)
(def-fixture using-lock ()
(let ((lock (make-lock)))
(&body)))
(in-suite :bordeaux-threads)
(test should-have-current-thread
(is (current-thread)))
(test current-thread-identity
(let* ((box (list nil))
(thread (make-thread (lambda ()
(setf (car box) (current-thread))))))
(join-thread thread)
(is (eql (car box) thread))))
(test current-thread-eql
(is (eql (current-thread)
(current-thread))))
(test join-thread-return-value
(is (eql 0 (join-thread (make-thread (lambda () 0))))))
(test should-identify-threads-correctly
(is (threadp (current-thread)))
(is (threadp (make-thread (lambda () t) :name "foo")))
(is (not (threadp (make-lock)))))
(test should-retrieve-thread-name
(is (equal "foo" (thread-name (make-thread (lambda () t) :name "foo")))))
(test interrupt-thread
(let* ((box (list nil))
(thread (make-thread (lambda ()
(setf (car box)
(catch 'new-thread
(sleep 60)
'not-interrupted))))))
(sleep 1)
(interrupt-thread thread (lambda ()
(throw 'new-thread 'interrupted)))
(join-thread thread)
(is (eql 'interrupted (car box)))))
(test should-lock-without-contention
(with-fixture using-lock ()
(is (acquire-lock lock t))
(release-lock lock)
(is (acquire-lock lock nil))
(release-lock lock)))
#-(or allegro sbcl)
(def-test acquire-recursive-lock ()
(let ((test-lock (make-recursive-lock))
(results (make-array 4 :adjustable t :fill-pointer 0))
(results-lock (make-lock))
(threads ()))
(flet ((add-result (r)
(with-lock-held (results-lock)
(vector-push-extend r results))))
(dotimes (i 2)
(push (make-thread
#'(lambda ()
(when (acquire-recursive-lock test-lock)
(unwind-protect
(progn
(add-result :enter)
(sleep 1)
(add-result :leave))
(release-recursive-lock test-lock)))))
threads)))
(map 'nil #'join-thread threads)
(is (equalp results #(:enter :leave :enter :leave)))))
(defun set-equal (set-a set-b)
(and (null (set-difference set-a set-b))
(null (set-difference set-b set-a))))
(test default-special-bindings
(locally (declare (special *a* *c*))
(let* ((the-as 50) (the-bs 150) (*b* 42)
some-a some-b some-other-a some-other-b
(*default-special-bindings*
`((*a* . (funcall ,(lambda () (incf the-as))))
(*b* . (funcall ,(lambda () (incf the-bs))))
,@*default-special-bindings*))
(threads (list (make-thread
(lambda ()
(setf some-a *a* some-b *b*)))
(make-thread
(lambda ()
(setf some-other-a *a*
some-other-b *b*))))))
(declare (special *b*))
(thread-yield)
(is (not (boundp '*a*)))
(loop while (some #'thread-alive-p threads)
do (thread-yield))
(is (set-equal (list some-a some-other-a) '(51 52)))
(is (set-equal (list some-b some-other-b) '(151 152)))
(is (not (boundp '*a*))))))
(defparameter *shared* 0)
(defparameter *lock* (make-lock))
(test should-have-thread-interaction
;; this simple test generates N process. Each process grabs and
;; releases the lock until SHARED has some value, it then
;; increments SHARED. the outer code first sets shared 1 which
;; gets the thing running and then waits for SHARED to reach some
;; value. this should, i think, stress test locks.
(setf *shared* 0)
(flet ((worker (i)
(loop
do (with-lock-held (*lock*)
(when (= i *shared*)
(incf *shared*)
(return)))
(thread-yield)
(sleep 0.001))))
(let* ((procs (loop
for i from 1 upto 2
;; create a new binding to protect against implementations that
;; mutate instead of binding the loop variable
collect (let ((i i))
(make-thread (lambda ()
(funcall #'worker i))
:name (format nil "Proc #~D" i))))))
(with-lock-held (*lock*)
(incf *shared*))
(block test
(loop
until (with-lock-held (*lock*)
(= (1+ (length procs)) *shared*))
do (with-lock-held (*lock*)
(is (>= (1+ (length procs)) *shared*)))
(thread-yield)
(sleep 0.001))))))
;; Generally safe sanity check for the locks and single-notify
#+(and lispworks (or lispworks4 lispworks5))
(test condition-variable-lw
(let ((condition-variable (make-condition-variable :name "Test"))
(test-lock (make-lock))
(completed nil))
(dotimes (id 6)
(let ((id id))
(make-thread (lambda ()
(with-lock-held (test-lock)
(condition-wait condition-variable test-lock)
(push id completed)
(condition-notify condition-variable))))))
(sleep 2)
(if completed
(print "Failed: Premature passage through condition-wait")
(print "Successfully waited on condition"))
(condition-notify condition-variable)
(sleep 2)
(if (and completed
(eql (length completed) 6)
(equal (sort completed #'<)
(loop for id from 0 to 5 collect id)))
(print "Success: All elements notified")
(print (format nil "Failed: Of 6 expected elements, only ~A proceeded" completed)))
(bt::with-cv-access condition-variable
(if (and
(not (or (car wait-tlist) (cdr wait-tlist)))
(zerop (hash-table-count wait-hash))
(zerop (hash-table-count unconsumed-notifications)))
(print "Success: condition variable restored to initial state")
(print "Error: condition variable retains residue from completed waiters")))
(setq completed nil)
(dotimes (id 6)
(let ((id id))
(make-thread (lambda ()
(with-lock-held (test-lock)
(condition-wait condition-variable test-lock)
(push id completed))))))
(sleep 2)
(condition-notify condition-variable)
(sleep 2)
(if (= (length completed) 1)
(print "Success: Notify-single only notified a single waiter to restart")
(format t "Failure: Notify-single restarted ~A items" (length completed)))
(condition-notify condition-variable)
(sleep 2)
(if (= (length completed) 2)
(print "Success: second Notify-single only notified a single waiter to restart")
(format t "Failure: Two Notify-singles restarted ~A items" (length completed)))
(loop for i from 0 to 5 do (condition-notify condition-variable))
(print "Note: In the case of any failures, assume there are outstanding waiting threads")
(values)))
#+(or abcl allegro clisp clozure ecl genera lispworks6 mezzano sbcl scl)
(test condition-wait-timeout
(let ((lock (make-lock))
(cvar (make-condition-variable))
(flag nil))
(make-thread (lambda () (sleep 0.4) (setf flag t)))
(with-lock-held (lock)
(condition-wait cvar lock :timeout 0.2)
(is (null flag))
(sleep 0.4)
(is (eq t flag)))))
(test semaphore-signal
(let ((sem (make-semaphore)))
(make-thread (lambda () (sleep 0.4) (signal-semaphore sem)))
(is (not (null (wait-on-semaphore sem))))))
(test semaphore-signal-n-of-m
(let* ((sem (make-semaphore :count 1))
(lock (make-lock))
(count 0)
(waiter (lambda ()
(wait-on-semaphore sem)
(with-lock-held (lock) (incf count)))))
(make-thread (lambda () (sleep 0.2) (signal-semaphore sem :count 3)))
(dotimes (v 5) (make-thread waiter))
(sleep 0.3)
(is (= count 4))
;; release other waiters
(signal-semaphore sem :count 10)
(sleep 0.1)
(is (= count 5))))
(test semaphore-wait-timeout
(let ((sem (make-semaphore))
(flag nil))
(make-thread (lambda () (sleep 3) (setf flag t)))
(is (null (wait-on-semaphore sem :timeout 0.2)))
(is (eql nil flag))
(sleep 5)
(is (eql t flag))))
(test semaphore-typed
(is (typep (bt:make-semaphore) 'bt:semaphore))
(is (bt:semaphore-p (bt:make-semaphore)))
(is (null (bt:semaphore-p (bt:make-lock)))))
(test with-timeout-return-value
(is (eql :foo (bt:with-timeout (5) :foo))))
(test with-timeout-signals
(signals timeout (bt:with-timeout (1) (sleep 5))))
(test with-timeout-non-interference
(flet ((sleep-with-timeout (s)
(bt:with-timeout (4) (sleep s))))
(finishes
(progn
(sleep-with-timeout 3)
(sleep-with-timeout 3)))))
bordeaux-threads-0.9.4/test/tests-v2.lisp000066400000000000000000000431241463556250700203450ustar00rootroot00000000000000;;;; -*- Mode: LISP; Syntax: ANSI-Common-lisp; Base: 10; Package: BORDEAUX-THREADS-2/TEST -*-
;;;; The above modeline is required for Genera. Do not change.
(in-package :bordeaux-threads-2/test)
(in-suite :bordeaux-threads-2)
;;;
;;; Threads
;;;
(test join-thread.return-value
(is (eql 0 (join-thread (make-thread (lambda () 0))))))
(test current-thread.not-null
(is (current-thread)))
(test current-thread.eql
(is (eql (current-thread)
(current-thread))))
#+#.(bt2::implemented-p* 'bt2:join-thread)
(test current-thread.identity
(let ((thread (make-thread #'current-thread)))
(is (eql thread (join-thread thread)))))
#+#.(bt2::implemented-p* 'bt2:join-thread)
(test current-thread.special
(let ((thread (make-thread (lambda () bt2::*current-thread*))))
(is (eql thread (join-thread thread)))))
#+#.(bt2::implemented-p* 'bt2:join-thread)
(test current-thread.error
(let ((thread (make-thread (lambda ()
(error "FOOBAR"))
:trap-conditions t)))
(signals abnormal-exit (join-thread thread))))
(test threadp.should-identify-threads
(is (threadp (current-thread)))
(is (threadp (make-thread (lambda () t))))
(is (not (threadp (make-lock)))))
(test thread-name.should-retrieve-thread-name
(is (equal "foo" (thread-name
(make-thread (lambda () t) :name "foo")))))
(test thread-name.all-strings
(is (every #'(lambda (thread) (stringp (thread-name thread)))
(all-threads))))
(defparameter *some-special* :global-value)
(test default-special-bindings.sees-global-bindings
(let* ((*some-special* :local-value)
(*default-special-bindings*
`((*some-special* . (list :more *some-special*))
,@*default-special-bindings*))
(thread (make-thread (lambda () *some-special*))))
(is (equal '(:more :local-value) (join-thread thread)))))
(defparameter *shared* 0)
(defparameter *lock* (make-lock))
#+#.(bt2::implemented-p* 'bt2:thread-yield)
(test threads.interaction
;; this simple test generates N process. Each process grabs and
;; releases the lock until SHARED has some value, it then
;; increments SHARED. the outer code first sets shared 1 which
;; gets the thing running and then waits for SHARED to reach some
;; value. this should, i think, stress test locks.
(setf *shared* 0)
(flet ((worker (i)
(loop
do (with-lock-held (*lock*)
(when (= i *shared*)
(incf *shared*)
(return)))
(thread-yield)
(sleep 0.001))))
(let* ((procs (loop
for i from 1 upto 2
;; create a new binding to protect against implementations that
;; mutate instead of binding the loop variable
collect (let ((i i))
(make-thread (lambda ()
(funcall #'worker i))
:name (format nil "threads.interaction Proc #~D" i))))))
(with-lock-held (*lock*)
(incf *shared*))
(block test
(loop
until (with-lock-held (*lock*)
(= (1+ (length procs)) *shared*))
do (with-lock-held (*lock*)
(is (>= (1+ (length procs)) *shared*)))
(thread-yield)
(sleep 0.001))))))
(test all-threads.contains-threads
(is (every #'threadp (all-threads))))
(test all-threads.contains-new-thread
(let ((thread (make-thread (lambda () (sleep 60))
:name "all-threads.contains-new-thread")))
(is (find thread (all-threads)))))
#+#.(bt2::implemented-p* 'bt2:interrupt-thread)
(test interrupt-thread.throw
(let ((thread (make-thread (lambda ()
(catch 'new-thread
(sleep 60)
'not-interrupted))
:name "interrupt-thread.throw")))
(sleep 1)
(is (threadp
(interrupt-thread thread (lambda ()
(throw 'new-thread 'interrupted)))))
(is (eql 'interrupted (join-thread thread)))))
(test thread-alive-p.new-thread
(is (thread-alive-p (make-thread (lambda () (sleep 60))
:name "thread-alive-p.new-thread"))))
#+#.(bt2::implemented-p* 'bt2:join-thread)
(test thread-termination.unwind-protect
(setf *some-special* nil)
#+abcl
(skip "DESTROY-THREAD does not execute UNWIND-PROTECT cleanup forms.
Filed https://github.com/armedbear/abcl/issues/430.")
#-abcl
(flet ((thread-fn ()
(setf *some-special* :entered)
(unwind-protect
(progn
(sleep 5)
(setf *some-special* :failed))
(when (eq *some-special* :entered)
(setf *some-special* :success)))))
(let ((thread (make-thread #'thread-fn)))
(sleep 1)
(destroy-thread thread)
(signals abnormal-exit
(join-thread thread))
(is (eq :success *some-special*)))))
(define-condition test-error (error) ())
#+#.(bt2::implemented-p* 'bt2:join-thread)
(test thread-termination.handle-condition
(flet ((thread-fn ()
(error 'test-error)))
(let ((thread (make-thread #'thread-fn :trap-conditions t)))
(handler-case
(join-thread thread)
(abnormal-exit (e)
(is (typep (abnormal-exit-condition e) 'test-error)))))))
#+#.(bt2::implemented-p* 'bt2:destroy-thread)
(test destroy-thread.terminates
(let ((thread (make-thread (lambda () (sleep 3))
:name "destroy-thread.terminates")))
(is (threadp (destroy-thread thread)))
(sleep 5)
(is-false (thread-alive-p thread))))
#+#.(bt2::implemented-p* 'bt2:destroy-thread)
(test join-thread.error-if-destroyed
(let ((thread (make-thread (lambda () (sleep 3))
:name "join-thread.error-if-destroyed")))
(destroy-thread thread)
(signals abnormal-exit (join-thread thread))))
#+#.(bt2::implemented-p* 'bt2:destroy-thread)
(test destroy-thread.error-if-exited
(let ((thread (make-thread (lambda () (sleep 3))
:name "destroy-thread.error-if-exited")))
(join-thread thread)
(signals bordeaux-threads-error (destroy-thread thread))))
;;;
;;; Non-recursive Locks
;;;
(test lock.constructor
(let ((lock (make-lock :name "Name")))
(is (lockp lock))
(is (native-lock-p (lock-native-lock lock)))
(is (equal "Name" (lock-name lock)))))
(test acquire-lock.no-contention
(let ((lock (make-lock)))
(is (acquire-lock lock :wait t))
(is (lockp (release-lock lock)))
(is (acquire-lock lock :wait nil))
(is (lockp (release-lock lock)))))
(test acquire-lock.try-lock
(let ((lock (make-lock)))
(make-thread (lambda ()
(with-lock-held (lock)
(sleep 5)))
:name "acquire-lock.try-lock")
(sleep 1)
(is-false (acquire-lock lock :wait nil))))
(test acquire-lock.timeout-expires
(let ((lock (make-lock)))
(make-thread (lambda ()
(with-lock-held (lock)
(sleep 5)))
:name "acquire-lock.timeout-expires")
(sleep 1)
(is (null (acquire-lock lock :timeout .1)))))
#+#.(bt2::implemented-p* 'bt2:with-lock-held)
(test with-lock-held.timeout-no-contention-acquired
(let ((lock (make-lock)))
(is (eql :ok (with-lock-held (lock :timeout .1) :ok)))))
#+#.(bt2::implemented-p* 'bt2:with-lock-held)
(test with-lock-held.timeout-expires
(let ((lock (make-lock)))
(make-thread (lambda ()
(with-lock-held (lock)
(sleep 5)))
:name "with-lock-held.timeout-expires")
(sleep 1)
(is (eql :timeout
(block ok
(with-lock-held (lock :timeout .1)
(return-from ok :ok))
:timeout)))))
;;;
;;; Recursive Locks
;;;
#+#.(bt2::implemented-p* 'bt2:acquire-recursive-lock)
(test acquire-recursive-lock
(let ((test-lock (make-recursive-lock))
(results (make-array 4 :adjustable t :fill-pointer 0))
(results-lock (make-lock))
(threads ()))
(flet ((add-result (r)
(with-lock-held (results-lock)
(vector-push-extend r results))))
(dotimes (i 2)
(push (make-thread
#'(lambda ()
(when (acquire-recursive-lock test-lock)
(unwind-protect
(progn
(add-result :enter)
(sleep 1)
(add-result :leave))
(release-recursive-lock test-lock))))
:name (format nil "acquire-recursive-lock Proc #~D" i))
threads)))
(map 'nil #'join-thread threads)
(is (equalp #(:enter :leave :enter :leave) results))))
#+#.(bt2::implemented-p* 'bt2:acquire-recursive-lock)
(test acquire-recursive-lock.no-contention
(let ((lock (make-recursive-lock)))
(is (acquire-recursive-lock lock :wait t))
(is (recursive-lock-p (release-recursive-lock lock)))
(is (acquire-recursive-lock lock :wait nil))
(is (recursive-lock-p (release-recursive-lock lock)))))
#+#.(bt2::implemented-p* 'bt2:with-recursive-lock-held)
(test acquire-recursive-lock.try-lock
(let ((lock (make-recursive-lock)))
(make-thread (lambda ()
(with-recursive-lock-held (lock)
(sleep 5)))
:name "acquire-recursive-lock.try-lock")
(sleep 1)
(is (null (acquire-recursive-lock lock :wait nil)))))
#+#.(bt2::implemented-p* 'bt2:with-recursive-lock-held)
(test acquire-recursive-lock.timeout-expires
(let ((lock (make-recursive-lock)))
(make-thread (lambda ()
(with-recursive-lock-held (lock)
(sleep 5)))
:name "acquire-recursive-lock.timeout-expires")
(sleep 1)
(is (null (acquire-recursive-lock lock :timeout .1)))))
#+#.(bt2::implemented-p* 'bt2:with-recursive-lock-held)
(test with-recursive-lock-held.timeout-no-contention-acquired
(let ((lock (make-recursive-lock)))
(is (eql :ok (with-recursive-lock-held (lock :timeout .1) :ok)))))
#+#.(bt2::implemented-p* 'bt2:with-recursive-lock-held)
(test with-recursive-lock-held.timeout-expires
(let ((lock (make-recursive-lock)))
(make-thread (lambda ()
(with-recursive-lock-held (lock)
(sleep 5)))
:name "with-recursive-lock-held.timeout-expires")
(sleep 1)
(is (eql :timeout
(block ok
(with-recursive-lock-held (lock :timeout .1)
(return-from ok :ok))
:timeout)))))
;;;
;;; Semaphores
;;;
#+#.(bt2::implemented-p* 'bt2:make-semaphore)
(progn
(test semaphore.typed
(is (typep (make-semaphore) 'semaphore))
(is (semaphorep (make-semaphore)))
(is (not (semaphorep (make-lock)))))
(test semaphore.signal
(let ((sem (make-semaphore)))
(make-thread (lambda () (sleep 0.4) (signal-semaphore sem)))
(is-true (wait-on-semaphore sem))
(is-true (signal-semaphore sem))))
(test semaphore.wait-on-nonzero-creation
"Tests that `WAIT-ON-SEMAPHORE` correctly returns T
on a smaphore that was initialized to a non-zero value.
In other words, it tests that `SIGNAL-SEMAPHORE` is not
the only cause that can wake a waiter."
(let ((sem (make-semaphore :count 1)))
(is-true (wait-on-semaphore sem :timeout 0))))
(test semaphore.wait.timeout
(let* ((sem (make-semaphore)))
(is-false (wait-on-semaphore sem :timeout 0))
(is-false (wait-on-semaphore sem :timeout 0.2))))
(test semaphore.signal-n-of-m
(let* ((sem (make-semaphore :count 1))
(lock (make-lock))
(count 0)
(waiter (lambda ()
(wait-on-semaphore sem)
(with-lock-held (lock) (incf count)))))
(make-thread (lambda ()
(sleep 0.2)
(signal-semaphore sem :count 3)))
(dotimes (v 5) (make-thread waiter))
(sleep 0.3)
(is (= 4 count))
;; release other waiters
(is (eql t (signal-semaphore sem :count 2)))
(sleep 0.1)
(is (= 5 count)))))
;;;
;;; Condition variables
;;;
#+#.(bt2::implemented-p* 'bt2:make-condition-variable)
(test condition-variable.typed
(is (typep (make-condition-variable) 'condition-variable))
(is (condition-variable-p (make-condition-variable)))
(is (not (condition-variable-p (make-lock)))))
#+#.(bt2::implemented-p* 'bt2:make-condition-variable)
(test condition-variable.concurrency
(setf *shared* 0)
(let ((cv (make-condition-variable)))
(flet ((worker (i)
(with-lock-held (*lock*)
(loop
until (= i *shared*)
do (condition-wait cv *lock*)
(sleep (random .1)))
(incf *shared*))
(condition-broadcast cv)))
(let ((num-procs 30))
(dotimes (i num-procs)
(let ((i (- num-procs i 1)))
(make-thread (lambda ()
(sleep (random 1))
(funcall #'worker i))
:name (format nil "Proc #~D" i))))
(with-lock-held (*lock*)
(loop
until (= num-procs *shared*)
do (condition-wait cv *lock*)))
(is (equal num-procs *shared*))))))
#+#.(bt2::implemented-p* 'bt2:condition-wait :timeout)
(test condition-wait.timeout
(let ((lock (make-lock))
(cv (make-condition-variable))
(flag nil))
(make-thread (lambda () (sleep 0.4) (setf flag t)))
(with-lock-held (lock)
(let ((success
(condition-wait cv lock :timeout 0.2)))
#+abcl
(skip "ABCL's condition-wait always returns T")
#-abcl
(is-false success)
(is (null flag))
(sleep 0.4)
(is (eq t flag))))))
#+#.(bt2::implemented-p* 'bt2:condition-wait :timeout)
(test condition-wait.lock-held-on-timeout
"Tests that even when `CONDITION-WAIT` times out, it reacquires the
lock."
(let ((lock (make-lock :name "Test lock"))
(cv (make-condition-variable :name "Test condition variable")))
(with-lock-held (lock)
(let ((success
(condition-wait cv lock :timeout 2)))
#+abcl
(skip "ABCL's condition-wait always returns T")
#-abcl
(is-false success)
;; We need to test if `lock` is locked, but it must be done in
;; another thread, otherwise it would be a recursive attempt.
(let ((res-lock (make-lock :name "Result lock"))
(res-cv (make-condition-variable :name "Result condition variable"))
(lock-was-acquired-p nil))
(make-thread (lambda ()
(with-lock-held (res-lock)
(setf lock-was-acquired-p (acquire-lock lock :wait nil)))
(condition-notify res-cv)))
(with-lock-held (res-lock)
(condition-wait res-cv res-lock)
(is-false lock-was-acquired-p)))))))
#+#.(bt2::implemented-p* 'bt2:make-condition-variable)
(test condition-notify.no-waiting-threads
"Test that `CONDITION-NOTIFY` returns NIL whether or not there are
threads waiting."
(let ((lock (make-lock :name "Test lock"))
(cv (make-condition-variable :name "Test condition variable")))
(is-false (condition-notify cv))
(make-thread (lambda ()
(with-lock-held (lock)
(condition-wait cv lock))))
(is-false (condition-notify cv))))
#+#.(bt2::implemented-p* 'bt2:make-condition-variable)
(test condition-broadcast.return-value
"Test that `CONDITION-BROADCAST` returns NIL whether or not there
are threads waiting."
(let ((lock (make-lock :name "Test lock"))
(cv (make-condition-variable :name "Test condition variable")))
(is-false (condition-notify cv))
(make-thread (lambda ()
(with-lock-held (lock)
(condition-wait cv lock)))
:name "Waiting thread 1")
(make-thread (lambda ()
(with-lock-held (lock)
(condition-wait cv lock)))
:name "Waiting thread 2")
(is-false (condition-broadcast cv))))
;;;
;;; Timeouts
;;;
(test with-timeout.return-value
(is (eql :foo (with-timeout (5) :foo))))
(test with-timeout.signals
(signals timeout (with-timeout (1) (sleep 5))))
(test with-timeout.non-interference
(flet ((sleep-with-timeout (s)
(with-timeout (4) (sleep s))))
(finishes
(progn
(sleep-with-timeout 3)
(sleep-with-timeout 3)))))
;;;
;;; Atomics
;;;
#+(or abcl allegro ccl clisp ecl lispworks sbcl)
(test atomic-integer-incf-decf.return-value
(let ((aint (make-atomic-integer :value 0)))
(is (= 5 (atomic-integer-incf aint 5)))
(is (= 4 (atomic-integer-decf aint 1)))))
#+(or abcl allegro ccl clisp ecl lispworks sbcl)
(test atomic-integer-compare-and-swap.return-value
(let ((aint (make-atomic-integer :value 4)))
(is (null (atomic-integer-compare-and-swap aint 0 100)))
(is (eql t (atomic-integer-compare-and-swap aint 4 7)))))
#+(or abcl allegro ccl clisp ecl lispworks sbcl)
(test atomic-integer.concurrency
(let* ((aint (make-atomic-integer :value 1000000))
(thread-inc
(make-thread (lambda ()
(dotimes (i 1000000)
(atomic-integer-incf aint)))))
(thread-dec
(make-thread (lambda ()
(dotimes (i 1000000)
(atomic-integer-decf aint))))))
(join-thread thread-inc)
(join-thread thread-dec)
(is (= 1000000 (atomic-integer-value aint)))))
bordeaux-threads-0.9.4/version.sexp000066400000000000000000000000301463556250700173610ustar00rootroot00000000000000;; -*- lisp -*-
"0.9.4"