SRFI-18 Threads¶
Multithreading primitives for concurrent programming. Import with
(import (srfi 18)). See also Kaappi Extensions for
lightweight green threads (fibers).
Note
thread-start! requires the --experimental-threads flag. Without it,
calling thread-start! raises an error. Each OS thread gets its own VM
and GC heap -- values are deep-copied when crossing thread boundaries.
See Advanced Features for details.
Thread Operations¶
current-thread¶
Syntax: (current-thread)
Returns the thread object representing the currently executing thread.
See also: thread?, make-thread
thread?¶
Syntax: (thread? obj)
Returns #t if obj is a thread object, #f otherwise.
kaappi> (thread? (current-thread))
;=> #t
kaappi> (thread? (make-thread (lambda () 42)))
;=> #t
kaappi> (thread? 'not-a-thread)
;=> #f
See also: current-thread, make-thread
make-thread¶
Syntax: (make-thread thunk) | (make-thread thunk name)
Creates a new thread that will execute thunk (a procedure of zero
arguments) when started. The optional name is a string used for
identification and debugging. The thread is created in a non-started
state; call thread-start! to begin execution.
kaappi> (define t (make-thread (lambda () (+ 1 2))))
kaappi> t
;=> #<thread>
kaappi> (define t2 (make-thread (lambda () 'done) "worker"))
kaappi> (thread-name t2)
;=> "worker"
See also: thread-start!, thread-join!
thread-name¶
Syntax: (thread-name thread)
Returns the name associated with thread, or #f if no name was given
at creation time.
kaappi> (thread-name (make-thread (lambda () #f) "my-thread"))
;=> "my-thread"
kaappi> (thread-name (make-thread (lambda () #f)))
;=> #f
See also: make-thread
thread-specific¶
Syntax: (thread-specific thread)
Returns the thread-specific value associated with thread. Each thread
has a single slot for storing an arbitrary value, initially #f.
kaappi> (thread-specific (current-thread))
;=> #f
kaappi> (thread-specific-set! (current-thread) '(my data))
kaappi> (thread-specific (current-thread))
;=> (my data)
See also: thread-specific-set!
thread-specific-set!¶
Syntax: (thread-specific-set! thread obj)
Sets the thread-specific value of thread to obj. This can be used to associate per-thread data without using global mutable state.
kaappi> (thread-specific-set! (current-thread) 42)
kaappi> (thread-specific (current-thread))
;=> 42
See also: thread-specific
thread-start!¶
Syntax: (thread-start! thread)
Starts thread and returns the thread object. The thread begins
executing the thunk that was passed to make-thread. A thread can only
be started once; starting an already-started thread is an error.
Requires --experimental-threads. Without the flag, thread-start!
raises an error. The child thread receives a deep copy of the thunk's
closure -- mutations in the child are not visible to the parent.
kaappi> (define t (make-thread (lambda () (display "hello\n"))))
kaappi> (thread-start! t)
;=> #<thread>
hello
kaappi> (thread-join! t)
See also: make-thread, thread-join!
thread-yield!¶
Syntax: (thread-yield!)
Causes the current thread to voluntarily give up its time slice, allowing the scheduler to run other ready threads. Returns an unspecified value.
See also: thread-sleep!,
yield (fiber equivalent)
thread-sleep!¶
Syntax: (thread-sleep! timeout)
Causes the current thread to sleep until timeout. The timeout can
be a time object (from seconds->time) or a number of seconds as an
inexact real. Other threads continue to run during the sleep.
kaappi> (thread-sleep! 0.5) ; sleep 500ms
kaappi> (thread-sleep! (seconds->time (+ (time->seconds (current-time)) 1)))
See also: thread-yield!,
current-time, seconds->time
thread-terminate!¶
Syntax: (thread-terminate! thread)
Forcefully terminates thread. Any thread that subsequently calls
thread-join! on the terminated thread will receive a
terminated-thread-exception. Use with caution: the terminated thread
does not get a chance to release resources or unlock mutexes.
kaappi> (define t (make-thread (lambda () (thread-sleep! 100))))
kaappi> (thread-start! t)
;=> #<thread>
kaappi> (thread-terminate! t)
Note
Terminating a thread that holds a mutex leaves the mutex in an
abandoned state. Subsequent attempts to lock it will raise an
abandoned-mutex-exception.
See also: thread-join!,
terminated-thread-exception?
thread-join!¶
Syntax: (thread-join! thread) | (thread-join! thread timeout) | (thread-join! thread timeout timeout-val)
Blocks the current thread until thread terminates. Returns the result
value of the thread's thunk (deep-copied from the child's heap to the
caller's heap). If the thread raised an uncaught exception, thread-join!
re-raises it wrapped in an uncaught-exception object.
If timeout is given (a time object or number of seconds) and the thread
has not terminated by then, a join-timeout-exception is raised -- unless
timeout-val is provided, in which case that value is returned instead.
kaappi> (define t (make-thread (lambda () (* 6 7))))
kaappi> (thread-start! t)
;=> #<thread>
kaappi> (thread-join! t)
;=> 42
kaappi> (define t2 (make-thread (lambda () (thread-sleep! 10))))
kaappi> (thread-start! t2)
;=> #<thread>
kaappi> (thread-join! t2 0.1 'timed-out)
;=> timed-out
See also: thread-start!,
join-timeout-exception?,
uncaught-exception?
Mutexes¶
mutex?¶
Syntax: (mutex? obj)
Returns #t if obj is a mutex, #f otherwise.
See also: make-mutex
make-mutex¶
Syntax: (make-mutex) | (make-mutex name)
Creates a new mutex in the unlocked state. The optional name is a string used for identification and debugging.
kaappi> (define m (make-mutex "my-lock"))
kaappi> m
;=> #<mutex my-lock>
kaappi> (mutex-state m)
;=> not-abandoned
See also: mutex-lock!, mutex-unlock!
mutex-name¶
Syntax: (mutex-name mutex)
Returns the name associated with mutex, or #f if no name was given.
See also: make-mutex
mutex-specific¶
Syntax: (mutex-specific mutex)
Returns the mutex-specific value associated with mutex, initially #f.
kaappi> (define m (make-mutex))
kaappi> (mutex-specific m)
;=> #f
kaappi> (mutex-specific-set! m 'data)
kaappi> (mutex-specific m)
;=> data
See also: mutex-specific-set!
mutex-specific-set!¶
Syntax: (mutex-specific-set! mutex obj)
Sets the mutex-specific value of mutex to obj.
kaappi> (define m (make-mutex))
kaappi> (mutex-specific-set! m '(resource-info))
kaappi> (mutex-specific m)
;=> (resource-info)
See also: mutex-specific
mutex-state¶
Syntax: (mutex-state mutex)
Returns the state of mutex. Possible return values:
- A thread object -- the mutex is locked and owned by that thread
- The symbol
not-owned-- the mutex is locked but not owned - The symbol
abandoned-- the mutex was abandoned by a terminated thread - The symbol
not-abandoned-- the mutex is unlocked
kaappi> (define m (make-mutex))
kaappi> (mutex-state m)
;=> not-abandoned
kaappi> (mutex-lock! m)
;=> #t
kaappi> (mutex-state m)
;=> #<thread main>
See also: mutex-lock!, mutex-unlock!
mutex-lock!¶
Syntax: (mutex-lock! mutex) | (mutex-lock! mutex timeout) | (mutex-lock! mutex timeout thread)
Locks mutex. If the mutex is already locked, the current thread blocks
until it becomes available. Returns #t if the lock was acquired.
If timeout is given (a time object or number of seconds) and the mutex
cannot be acquired within that time, returns #f. The optional thread
argument specifies the new owner of the mutex (defaults to the current
thread); passing #f locks the mutex without an owner.
kaappi> (define m (make-mutex))
kaappi> (mutex-lock! m)
;=> #t
kaappi> (mutex-state m)
;=> #<thread main>
kaappi> (mutex-unlock! m)
;=> #t
See also: mutex-unlock!, make-mutex
mutex-unlock!¶
Syntax: (mutex-unlock! mutex) | (mutex-unlock! mutex condition-variable) | (mutex-unlock! mutex condition-variable timeout)
Unlocks mutex and returns #t. If a condition-variable is given,
the current thread is blocked on that condition variable and the mutex is
unlocked atomically. If timeout is also given, the thread unblocks
after the timeout even if the condition variable was not signaled, and
returns #f.
kaappi> (define m (make-mutex))
kaappi> (mutex-lock! m)
;=> #t
kaappi> (mutex-unlock! m)
;=> #t
kaappi> (mutex-state m)
;=> not-abandoned
See also: mutex-lock!,
condition-variable-signal!,
condition-variable-broadcast!
Condition Variables¶
condition-variable?¶
Syntax: (condition-variable? obj)
Returns #t if obj is a condition variable, #f otherwise.
kaappi> (condition-variable? (make-condition-variable))
;=> #t
kaappi> (condition-variable? (make-mutex))
;=> #f
See also: make-condition-variable
make-condition-variable¶
Syntax: (make-condition-variable) | (make-condition-variable name)
Creates a new condition variable. The optional name is a string used for identification and debugging.
kaappi> (define cv (make-condition-variable "data-ready"))
kaappi> cv
;=> #<condition-variable data-ready>
See also: condition-variable-signal!,
condition-variable-broadcast!
condition-variable-name¶
Syntax: (condition-variable-name condition-variable)
Returns the name associated with condition-variable, or #f if no
name was given.
kaappi> (condition-variable-name (make-condition-variable "cv-1"))
;=> "cv-1"
kaappi> (condition-variable-name (make-condition-variable))
;=> #f
See also: make-condition-variable
condition-variable-specific¶
Syntax: (condition-variable-specific condition-variable)
Returns the condition-variable-specific value, initially #f.
See also: condition-variable-specific-set!
condition-variable-specific-set!¶
Syntax: (condition-variable-specific-set! condition-variable obj)
Sets the condition-variable-specific value to obj.
kaappi> (define cv (make-condition-variable))
kaappi> (condition-variable-specific-set! cv 'waiting)
kaappi> (condition-variable-specific cv)
;=> waiting
See also: condition-variable-specific
condition-variable-signal!¶
Syntax: (condition-variable-signal! condition-variable)
Wakes one thread blocked on condition-variable (if any). If multiple threads are waiting, exactly one is selected to be woken. Which thread is chosen is implementation-dependent. If no threads are waiting, the signal has no effect.
kaappi> (define cv (make-condition-variable))
kaappi> (define m (make-mutex))
kaappi> ;; In a producer/consumer pattern:
kaappi> (condition-variable-signal! cv)
See also: condition-variable-broadcast!,
mutex-unlock!
condition-variable-broadcast!¶
Syntax: (condition-variable-broadcast! condition-variable)
Wakes all threads blocked on condition-variable. If no threads are waiting, the broadcast has no effect.
See also: condition-variable-signal!,
mutex-unlock!
Time¶
current-time¶
Syntax: (current-time)
Returns the current time as a time object. This is used with
time->seconds and seconds->time for computing timeouts in threading
operations.
See also: time?, time->seconds,
seconds->time
time?¶
Syntax: (time? obj)
Returns #t if obj is a time object, #f otherwise.
See also: current-time
time->seconds¶
Syntax: (time->seconds time)
Converts a time object to an inexact real number representing seconds since the epoch.
See also: seconds->time,
current-time
seconds->time¶
Syntax: (seconds->time seconds)
Converts an inexact real number representing seconds since the epoch
to a time object. Useful for constructing absolute timeouts for
thread-sleep!, mutex-lock!, and thread-join!.
kaappi> (define later (seconds->time (+ (time->seconds (current-time)) 5)))
kaappi> (time? later)
;=> #t
See also: time->seconds,
thread-sleep!
Exception Predicates¶
join-timeout-exception?¶
Syntax: (join-timeout-exception? obj)
Returns #t if obj is a join-timeout exception, which is raised by
thread-join! when the timeout expires before the thread terminates.
kaappi> (define t (make-thread (lambda () (thread-sleep! 100))))
kaappi> (thread-start! t)
;=> #<thread>
kaappi> (guard (e ((join-timeout-exception? e) 'timed-out))
(thread-join! t (seconds->time (+ (time->seconds (current-time)) 0.1))))
;=> timed-out
See also: thread-join!
abandoned-mutex-exception?¶
Syntax: (abandoned-mutex-exception? obj)
Returns #t if obj is an abandoned-mutex exception, which is raised
when attempting to lock a mutex that was left locked by a thread that
has since been terminated.
See also: mutex-lock!,
thread-terminate!
terminated-thread-exception?¶
Syntax: (terminated-thread-exception? obj)
Returns #t if obj is a terminated-thread exception, which is raised
by thread-join! when the joined thread was terminated with
thread-terminate!.
See also: thread-join!,
thread-terminate!
uncaught-exception?¶
Syntax: (uncaught-exception? obj)
Returns #t if obj is an uncaught-exception object, which wraps the
original exception raised by a thread that terminated abnormally. Use
uncaught-exception-reason to extract the original exception.
kaappi> (define t (make-thread (lambda () (error "oops"))))
kaappi> (thread-start! t)
;=> #<thread>
kaappi> (guard (e ((uncaught-exception? e)
(error-object-message (uncaught-exception-reason e))))
(thread-join! t))
;=> "oops"
See also: uncaught-exception-reason,
thread-join!
uncaught-exception-reason¶
Syntax: (uncaught-exception-reason exception)
Returns the original exception object that caused the thread to terminate
abnormally. The exception must be an uncaught-exception object (as
tested by uncaught-exception?).
kaappi> (define t (make-thread (lambda () (error "something broke" 42))))
kaappi> (thread-start! t)
;=> #<thread>
kaappi> (guard (e ((uncaught-exception? e)
(let ((reason (uncaught-exception-reason e)))
(list (error-object-message reason)
(error-object-irritants reason)))))
(thread-join! t))
;=> ("something broke" (42))
See also: uncaught-exception?,
error-object-message