The threads library

The threads library allows concurrent programming in Caml Special Light. It provides multiple threads of control (also called lightweight processes) that execute concurrently in the same memory space. Threads communicate by in-place modification of shared data structures, or by sending and receiving data on communication channels.

The threads library is implemented by time-sharing on a single processor. It will not take advantage of multi-processor machines. Using this library will therefore never make programs run faster. However, many programs are easier to write when structured as several communicating processes.

This library cannot be used in native-code programs compiled with cslopt; it can only be used with bytecode programs compiled with cslc.

Programs that use the threads library must be linked as follows:

        cslc -custom other options threads.cma other files -cclib -lthreads
Interactive use through a toplevel built with cslmktop is impractical, because the Caml Special Light toplevel itself is not thread-aware.

Module Thread: user-level lightweight threads

type t
The type of thread identifiers.

Thread creation and termination

val new : ('a -> 'b) -> 'a -> t
new funct arg creates a new thread of control, in which the function application funct arg is executed concurrently with the other threads of the program. The application of new returns the identifier of the newly created thread. The new thread terminates when the application funct arg returns, either normally or by raising an uncaught exception. In the latter case, the exception is printed on standard error, but not propagated back to the parent thread. Similarly, the result of the application funct arg is discarded and not directly accessible to the parent thread.
val self : unit -> t
Return the identifier of the calling thread.
val exit : unit -> unit
Terminate prematurely the calling thread.
val kill : t -> unit
Terminate prematurely the thread whose identifier is given.

Suspending threads

val delay: float -> unit
delay d suspends the execution of the calling thread for d seconds. The other program threads continue to run during this time.
val wait_inchan : in_channel -> unit
wait_inchan ic suspends the execution of the calling thread until at least one character is available for reading on the input channel ic. The other program threads continue to run during this time. In contrast, calling an input function directly on ic would block all threads in the program until data is available on the channel. See the module ThreadIO for higher-level input functions compatible with threads.
val wait_descr : Unix.file_descr -> unit
Similar to wait_inchan, but operates on a file descriptor from the Unix library instead of an input channel.
val join : t -> unit
join th suspends the execution of the calling thread until the thread th has terminated.

Low-level thread synchronization primitives

The following primitives provide the basis for implementing synchronization functions between threads. Their direct use is discouraged, as they are very low-level and prone to race conditions and deadlocks. The modules Mutex, Condition and Event provide higher-level synchronization primitives.
val critical_section: bool ref
Setting this reference to true deactivate thread preemption (the timer interrupt that transfers control from thread to thread), causing the current thread to run uninterrupted until critical_section is reset to false or the current thread explicitely relinquishes control using sleep, delay, wait_inchan or wait_descr.
val sleep : unit -> unit
Suspend the calling thread until another thread reactivates it using wakeup. Just before suspending the thread, critical_section is reset to false. Resetting critical_section and suspending the calling thread is an atomic operation.
val wakeup : t -> unit
Reactivate the thread whose identifier is given. This thread is assumed to be suspended on a call to sleep, delay, wait_inchan or wait_descr. After the call to wakeup, the suspended thread will resume execution at some future time. wakeup does nothing if the thread was not suspended.

Module Mutex: locks for mutual exclusion

Mutexes (mutual-exclusion locks) are used to implement critical sections and protect shared mutable data structures against concurrent accesses. The typical use is (if m is the mutex associated with the data structure D):
     Mutex.lock m;
     (* Critical section that operates over D *);
     Mutex.unlock m

type t
The type of mutexes.
val new: unit -> t
Return a new mutex.
val lock: t -> unit
Lock the given mutex. Only one thread can have the mutex locked at any time. A thread that attempts to lock a mutex already locked by another thread will suspend until the other thread unlocks the mutex.
val try_lock: t -> bool
Same as try_lock, but does not suspend the calling thread if the mutex is already locked: just return false immediately in that case. If the mutex is unlocked, lock it and return true.
val unlock: t -> unit
Unlock the given mutex. Other threads suspended trying to lock the mutex will restart.

Module Condition: condition variables to synchronize between threads

Condition variables are used when one thread wants to wait until another thread has finished doing something: the former thread ``waits'' on the condition variable, the latter thread ``signals'' the condition when it is done. Condition variables should always be protected by a mutex. The typical use is (if D is a shared data structure, m its mutex, and c is a condition variable):
     Mutex.lock m;
     while (* some predicate P over D is not satisfied *) do
       Condition.wait c m
     done;
     (* Modify D *)
     if (* the predicate P over D is now satified *) then Condition.signal c;
     Mutex.unlock m

type t
The type of condition variables.
val new: unit -> t
Return a new condition variable.
val wait: t -> Mutex.t -> unit
wait c m atomically unlocks the mutex m and suspends the calling process on the condition variable c. The process will restart after the condition variable c has been signalled. The mutex m is locked again before wait returns.
val signal: t -> unit
signal c restarts one of the processes waiting on the condition variable c.
val broadcast: t -> unit
broadcast c restarts all processes waiting on the condition variable c.

Module Event: first-class synchronous communication

This module implements synchronous interprocess communications over channels. As in John Reppy's Concurrent ML system, the communication events are first-class values: they can be built and combined independently before being offered for communication.
type 'a channel
The type of communication channels carrying values of type 'a.
val new_channel: unit -> 'a channel
Return a new channel.
type 'a event
The type of communication events returning a result of type 'a.
val send: 'a channel -> 'a -> unit event
send ch v returns the event consisting in sending the value v over the channel ch. The result value of this event is ().
val receive: 'a channel -> 'a event
receive ch returns the event consisting in receiving a value from the channel ch. The result value of this event is the value received.
val choose: 'a event list -> 'a event
choose evl returns the event that is the alternative of all the events in the list evl.
val wrap: 'a event -> ('a -> 'b) -> 'b event
wrap ev fn returns the event that performs the same communications as ev, then applies the post-processing function fn on the return value.
val guard: (unit -> 'a event) -> 'a event
guard fn returns the event that, when synchronized, computes fn() and behaves as the resulting event. This allows to compute events with side-effects at the time of the synchronization operation.
val sync: 'a event -> 'a
``Synchronize'' on an event: offer all the communication possibilities specified in the event to the outside world, and block until one of the communications succeed. The result value of that communication is returned.
val select: 'a event list -> 'a
``Synchronize'' on an alternative of events. select evl is shorthand for sync(choose el).
val poll: 'a event -> 'a option
Non-blocking version of sync: offer all the communication possibilities specified in the event to the outside world, and if one can take place immediately, perform it and return Some r where r is the result value of that communication. Otherwise, return None without blocking.