|
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.
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-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-yield! | SRFI-18 function |
thread-sleep! timeout | SRFI-18 function |
|
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
|
|
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.
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)))))))
|
|
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 |
|
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.
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 |
|
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))
|
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
|
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))))
|
|
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)))))
|
|