The GHC Commentary - The Multi-threaded runtime, and multiprocessor execution

This section of the commentary explains the structure of the runtime system when used in threaded or SMP mode.

The threaded version of the runtime supports bound threads and non-blocking foreign calls, and an overview of its design can be found in the paper Extending the Haskell Foreign Function Interface with Concurrency. To compile the runtime with threaded support, add the line

GhcRTSWays += thr
to mk/build.mk. When building C code in the runtime for the threaded way, the symbol THREADED_RTS is defined (this is arranged by the build system when building for way thr, see mk/config.mk). To build a Haskell program with the threaded runtime, pass the flag -threaded to GHC (this can be used in conjunction with -prof, and possibly -debug and others depending on which versions of the RTS have been built.

The SMP version runtime supports the same facilities as the threaded version, and in addition supports execution of Haskell code by multiple simultaneous OS threads. For SMP support, both the runtime and the libraries must be built a special way: add the lines

GhcRTSWays += thr
GhcLibWays += s
to mk/build.mk. To build Haskell code for SMP execution, use the flag -smp to GHC (this can be used in conjunction with -debug, but no other way-flags at this time). When building C code in the runtime for SMP support, the symbol SMP is defined (this is arranged by the compiler when the -smp flag is given, see ghc/compiler/main/StaticFlags.hs).

When building the runtime in either the threaded or SMP ways, the symbol RTS_SUPPORTS_THREADS will be defined (see Rts.h).

Overall design

The system is based around the notion of a Capability. A Capability is an object that represents both the permission to execute some Haskell code, and the state required to do so. In order to execute some Haskell code, a thread must therefore hold a Capability. The available pool of capabilities is managed by the Capability API, described below.

In the threaded runtime, there is only a single Capabililty in the system, indicating that only a single thread can be executing Haskell code at any one time. In the SMP runtime, there can be an arbitrary number of capabilities selectable at runtime with the +RTS -Nn flag; in practice the number is best chosen to be the same as the number of processors on the host machine.

There are a number of OS threads running code in the runtime. We call these tasks to avoid confusion with Haskell threads. Tasks are managed by the Task subsystem, which is mainly concerned with keeping track of statistics such as how much time each task spends executing Haskell code, and also keeping track of how many tasks are around when we want to shut down the runtime.

Some tasks are created by the runtime itself, and some may be here as a result of a call to Haskell from foreign code (we call this an in-call). The runtime can support any number of concurrent foreign in-calls, but the number of these calls that will actually run Haskell code in parallel is determined by the number of available capabilities. Each in-call creates a bound thread, as described in the FFI/Concurrency paper (cited above).

In the future we may want to bind a Capability to a particular processor, so that we can support a notion of affinity - avoiding accidental migration of work from one CPU to another, so that we can make best use of a CPU's local cache. For now, the design ignores this issue.

The OSThreads interface

This interface is merely an abstraction layer over the OS-specific APIs for managing threads. It has two main implementations: Win32 and POSIX.

This is the entirety of the interface:

/* Various abstract types */
typedef Mutex;
typedef Condition;
typedef OSThreadId;

extern OSThreadId osThreadId      ( void );
extern void shutdownThread        ( void );
extern void yieldThread           ( void );
extern int  createOSThread        ( OSThreadId* tid,
				    void (*startProc)(void) );

extern void initCondition         ( Condition* pCond );
extern void closeCondition        ( Condition* pCond );
extern rtsBool broadcastCondition ( Condition* pCond );
extern rtsBool signalCondition    ( Condition* pCond );
extern rtsBool waitCondition      ( Condition* pCond, 
				    Mutex* pMut );

extern void initMutex             ( Mutex* pMut );
    

The Task interface

The Capability interface

Multiprocessor Haskell Execution