Thread utilities

To use the bindings from this module:

(import :std/misc/threads)

primordial-thread-group

(primordial-thread-group) -> thread-group

Similar to current-thread-group, but always returns the primordial (main) thread's group instead.

Examples:

;; same in main thread
> (current-thread-group)
#<thread-group #87 primordial>
> (primordial-thread-group)
#<thread-group #87 primordial>

;; differs in other groups
> (import :gerbil/gambit/threads)
> (def (task)
    (displayln (current-thread-group))
    (displayln (primordial-thread-group)))
> (thread-join! (spawn/group 'new-group task))
#<thread-group #181 new-group>
#<thread-group #87 primordial>

thread-group->thread-list*

(thread-group->thread-list* tg) -> list

  tg := thread-group to transform

Similar to thread-group->thread-list, but also collects the threads in all subgroups of tg. Returns a flat list of threads.

Examples:

> (import :gerbil/gambit/threads)
> (let* ((g1 (make-thread-group 'G1))
         (g2 (make-thread-group 'G2 g1))
         (g3 (make-thread-group 'G3 g2))
         (t1 (make-thread (cut 1) 'T1 g1))
         (t2 (make-thread (cut 2) 'T2 g1))
         (t3 (make-thread (cut 3) 'T3 g2))
         (t4 (make-thread (cut 4) 'T4 g3)))
    (displayln (thread-group->thread-list  g1))
    (displayln (thread-group->thread-list  g2))
    (displayln (thread-group->thread-list  g3))
    (displayln (thread-group->thread-list* g1)))
(#<thread #214 T1> #<thread #215 T2>)
(#<thread #216 T3>)
(#<thread #217 T4>)
(#<thread #217 T4> #<thread #216 T3> #<thread #214 T1> #<thread #215 T2>)

all-threads

(all-threads) -> (list thread ...)

Same as (thread-group->thread-list* (primordial-thread-group)). Walks all thread-groups recursively and collects threads in a flat list.

Examples:

> (all-threads)
(#<thread #205 th1>
 #<thread #202 th5>
 #<thread #203 th4>
 #<thread #204 th3>
 #<thread #201 th2>    ; th2, ..., th5 in subgroups
 #<thread #1 primordial>)

;; non-recursive, only threads in specified top group:
> (import :gerbil/gambit/threads)
> (thread-group->thread-list (current-thread-group))
(#<thread #1 primordial> #<thread #205 th1>)

thread-dead?

(thread-dead? th) -> boolean

  th := thread to check

Returns #t if th is terminated, i.e., no longer able to run, #f otherwise.

Examples:

> (let (th (spawn thread-sleep! 2))
    (displayln (thread-dead? th))
    (thread-join! th)
    (displayln (thread-dead? th)))
#f
#t

> (thread-dead? (current-thread))
#f

thread-group-kill!

(thread-group-kill! tg) -> void | error

  tg := thread-group to kill

Kills all threads and subgroups in tg. In addition, it detaches the thread group from its parent, making it unreachable from the primordial thread-group structure and eligible for garbage collection. Signals an error when tg contains the current thread.

Note: A thread group that has been killed should not be used again to spawn threads in it.

Examples:

> (def (fib n)
    (cond ((< n 2) n)
          (else (+ (fib (- n 1))
                   (fib (- n 2))))))
> (let (g (make-thread-group 'new-group))
    (spawn-thread (cut fib 10) 'fib10 g)
    (spawn-thread (cut fib 30) 'fib30 g)
    (spawn-thread (cut fib 50) 'fib50 g)
    (spawn-thread (cut fib 70) 'fib70 g)
    (thread-sleep! 1)
    (displayln (all-threads))
    (thread-group-kill! g)
    (displayln (all-threads)))
;; thread fib10 already terminated:
(#<thread #231 fib70> #<thread #232 fib50> #<thread #233 fib30> #<thread #1 primordial>)
(#<thread #1 primordial>)

> (thread-group-kill! (current-thread-group))
error

thread-raise!

(thread-raise! th obj) -> void | error

  th  := thread to signal error in
  obj := exception object to raise

Interrupts th, which can be the primordial thread, and raises obj, terminating that very thread if not handled properly.

Examples:

> (import :gerbil/gambit/threads)
> (spawn thread-sleep! 10)
#<thread #238>
> (thread-dead? #238)
#f
> (thread-raise! #238 'failure)    ; #<thread #238> silently terminates
> (thread-dead? #238)
#t

> (thread-raise! (current-thread) 'failure)
*** ERROR IN (console)@1819.1 -- This object was raised: failure

thread-abort!

(thread-abort! th) -> void | error

  th := thread to abort

Same as (thread-raise! th +thread-abort+). +thread-abort+ is an internal exception object that has predefined support for type checking via thread-abort? and a display-exception method overload.

Examples:

> (import :gerbil/gambit/threads :std/sugar)
> (def (task)
    (try
      (let loop ()
        (displayln "working")
        (thread-sleep! 0.2)
        (loop))
      (catch (thread-abort? ex)
        (display-exception ex (current-error-port)))))
> (let (t (spawn task))
    (thread-sleep! 1)
    (thread-abort! t)
    (thread-join! t))
working
working
working
working
working
thread aborted

thread-abort?

(thread-abort? ex) -> boolean

  ex := exception to check

Returns #t if ex is a thread-abort exception type, #f otherwise, essentially checking whether the current thread was interrupted via thread-abort!.

Examples:

> (import :gerbil/gambit/threads :std/sugar)
> (try
    (thread-abort! (current-thread))
    (catch (thread-abort? ex)
      (display-exception ex (current-error-port))))
thread aborted    ; predefined exception message for thread-abort objects

thread-async!

(thread-async! th proc) -> any | void

  th   := thread to interrupt
  proc := thunk, procedure to execute

Interrupts th, which can be the primordial thread, and executes proc. Returns the result of proc, if th is the current thread, void otherwise.

Examples:

> (import :gerbil/gambit/threads)
> (def (task)
    (let loop ((i 0))
      (thread-sleep! 0.25)
      (when (<= i 5)
        (displayln "regular work: " i)
        (loop (1+ i)))))
> (let (th (spawn task))
    (thread-sleep! 1)
    (thread-async! th (cut displayln "async work: " (current-thread)))
    (thread-join! th))
regular work: 0
regular work: 1
regular work: 2
async work: #<thread #60>    ; non-primordial thread
regular work: 3
regular work: 4
regular work: 5

on-all-processors

(on-all-processors proc) -> (list thread ...)

  proc := thunk, procedure to execute on all CPU cores

Executes proc multiple times, once on each available processing unit (CPU core), and returns a list containing the created threads.

Note: Runtime support for multiple OS threads needs to be enabled in Gambit (see the installation instructions), otherwise only a single core is used.

Examples:

;; check processor support:
> (##current-vm-processor-count)
2

> (import :gerbil/gambit/threads)
> (let (threads (on-all-processors (lambda () 'OK)))
    (map thread-join! threads))
(OK OK)