11.1 Programming with Pthreads
SML# provides the Pthread structure that is a direct binding of the Pthread library in SML#. The pthread_create and pthread_join functions, which creates and join a thread, respectively, are provided as the following SML# functions:
Pthread.Thread.create : (unit -> int) -> Pthread.thread
Pthread.Thread.join : Pthread.thread -> int
The following is a simple example that computes fib 42 in a different thread.
# fun fib 0 = 0 | fib 1 = 1 | fib n = fib (n - 1) + fib (n - 2);
val fib = fn : int -> int
# val t = Pthread.Thread.create (fn _ => fib 42);
val t = ptr : Pthread.thread
# Pthread.Thread.join t;
val it = 267914296 : int
The SML# runtime system for direct C interface is carefully designed so that SML# functions can be passed to a C function as call-back functions and can be called back from a C function running in a thread that is different from the one that originally calls the C function. Using this features, the programmer can enjoy multi-thread programming simply by importing the Pthread library. To do this, we define the type
type pthread_t = unit ptr
for Pthread handles. ptr is a built-in type for C pointers; unit ptr corresponds to void* type in C.
The thread creation function, pthread_create, is then imported by the following declaration.
val pthread_create =
_import "pthread_create"
: (pthread_t ref, unit ptr, unit ptr -> unit ptr, unit ptr) -> int
Using this, we can write a function spawn that creates a thread as follows.
fun spawn f =
let
val r = ref (Pointer.NULL ())
in
pthread_create (r,
Pointer.NULL (),
fn _ => (f () : unit; Pointer.NULL ()),
Pointer.NULL ());
!r
end
Pointer.NULL () return s the NULL pointer in C.
Similarly, pthread_join is imported and used to define a function join that waits for the termination of a created thread.
val pthread_join =
_import "pthread_join"
: (pthread_t, unit ptr ref) -> int
fun join t =
(pthread_join (t, ref (Pointer.NULL ())); ())
Using these functions, we can write the same program as shown at the beginning of this section.
# fun fib 0 = 0 | fib 1 = 1 | fib n = fib (n - 1) + fib (n - 2);
val fib = fn : int -> int
# val r = ref 0;
val r = ref 0 : int ref
# fun g () = r := fib 42;
val g = unit -> unit
# val t = spawn g;
val t = ptr : unit ptr
# join t;
val it = () : unit
# !r;
val it = 267914296 : int
Note that it is dengerous to replace the line
# val t = spawn g;
with
# val t = spawn (fn () => r := fib 42);
because the garbage collector may collect the closure generated by the function expression before the function is called in another thread. See Section 29.2 for details.