17. Bigloo The Programming Language (4.6a) User manual for version 4.6a January 2025 -- Threads

main page

top: | Bigloo The Programming Language (4.6a) User manual for version 4.6a January 2025

Threads

Chapters

Bigloo supports multithreaded programming. Two different libraries programming are available. The first one, the Fair Thread (see Section Fair Threads), enables, simple, easy to develop and to maintain code. The second one, the Posix Thread (see Section Posix Threads) enables more easily to take benefit of the actual parallelism that is now available on stock hardware. Because it is easier to program with `fthread` than with `pthread`, we strongly recommend to use the former as much as possible and leave the former for specially demanding applications. Both libraries are described in this chapter.

### 17.1 Thread Common Functions

Bigloo implements SRFI-18 (Multithreading support). This SRFI is available at http://srfi.schemers.org/srfi-18/srfi-18.html. As Bigloo's threads are objects (see Section Object System), the SRFI-18's thread specific functions can be used with either the `pthread` or the `fthread` library.
This section describes the functions that are available independently of the multi-threading library.

### 17.1.1 Thread API

Bigloo uses a set of primitive functions and methods to create, run and handle thread. For the sake of standardization the name and semantic of SRFI-18 has been used. This section presents only the mandatory functions to program with threads in Bigloo.
The most important difference with SRFI-18, is the missing of the function `make-thread`, which is not available for all libraries, as it can be hard to predict the type of thread which will be created if several thread libraries are used simultaneously. As threads are regular Bigloo objects, they can be created using the `instantiate` syntax. See the Fair Threads and Posix Threads specific sections for more details about thread creation and examples.
The examples given in this section use a generic syntax with `instantiate::thread`, to run the examples, you will have to put them in a function in a module (see Section Modules, and import one of the libraries using `library` module declaration.

current-thread | SRFI-18 function

Returns the current thread.

thread? `obj` | SRFI-18 function

Returns `#t` if `obj` is a thread, otherwise returns `#f`.

thread-name `thread` | SRFI-18 function

Returns the name of the `thread`.

thread-specific `thread` | SRFI-18 function

thread-specific-set! `thread` `obj` | SRFI-18 function

Returns and sets value in the specific field of the `thread`. If no value has been set, `thread-specific` returns an unspecified value.

    (let ((t (instantiate::thread
              (body (lambda ()
                       (print (thread-specific (current-thread))))))))
       (thread-specific-set! t 'foo)
       (thread-start! t)) -| foo

thread-cleanup `thread` | Bigloo function

thread-cleanup-set! `thread` `fun` | Bigloo function

Associates a cleanup function to a thread. The cleanup function is called with the thread itself. The cleanup function is executed in a context where `current-thread` is the thread owning the cleanup function.

    (let ((t (instantiate::thread (body (lambda () 'done) 'foo))))
       (thread-cleanup-set! t (lambda (v) (print (thread-name (current-thread))
    					     ", exit value: " v)))
       (thread-start! t)) -| foo, exit value: done

thread-name `thread` | Bigloo function

thread-name-set! `thread` `fun` | Bigloo function

Associates a name to a thread. If the platform does not support thread native name, the function is without effect.

    (let ((t (instantiate::thread (body (lambda () (print (thread-name (current-thread))))))))
       (thread-name-set! t ``my-thread'')
       (thread-start! t)) -| my-thread

thread-parameter `ident` | Bigloo function

thread-parameter-set! `ident` `value` | Bigloo function

Returns the value of the parameter `ident` in the current thread. If no value is bound to this parameter, `#f` is returned.
A thread parameter is implemented by a chunk of memory specific to each thread. All threads are created with an empty set of parameters.

The next functions have different behaviors depending in the library used, more details will be given in the specific sections below.

thread-start! `thread` [`args`] | SRFI-18 function

thread-start-joinable! `thread` | Bigloo function

thread-join! `thread` [`timeout`] | SRFI-18 function

thread-terminate! `thread` | SRFI-18 function

thread-kill! `thread` `signum` | SRFI-18 function

thread-yield! | SRFI-18 function

thread-sleep! `timeout` | SRFI-18 function

### 17.1.2 Mutexes

Thread locking mechanism is common to Fair Threads and Posix Threads.

mutex? `obj` | SRFI-18 function

make-mutex [`name`] | SRFI-18 function

make-spinlock [`name`] | SRFI-18 function

mutex-name `mutex` | SRFI-18 function

mutex-specific `mutex` | SRFI-18 function

mutex-specific-set! `mutex` `obj` | SRFI-18 function

mutex-state `mutex` | SRFI-18 function

mutex-lock! `mutex` [`timeout` [`thread`]] | SRFI-18 function, deprecated

mutex-unlock! `mutex` | SRFI-18 function, deprecated

The function `make-spinlock` creates a spin lock on architectures on support it, otherwise it creates a regular mutex as if `make-mutex` was called. The support for spin lock can be checked with:
`bigloo-config 'have-spinlock`

    (let ((m (make-mutex)))
       (thread-start!
        (instantiate::thread
           (body (lambda ()
                    (let loop ()
                       (if (mutex-lock! m 0)
                           (begin
                              (display "locked")
                              (mutex-unlock! m))
                           (begin
                              (thread-yield!)
                              (loop)))))))))
      -| locked

    (let ((res '()))
       (define (mutex-lock-recursively! mutex)
          (if (eq? (mutex-state mutex) (current-thread))
              (let ((n (mutex-specific mutex)))
                 (mutex-specific-set! mutex (+ n 1)))
              (begin
                 (mutex-lock! mutex)
                 (mutex-specific-set! mutex 0))))
       (define (mutex-unlock-recursively! mutex)
          (let ((n (mutex-specific mutex)))
             (if (= n 0)
                 (mutex-unlock! mutex)
                 (mutex-specific-set! mutex (- n 1)))))
       (thread-start!
        (instantiate::thread
           (body (lambda ()
                    (let ((m (make-mutex)))
                       (mutex-lock-recursively! m)
                       (mutex-lock-recursively! m)
                       (mutex-lock-recursively! m)
                       (set! res (cons (mutex-specific m) res))
                       (mutex-unlock-recursively! m)
                       (mutex-unlock-recursively! m)
                       (mutex-unlock-recursively! m)
                       (set! res (cons (mutex-specific m) res)))))))
       res)
      => (0 2)

synchronize mutex exp1 exp2 ... | Bigloo form

The function `synchronize` evaluates the expressions `exp1`, `exp2`, etc. The mutex `mutex` is acquired and released before `exp1` gets evaluated. Its value is the value of the evaluated expression. The form `synchronize` ensures that however the form returns, the mutex `mutex` is always unlocked.

    (synchronize mutex
       (print "before read...")
       (read p))

with-lock mutex thunk | Bigloo function, deprecated

The form `with-lock` is similar to `synchronize` into which it is expanded.
The function `with-lock` evaluates the body of the `thunk`. The mutex `mutex` is acquired and released before `thunk` gets invoked. The function `with-lock` might be implemented as:

    (define (with-lock mutex thunk)
       (synchronize mutex
          (thunk)))

### 17.1.3 Condition Variables

condition-variable? `obj` | SRFI-18 function

make-condition-variable [`name`] | SRFI-18 function

condition-variable-name `cv` | SRFI-18 function

condition-variable-specific `cv` | SRFI-18 function

condition-variable-specific-set! `cv` `obj` | SRFI-18 function

condition-variable-wait! `cv` `mutex` [timeout] | Bigloo function

condition-variable-signal! `cv` | SRFI-18 function

condition-variable-broadcast! `cv` | SRFI-18 function

    (let ((res 0))
       (define (make-semaphore n)
          (vector n (make-mutex) (make-condition-variable)))
       (define (semaphore-wait! sema)
          (mutex-lock! (vector-ref sema 1))
          (let ((n (vector-ref sema 0)))
             (if (> n 0)
                 (begin
                    (vector-set! sema 0 (- n 1))
                    (mutex-unlock! (vector-ref sema 1)))
                 (begin
                    (condition-variable-wait! (vector-ref sema 2) (vector-ref sema 1))
                    (mutex-unlock! (vector-ref sema 1))
                    (semaphore-wait! sema)))))
       (define (semaphore-signal-by! sema increment)
          (mutex-lock! (vector-ref sema 1))
          (let ((n (+ (vector-ref sema 0) increment)))
             (vector-set! sema 0 n)
             (if (> n 0)
                 (condition-variable-broadcast! (vector-ref sema 2)))
             (mutex-unlock! (vector-ref sema 1))))
       (let ((sema (make-semaphore 10)))
          (let ((t1 (thread-start!
                     (instantiate::thread
                        (body (lambda ()
                                 (semaphore-wait! sema)
                                 (set! res (current-time)))))))
                (t2 (thread-start!
                     (instantiate::thread
                        (body (lambda ()
                                 (let loop ((n 10))
                                    (if (> n 0)
                                        (begin
                                           (semaphore-signal-by! sema 1)
                                           (thread-yield!)
                                           (loop (- n 1)))))))))))
             (scheduler-start!)
             res)))
      => 2

### 17.2 Threads

Bigloo supports fair threads (see Section Thread), a specification of cooperative threads. In this framework a thread must explicitly or implicitly yield the processor to the scheduler (see Section Scheduler). Explicit cooperation is achieved by library functions such as `thread-yield!` or `thread-sleep!`. The scheduler does not preempt a running thread to allocate the processor to another waiting thread. Fair threads have two drawbacks over preemptive threads:

- Cooperative threads are not able to benefit of multi-processors platforms.
- Single threads programs must be adapted in order to be run concurrently.

On the other hand, Fair threads have advantages that make them suitable for a high level programming language such as Scheme:

- Fair threads have a strong and well defined semantic. Multi threaded programs using Fair threads are deterministic thus programs that deploy Fair threads are predictable.
- Fair threads are easier to program with because they hide most the of the concurrent programming pitfalls. In particular, since Fair threads enforce a strong synchronization, there is no need to deploy techniques such as mutex, semaphore or condition variables.

This whole chapter has been written in collaboration with F. Boussinot. It uses materials on Fair threads that can be found at http://www-sop.inria.fr/indes/rp/FairThreads/html/FairThreads.html.

### 17.2.1 Introduction to Fair Threads

Fair threads are cooperative threads run by a fair scheduler which gives them equal access to the processor. Fair threads can communicate using broadcast events. Their semantics does not depends on the executing platform. Fine control over fair threads execution is possible allowing the programming of specific user-defined scheduling strategies.
Contrary to standard sequential programming where the processor executes a single program, in concurrent programming the processor is a shared resource which is dispatched to several programs. The term concurrent is appropriate because programs can be seen as concurrently competing to gain access to the processor, in order to execute.
Threads are a basic means for concurrent programming, and are widely used in operating systems. At language level, threads offer a way to structure programs by decomposing systems in several concurrent components; in this respect, threads are useful for modularity.
However, threads are generally considered as low-level primitives leading to over-complex programming. Moreover, threads generally have loose semantics, in particular depending on the underlying executing platform; to give them a precise semantics is a difficult task, and this is a clearly identified problem to get portable code.
Bigloo proposes a new framework with clear and simple semantics, and with an efficient implementation. In it, threads are called fair; basically a fair thread is a cooperative thread executed in a context in which all threads always have equal access to the processor. Fair threads have a deterministic semantics, relying on previous work belonging to the so-called reactive approach.

### 17.2.2 Fair Threads Api

The Fair Thread library relies on the Posix Thread one, but you don't need to import the `pthread` library, as it is done automatically when importing the `fthread` one.
The functions listed in Thread Common Functions can be used to manipulates the Fair Thread, but `thread-start-joinable!`, as a fair thread can always join any other fair thread in the same scheduler.

#### 17.2.2.1 Thread

instantiate::fthread (body `thunk`) [(name `name`)] | Bigloo syntax

Returns a new thread which is not started yet. The body of the thread is the body of the procedure `thunk`. The optional argument `name` can be use to identify the thread. It can be any Bigloo value.

    (instantiate::fthread (body (lambda () (print 1) (thread-yield!)
    (print 2)))
                          (name 'my-thread))

The former thread-start function can be rewritten as follow:

    (define (make-thread body . name)
      (if (pair? name)
          (instantiate::fthread (body body) (name (car name)))
          (instantiate::fthread (body body))))

thread-start! `thread` [`scheduler`] | SRFI-18 function

Runs a thread created with `make-thread`. If `scheduler` is provided, the `thread` is started in this particular scheduler. Otherwise, it is started in the current scheduler (see Section Scheduler). Threads are started at the beginning of reactions (see Section Scheduler).

thread-yield! | SRFI-18 function

The current thread cooperates. That is, it is suspended for the reaction and the scheduler selects a new thread to be resumed. The scheduler resumes the next avaliable thread. If there is only one thread started in the scheduler, the same thread is resumed. A reaction corresponds to the invocation of a `scheduler-react!` call (see Section Scheduler).

thread-sleep! `timeout` | SRFI-18 function

The current thread cooperates during exactly `timeout` reactions (see Scheduler). It is suspended and the scheduler selects a new thread to be resumed. If there is only one thread started in the scheduler, the same thread will be resumed.

    (let ((t1 (instantiate::fthread
                 (body (lambda () (thread-sleep! 2) (display 'foo)))))
          (t2 (instantiate::fthread
                 (body (lambda () (let loop ((n 1))
                                     (display n)
                                     (thread-yield!)
                                     (if (< n 5)
                                     (loop (+ n 1)))))))))
       (thread-start! t1)
       (thread-start! t2)
       (scheduler-start!)) -| 12foo34

thread-terminate! `thread` | SRFI-18 function

Terminates `thread` at the end of the current reaction.

thread-join! `thread` [`timeout` [`timeout-val`]] | SRFI-18 function

The current thread waits until `thread` terminates or until `timeout` is reached (when supplied). If the `timeout` is reached, `thread-join!` returns `timeout-val`. If `thread` terminates, `thread-join!` returns the end-result of the `thread` or the end-exception if that thread terminates abnormally.
If several threads wait for the termination of the same thread, they are all notified of the termination during the current reaction.

    (let* ((t1 (thread-start!
                (instantiate::fthread
                   (body (lambda () (thread-sleep! 3) 'foo)))))
           (t2 (thread-start!
                (instantiate::fthread
                   (body (lambda () (print "t1: " (thread-join! t1 1)))))))
           (t3 (thread-start!
                (instantiate::fthread
                   (body (lambda () (print "t2: " (thread-join! t1 2 'bar)))))))
           (t3 (thread-start!
                (instantiate::fthread
                   (body (lambda () (print "t3: " (thread-join! t1)))))))
           (t4 (thread-start!
                (instantiate::fthread
                   (body (lambda () (print "t4: " (thread-join! t1))))))))
       (scheduler-start!))
       -| t1: #|%uncaught-exception [reason: (exception . join-timeout)]|
          t2: bar
          t3: foo
          t4: foo

`thread-join!` can be used to wait for a Posix Thread termination. The `pthread` object must be started with `thread-start-joinable!`.

thread-suspend! `thread` | Bigloo function

thread-resume! `thread` | Bigloo function

Suspends/resumes the `thread` at the end of reaction. While suspended a thread is not eligible to get the processor by the scheduler.

thread-await! `signal` [`timeout`] | Bigloo function

Blocks the thread until `signal` has been broadcast or until `timeout` has elapsed. The function `thread-await!` returns the value associated with the previous emissions of the signal that took place during the reaction.

    (let ((t1 (thread-start! (instantiate::fthread
                                (body (lambda ()
                                         (display (thread-await! 'foo))
                                         (display (thread-await! 'bar)))))))
          (t2 (thread-start! (instantiate::fthread
                                (body (lambda ()
                                         (broadcast! 'foo 'val1-foo)
                                         (broadcast! 'foo 'val2-foo))))))
          (t3 (thread-start! (instantiate::fthread
                                (body (lambda ()
                                         (thread-sleep! 2)
                                         (broadcast! 'bar 'val-bar)))))))
       (let loop ((n 1))
          (display n)
          (scheduler-react! (default-scheduler))
          (loop (+ n 1))))
      -| 1val2-foo23val-bar456...

The function `thread-await!` cannot be used to intercept all the signals broadcast during a reaction. This is illustrated by the following example where obviously `thread-await!` cannot intercept the emission of the signal:

    (thread-start! (instantiate::fthread (body (lambda ()
                                                  (thread-await! 'foo)
                                                  (broadcast! 'foo 1)))))
    (thread-start! (instantiate::fthread (body (lambda ()
                                                  (broadcast! 'foo 2)))))

thread-get-values! `signal` | Bigloo function

Terminates the instant for the thread (as `thread-yield!`) and returns, hence at the next instant, all the values associated with broadcast `signal` (see Section Signal) during the previous scheduler reaction (see Section Scheduler).
Example:

    (thread-start! (instantiate::fthread
                      (body (lambda ()
                               (for-each print (thread-get-values! 'foo))))))
    (thread-start! (instantiate::fthread
                      (body (lambda ()
                               (broadcast! 'foo 1)
                               (broadcast! 'foo 'foo)
                               (broadcast! 'foo "blabla")))))
       -| 1
          foo
          blabla

Example:

    (let ((t1 (thread-start!
               (instantiate::fthread
                  (body (lambda ()
                           (for-each print (thread-get-values! 'foo))))
                  (name 't1))))
          (t2 (thread-start!
               (instantiate::fthread
                  (body (lambda ()
                           (broadcast! 'foo (current-thread))
                           (thread-yield!)
                           ;; this second broadcast won't be intercepted
                           ;; because it occurs during the next reaction
                           (broadcast! 'foo (current-thread))))
                  (name 't2))))
          (t3 (thread-start!
               (instantiate::fthread
                  (body (lambda ()
                           (broadcast! 'foo (current-thread))
                           (broadcast! 'foo (current-thread))))
                  (name 't3)))))
       (scheduler-start!))
       -| #<thread:t2>
          #<thread:t3>
          #<thread:t3>

thread-await-values! `signal` [`timeout`] | Bigloo function

This blocks the current thread until `signal` has been broadcast. It then returns, at the next instant, all the values associated with all the broadcasts that took place during the instant. It can be defined as:

    (define (thread-await-values! signal . tmt)
       (apply thread-await! signal tmt)
       (thread-get-values signal))

thread-await*! `signals` [`timeout`] | Bigloo function

Wait for one of a list of signals. The function `thread-await*!` can be compared to the Unix `select` function. The argument `signals` is a list of signal identifier. The function `thread-await*!` blocks the current thread until one of the signal in the list `signals` is broadcast or until the optional numerical argument `timeout` is elapsed. If the thread unblocks because the timeout is elapsed, `thread-await*!` returns `#f`. Otherwise it returns two values that have to be collected with `multiple-value-bind` (see Control Features). The first one is the value of the broadcast signal. The second one is the broadcast signal.
Example:

    (let ((res #f))
       (thread-start!
        (instantiate::fthread
           (body (lambda ()
                    (let ((sig* (list 'foo 'bar)))
                       (multiple-value-bind (val1 sig1)
                          (thread-await*! sig*)
                          (multiple-value-bind (val2 sig2)
                             (thread-await*! sig*)
                             (thread-yield!)
                             (multiple-value-bind (val3 sig3)
                                (thread-await*! sig*)
                                (set! res (list sig1 sig2 sig3))))))))))
       (thread-start!
        (instantiate::fthread
           (body (lambda ()
                    (thread-sleep! 2)
                    (broadcast! 'foo 1)))))
       (thread-start!
        (instantiate::fthread
           (body (lambda ()
                    (thread-sleep! 3)
                    (broadcast! 'bar 2)))))
       (scheduler-start!)
       res)
      => '(foo foo bar)

A second example using timeouts:

    (let ((res #f))
       (thread-start!
        (instantiate::fthread
           (body (lambda ()
                    (let ((sig* (list 'foo 'bar)))
                       (multiple-value-bind (val1 sig1)
                          (thread-await*! sig* 1)
                          (thread-yield!)
                          (multiple-value-bind (val2 sig2)
                             (thread-await*! sig* 1)
                             (thread-yield!)
                             (multiple-value-bind (val3 sig3)
                                (thread-await*! sig* 2)
                                (set! res (list sig1 sig2 sig3))))))))))
       (thread-start!
        (instantiate::fthread
           (body (lambda ()
                    (thread-sleep! 2)
                    (broadcast! 'foo 1)))))
       (thread-start!
        (instantiate::fthread
           (body (lambda ()
                    (thread-sleep! 3)
                    (broadcast! 'bar 2)))))
       (scheduler-start!)
       res)
      => '(#f foo bar)

thread-get-values*! `signals` | Bigloo function

Terminates the instant for the thread (as `thread-yield!`) and returns, hence at the next instant, all the values associated with all broadcast `signals` (see Section Signal) during the previous scheduler reaction (see Section Scheduler). The function `thread-get-values*!` returns an alist made of the scanned signal and their values. That is the length of the returns list is the length of the list `signals`. If a signal of the list `signals` has not been broadcast, its associated entry the list returned by `thread-get-values*!` has an empty `cdr`.
Example:

    (let ((s1 'foo)
          (s2 'bar)
          (s3 'gee)
          (res #f))
       (thread-start!
        (instantiate::fthread
           (body (lambda ()
                    (thread-sleep! 2)
                    (broadcast! 'foo (current-time))
                    (broadcast! 'bar 0)))))
       (thread-start!
        (instantiate::fthread
           (body (lambda ()
                    (thread-await*! (list s1 s2 s3))
                    (set! res (thread-get-values*! (list s1 s2 s3)))))))
       (thread-start!
        (instantiate::fthread
           (body (lambda ()
                    (thread-sleep! 2)
                    (broadcast! 'bar (current-time))))))
       (scheduler-start!)
       res)
      => ((foo 3) (bar 3 0) (gee))

Used with asynchronous signal, the functions `thread-await*!` and `thread-get-values*!` can be used to read concurrently, in a non blocking way, several files.

thread-await-values*! `signals` [`timeout`] | Bigloo function

This blocks the current thread until at least one of `signals` has been broadcast. It then returns, at the next instant, all the values associated with all the broadcasts that took place during the instant. It can be defined as:

    (define (thread-await-values*! signal . tmt)
       (apply thread-await*! signal tmt)
       (thread-get-values*! signal))

#### 17.2.2.2 Scheduler

make-scheduler [`strict-order?`] [`envs`] | Bigloo function

Creates a new scheduler. The optional boolean argument `strict-order?` is used to ask the scheduler to always schedule the threads in the same order, it defaults to `#f`. The optional arguments `envs` are fair thread environments which will be defined in forthcoming Bigloo releases.

scheduler-strict-order? | Bigloo function

scheduler-strict-order?-set! `bool` | Bigloo function

Gets or sets the strict scheduling policy of the scheduler. If set, the threads will always be scheduled in the same order, until their termination. By default, it is set to false, which improve performances when there is a lot of thread to schedule.

scheduler? `obj` | Bigloo function

Returns `#t` if `obj` is a scheduler. Otherwise returns `#f`.

scheduler? `obj` | Bigloo function

Returns `#t` if `obj` is a scheduler. Otherwise returns `#f`.

current-scheduler | Bigloo function

Returns the current scheduler. The current scheduler is the scheduler which currently schedules the current thread. This value is not mutable, as it is set during the call to `thread-start!`.

default-scheduler [`scheduler`] | Bigloo function

Sets or gets the default scheduler. The default scheduler is the scheduler that will be used in the calls to `scheduler-react!`, `scheduler-start!` or `thread-start!` if not specified. It always exists a default scheduler. That is, it is optional for an application to create a scheduler.

scheduler-react! [`scheduler`] | Bigloo function

Executes all the threads started (see `thread-start!`, Section Thread) in the scheduler until all the threads are blocked. A thread is blocked if the has explicitly yield the processor (`thread-yield!` and `thread-sleep!`) or because it is waiting a signal (`thread-await!`). A thread can be selected several times during the same reaction. The function `scheduler-react!` returns a symbol denoting the state of the scheduler. The possible states are:

- `ready` The Scheduler is ready to execute some threads.
- `done` All the threads started in the scheduler have terminated.
- `await` All the threads started in the scheduler are waiting for a signal.

An invocation of `scheduler-react!` is called a reaction.

scheduler-start! [`arg` [`scheduler`]] | Bigloo function

Executes `scheduler-react!` as long as the scheduler is not done. If the optional argument `scheduler` is not provided, `scheduler-start!` uses the current scheduler (see `current-scheduler`). The optional `arg` can either be:

- An integer standing for the number of times `scheduler-react!` must be called.
- A procedure `f` of one argument. The procedure `f` is invoked after each reaction. It is passed a value `i` which is the iteration number of the scheduler. The reactions of the scheduler continue while `f` returns `#f`.

    (let* ((s (make-scheduler))
           (t (instantiate::fthread
                 (body (lambda ()
                          (let loop ((n 0))
                             (display n)
                             (thread-yield!)
                             (loop (+ 1 n))))))))
       (scheduler-start! 10 s))
      -| 0123456789

    (let* ((s (make-scheduler))
           (t (instantiate::fthread
                 (body (lambda ()
                          (let loop ((n 0))
                             (display n)
                             (thread-yield!)
                             (loop (+ 1 n))))))))
       (scheduler-start! (lambda (i) (read-char)) s))
      -| 0123456789

scheduler-terminate! [`scheduler`] | Bigloo function

Terminates all the threads in `scheduler`.

scheduler-instant [`scheduler`] | Bigloo function

Returns the current reaction number of `scheduler`. The reaction number is the number of times `scheduler-react!` has been invoked passing `scheduler` as argument.

#### 17.2.2.3 Signal

broadcast! `signal` [`val`] | Bigloo function

Broadcasts `signal` to all threads started in `scheduler` immediately, that is during the reaction. This function can only be called from within a running thread. If the optional argument `val` is omitted, the `signal` is broadcast with an unspecified value.

    (thread-start! (instantiate::fthread
                      (body (lambda ()
                               (thread-await! 'foo)
                               (print (scheduler-instant (current-scheduler)))))))
    (thread-start! (instantiate::fthread
                      (body (lambda ()
                               (broadcast! 'foo)))))
    (scheduler-start!)
      -| 1

scheduler-broadcast! `scheduler` `signal` [`val`] | Bigloo function

At the next react broadcasts `signal` to all threads started in `scheduler`. This is used to impact running threads from outside any threads. If the optional argument `val` is omitted, the `signal` is broadcast with an unspecified value.

make-asynchronous-signal `proc` | Bigloo function

This function invokes in the background, the procedure `proc`. This function takes one parameter which is the signal that is broadcast when the invocation returns. When the host operating system supports parallel executions, the invocation of `proc` is executed in parallel with the waiting thread.
Asynchronous signals can be used to implement non blocking system operations, such as input/output. Here is an example that illustrates how to implement concurrent programs that behaves similarly with Fair Threads and Posix Threads.

    (define-expander read
       (lambda (x e)
          (cond-expand
    	 (fthread
    	  (thread-await!
    	   (make-aynchronous-signal
    	    (lambda (s)
    	       (read ,@(map (lambda (x) (e x e)) (cdr x)))))))
    	 (else
    	  `(read ,@(map (lambda (x) (e x e)) (cdr x)))))))

### 17.2.3 SRFI-18

This section presents the functions that are not necessary to Bigloo but supported for compliance with SRFI-18, provided by the Fair Thread library.

current-time [`scheduler`] | SRFI-18 function

Returns the reaction number of `scheduler`.

time? `obj` | SRFI-18 function

time->seconds `obj` | SRFI-18 function

join-timeout-exception? `obj` | SRFI-18 function

abandoned-mutex-exception? `obj` | SRFI-18 function

terminated-thread-exception? `obj` | SRFI-18 function

uncaught-exception? `obj` | SRFI-18 function

uncaught-exception-reason `exc` | SRFI-18 function

### 17.3 Posix Threads

This section describes two Posix-Like multi-threading Bigloo libraries. The two libraries, `pthread`, and `srfi-18` are all the same but the `mutex-state` function that returns different results. Because of these differences that might seem thin at first glance, the `pthread` library is significantly faster than the `srfi-18` library. For that reason, it is recommended to use the `pthread` library instead of the `srfi-18` library that is mostly supported for backward compatibility.
As much as possible, the names exported by this library are compatible with the Fair Threads library (see Section Fair Threads).

### 17.3.1 Using Posix Threads

The Bigloo modules initialization model does not permit to create threads before the main function is started. In other words, it is unsafe to use the Posix Threads API at the top level of modules. On some particular applications this might work correctly. On other it could produce an error message stating the threads cannot be created or started before the pthread library is initialized.

### 17.3.2 Threads

instantiate::pthread (body `thunk`) [(name `name`)] | Bigloo syntax

make-thread `thunk` [`name`] | SRFI-18 function

Returns a new thread which is not started yet. The body of the thread is the body of the procedure `thunk`. The optional argument `name` can be use to identify the thread. It can be any Bigloo value.
Warning: the `make-thread` function is deprecated, but still provided for a backward compatibility with previous release of Bigloo. The use of this function is highly discouraged, in favor of the `instantiate::pthread` form.

    (module example
       (library pthread)
       (main main))

    (define (main argv)
       (make-thread
        (lambda ()
           (print 1)
           (thread-yield!)
           (print 2))
        'my-thread))

thread-start! `thread` | SRFI-18 function

thread-start-joinable! `thread` | SRFI-18 function

Runs a thread created with `instantiate::pthread`. By default, threads are detached, and thus, they cannot be joined.

thread-yield! | SRFI-18 function

The current thread cooperates.

thread-sleep! `timeout` | SRFI-18 function

The current thread sleeps for a certain period. It is suspended and the scheduler is free to select a new thread to be resumed. If there is only one thread started in the scheduler, the same thread will be resumed. The time of `timeout` is used to determine the time the thread must sleep.
Here are the possible types for `timeout`.

- `date`: the thread sleeps at least until the date `timeout`.
- `real`: the thread sleeps at least `timeout` seconds.
- `fixum`, `elong`, `llong`: the thread sleeps at least `timeout` milli-seconds.

thread-terminate! `thread` | SRFI-18 function

Terminates `thread` as soon as possible.

thread-join! `thread` [`timeout`] | SRFI-18 function

The current thread waits until the `thread` terminates. If `thread` terminates, `thread-join!` returns the end-result of the `thread` or the end-exception if that thread terminates abnormally.
It is possible to wait for the termination of the a thread if and only if it has been started with `thread-start-joinable!`. In particular, threads started with `thread-start!` cannot be joined.
The optional argument `timeout`, forces to wait at for `timeout` milli-seconds for the thread to terminate. Note that not all systems support this facility. When supported, the `cond-expand` (see see SRFIs) `pthread-timedjoin` is defined. When the timeout expires some systems, raise an error. Other systems abort silently.

terminated-thread-exception? `obj` | SRFI-18 function

uncaught-exception? `obj` | SRFI-18 function

uncaught-exception-reason `exc` | SRFI-18 function

### 17.3.3 Mutexes

Thread locking mechanism is common to Fair Threads and Posix Threads (see Thread Common Functions).

mutex-state mutex | SRFI-18 function

Returns the symbol `locked` when the mutex is locked by a thread. Otherwise, it returns the symbol `unlocked`.

### 17.3.4 Condition Variables

Posix thread condition variables follows the common thread API (see Thread Common Functions).

    (module example
      (library pthread)
      (main main))

    (define (main argv)
       (let ((res #f)
    	 (lock (make-mutex))
    	 (cv (make-condition-variable)))
          (thread-join!
           (thread-start-joinable!
    	(instantiate::pthread
    	   (body (lambda ()
    		    (mutex-lock! lock)
    		    (thread-start!
    		     (instantiate::pthread
    			(body (lambda ()
    				 (mutex-lock! lock)
    				 (condition-variable-signal! cv)
    				 (mutex-unlock! lock)))))
    		    (condition-variable-wait! cv lock)
    		    (set! res 23)
    		    (mutex-unlock! lock))))))
          res))

### 17.3.5 Semaphores

open-semaphore `name` [`create` `#t`] [`excl` `#t`] [`read` `#t`] [`write` `#t`] [`value` `1`] | bigloo function

close-semaphore `sem` | bigloo function

delete-semaphore `name` | bigloo function

Opens, closes, and deletes a named semaphore.

semaphore? `obj` | bigloo function

Returns `#t` if `obj` is a semaphore, returns `#f` otherwise.

semaphore-post `sem` | bigloo function

semaphore-wait `sem` | bigloo function

semaphore-trywait `sem` | bigloo function

Wait and post for a semaphore.

semaphore-value `sem` | bigloo function

Returns the current value of the semaphoe

### 17.3.6 SRFI-18

mutex-state mutex | SRFI-18 function

Returns information about the state of the mutex. The possible results are:

- thread T: the mutex is in the locked/owned state and thread T is the owner of the mutex
- symbol `not-owned`: the mutex is in the locked/not-owned state
- symbol `abandoned`: the mutex is in the unlocked/abandoned state
- symbol `not-abandoned`: the mutex is in the unlocked/not-abandoned state

Examples:

    (mutex-state (make-mutex))
       => not-abandoned

    (define (thread-alive? thread)
       (let ((mutex (make-mutex)))
          (mutex-lock! mutex #f thread)
          (let ((state (mutex-state mutex)))
             (mutex-unlock! mutex) ; avoid space leak
             (eq? state thread))))

### 17.4 Mixing Thread APIs

The Fair Threads library is ``Posix Threads'' safe, which means it is possible to use at the same time both libraries. In other words, it is possible to embed one fair scheduler into a Posix thread.
Here is a little example with two schedulers started into two different Posix threads, each schedulers running two fair threads.

    (module mix_threads
       (library fthread pthread)
       (main main))

    (define *f1* 0)
    (define *f2* 0)

    (define (main args)
       (let ((s1 (make-scheduler #t))
    	 (s2 (make-scheduler #t))

    	 (f1a (instantiate::fthread
    		 (body (lambda ()
    			  (let loop ()
    			     (print "f1a: " *f1* " " (current-thread))
    			     (set! *f1* (+ 1 *f1*))
    			     (thread-yield!)
    			     (loop))))))

    	 (f1b (instantiate::fthread
    		 (body (lambda ()
    			  (let loop ()
    			     (print "f1b: " *f1* " " (current-thread))
    			     (set! *f1* (+ 1 *f1*))
    			     (thread-yield!)
    			     (loop))))))

    	 (f2a (instantiate::fthread
    		 (body (lambda ()
    			  (let loop ()
    			     (print "f2a: " *f2* " " (current-thread))
    			     (set! *f2* (+ 1 *f2*))
    			     (thread-yield!)
    			     (loop))))))

    	 (f2b (instantiate::fthread
    		 (body (lambda ()
    			  (let loop ()
    			     (print "f2b: " *f2* " " (current-thread))
    			     (set! *f2* (+ 1 *f2*))
    			     (thread-yield!)
    			     (loop)))))))

          (let* ((p1 (instantiate::pthread
    		   (body (lambda ()
    			    ;; Sets the thread's specific scheduler
    			    (default-scheduler s1)
    			    (scheduler-start! 5)))))

    	    (p2 (instantiate::pthread
    		   (body (lambda ()
    			    ;; Sets the thread's specific scheduler
    			    (default-scheduler s2)
    			    ;; One reaction for s2
    			    (scheduler-react!)
    			    ;; Starts s1
    			    (thread-start-joinable! p1)
    			    ;; Do three reactions
    			    (scheduler-start! 3)
    			    ;; Waits for p1/s1 termination
    			    (thread-join! p1)
    			    ;; The final reaction
    			    (scheduler-react!))))))

    	 (thread-start! f1a s1)
    	 (thread-start! f1b s1)
    	 (thread-start! f2a s2)
    	 (thread-start! f2b s2)

    	 (thread-join! (thread-start-joinable! p2)))))

This Html page has been produced by Skribe.
Last update Mon Jan 27 07:55:49 2025.