SML# Document Version 4.1.0
11 SML# feature: Multithread programming

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.