1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>The Thread structure and signature</TITLE>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link href="docstyle.css" rel="stylesheet" type="text/css">
</HEAD>
<BODY BGCOLOR="#ffffff">
<ul class="nav">
<li><a href="SingleAssignment.html">Previous</a></li>
<li><a href="Basis.html">Up</a></li>
<li><a href="Universal.html">Next</a></li>
</ul>
<H2><STRONG><font face="Arial, Helvetica, sans-serif">Thread structure</font></STRONG></H2>
<P>Earlier versions of Poly/ML have provided a form of concurrent execution through
the Process structure. Version 5.1 introduces
new thread primitives in the Thread structure. This structure is modelled on
the Posix thread (pthread) package but simplified and modified for ML. The aim
is to provide an efficient implementation of parallelism particularly to enable
ML programs to make use of multi-core processors while minimising the changes
needed to existing code. The Process structure will continue to be available
as a library written on top of these primitives but new programs should use
the Thread structure directly. </P>
<P>The thread package differs from pthreads in a number of ways.
There is no join function to wait for the completion of a thread.
This can be written using mutexes and condition variables.
Cancellation and signal handling are combined into the interrupt
functions. (The Poly/ML Signal structure handles signals for all the
threads together). The effect of explicit cancellation is achieved
using the interrupt function. This causes an interrupt to be
generated in a specific thread. Alternatively an interrupt can be
broadcast to all threads. This is most likely to be used
interactively to kill threads that appear to have gone out of
control. The normal top-level handler for a console interrupt will
generate this. Threads can choose how or whether they respond to
these interrupts. A thread that is doing processor-intensive work
probably needs to be able to be interrupted asynchronously whereas if
it is communicating with other threads the presence of asynchronous
interrupts makes correct programming difficult.</P>
<PRE><STRONG>signature</STRONG> THREAD =
<STRONG>sig</STRONG>
<STRONG>exception</STRONG> Thread <STRONG>of</STRONG> string
<STRONG>structure</STRONG> Thread:
<STRONG>sig</STRONG>
<STRONG>type</STRONG> thread;
<STRONG>datatype</STRONG> threadAttribute =
EnableBroadcastInterrupt <STRONG>of</STRONG> bool
| InterruptState <STRONG>of</STRONG> interruptState
| MaximumMLStack <STRONG>of</STRONG> int option (* Added in 5.5.3 *)
<STRONG>and</STRONG> interruptState =
InterruptDefer
| InterruptSynch
| InterruptAsynch
| InterruptAsynchOnce
<STRONG>val</STRONG> fork: (unit->unit) * threadAttribute list -> thread
<STRONG>val</STRONG> exit: unit -> unit
<B>val</B> isActive: thread -> bool
<STRONG>val</STRONG> equal: thread * thread -> bool
<STRONG>val</STRONG> self: unit -> thread
<STRONG>exception</STRONG> Interrupt
<STRONG>val</STRONG> interrupt: thread -> unit
<STRONG>val</STRONG> broadcastInterrupt: unit -> unit
<STRONG>val</STRONG> testInterrupt: unit -> unit
<B>val</B> kill: thread -> unit
<STRONG>val</STRONG> getLocal: 'a Universal.tags -> 'a option
<STRONG>val</STRONG> setLocal: 'a Universal.tag * 'a -> unit
<STRONG>val</STRONG> setAttributes: threadAttribute list -> unit
<STRONG>val</STRONG> getAttributes: unit -> threadAttribute list
<STRONG>val</STRONG> numProcessors: unit -> int
<STRONG>end</STRONG>
<STRONG>structure</STRONG> Mutex:
<STRONG>sig</STRONG>
<STRONG>type</STRONG> mutex
<STRONG>val</STRONG> mutex: unit -> mutex
<STRONG>val</STRONG> lock: mutex -> unit
<STRONG>val</STRONG> unlock: mutex -> unit
<STRONG>val</STRONG> trylock: mutex -> bool
<STRONG>end</STRONG>
<STRONG>structure</STRONG> ConditionVar:
<STRONG>sig</STRONG>
<STRONG>type</STRONG> conditionVar
<STRONG>val</STRONG> conditionVar: unit -> conditionVar
<STRONG>val</STRONG> wait: conditionVar * Mutex.mutex -> unit
<STRONG>val</STRONG> waitUntil: conditionVar * Mutex.mutex * Time.time -> bool
<STRONG>val</STRONG> signal: conditionVar -> unit
<STRONG>val</STRONG> broadcast: conditionVar -> unit
<STRONG>end</STRONG>
<STRONG>end</STRONG>;</PRE>
<H3>
The Thread substructure</H3>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>exception</STRONG> Thread <STRONG>of</STRONG> string</PRE><BLOCKQUOTE STYLE="margin-bottom: 0cm">
The Thread exception can be raised by various of the functions in the
structure if they detect an error.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>type</STRONG> thread</PRE><BLOCKQUOTE STYLE="margin-bottom: 0cm">
The type of a thread identifier.</BLOCKQUOTE>
<PRE><STRONG>datatype</STRONG> threadAttribute =
EnableBroadcastInterrupt <STRONG>of</STRONG> bool
| InterruptState <STRONG>of</STRONG> interruptState
| MaximumMLStack <STRONG>of</STRONG> int option
<STRONG>and</STRONG> interruptState =
InterruptDefer
| InterruptSynch
| InterruptAsynch
| InterruptAsynchOnce</PRE>
<BLOCKQUOTE>The type of a thread attribute. Thread attributes are
properties of the thread that are set initially when the thread is
created but can subsequently be modified by the thread itself. The
thread attribute type may be extended in the future to include things
like scheduling priority. The current thread attributes control the
way interrupt exceptions are delivered to the thread.</BLOCKQUOTE>
<BLOCKQUOTE STYLE="margin-bottom: 0cm">EnableBroadcastInterrupt
controls whether the thread will receive an interrupt sent using
broadcastInterrupt or as a result of pressing the console interrupt
key. If this is false the thread will not receive them. The default
for a new thread if this is not specified is false.</BLOCKQUOTE>
<BLOCKQUOTE STYLE="margin-bottom: 0cm">
<p>InterruptState controls when and whether interrupts are delivered to the
thread. This includes broadcast interrupts and also interrupts directed at
a specific thread with the interrupt call. InterruptDefer means the thread
will not receive any interrupts. However, if the thread has previously been
interrupted the interrupt may be delivered when the thread calls setAttributes
to change its interrupt state. InterruptSynch means interrupts are delivered
synchronously. An interrupt will be delayed until an interruption point. An
interruption point is one of: testInterrupt, ConditionVar.wait, ConditionVar.waitUntil
and various library calls that may block, such as IO calls, pause etc. N.B.
Mutex.lock is not an interruption point even though it can result in a thread
blocking for an indefinite period. InterruptAsynch means interrupts are delivered
asynchronously i.e. at a suitable point soon after they are triggered. InterruptAsynchOnce
means that only a single interrupt is delivered asynchronously after which
the interrupt state is changed to InterruptSynch. It allows a thread to tidy
up and if necessary indicate that it has been interrupted without the risk
of a second asynchronous interrupt occurring in the handler for the first
interrupt. If this attribute is not specified when a thread is created the
default is InterruptSynch.</p>
<p>MaximumMLStack was added in version 5.5.3. It controls the maximum size the
ML stack may grow to. It is an option type where NONE allows the stack to
grow to the limit of the available memory whereas SOME n limits the stack
to n words. This is approximate since there is some rounding involved. When
the limit is reached the thread is sent an Interrupt exception.</p>
</BLOCKQUOTE>
<PRE>
<STRONG>val</STRONG> fork: (unit->unit) * threadAttribute list -> thread</PRE>
<BLOCKQUOTE STYLE="margin-bottom: 0cm"> Fork a thread. Starts a new thread running
the function argument. The attribute list gives initial values for thread attributes
which can be modified by the thread itself. Any unspecified attributes take
default values. The thread is terminated when the thread function returns, if
it raises an uncaught exception or if it calls "exit".</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> exit: unit -> unit</PRE>
<BLOCKQUOTE STYLE="margin-bottom: 0cm"> Terminate this thread.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><B>val</B> isActive: thread -> bool</PRE>
<BLOCKQUOTE STYLE="margin-bottom: 0cm"> Test if a thread is still running or has
terminated.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> equal: thread * thread -> bool</PRE>
<BLOCKQUOTE STYLE="margin-bottom: 0cm"> Test whether thread values denote the
same thread.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> self: unit -> thread</PRE><BLOCKQUOTE>
Return the thread identifier for the current thread.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>exception</STRONG> Interrupt = SML90.Interrupt</PRE><BLOCKQUOTE>
The Interrupt exception can be generated in another thread either by
a broadcast to all threads or directed to a single thread.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> interrupt: thread -> unit</PRE><BLOCKQUOTE>
Send an Interrupt exception to a specific thread. When and indeed
whether the exception is actually delivered will depend on the
interrupt state of the target thread.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> broadcastInterrupt: unit -> unit</PRE><BLOCKQUOTE>
Send an interrupt exception to every thread which is set to accept
it.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> testInterrupt: unit -> unit</PRE><BLOCKQUOTE>
If this thread is handling interrupts synchronously, test to see if
it has been interrupted. If so it raises the Interrupt exception.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><B>val</B> kill: thread -> unit</PRE><BLOCKQUOTE>
Terminate a thread. This should be used as a last resort. Normally a
thread should be allowed to clean up and terminate by using the
interrupt function. Raises Thread if the thread is no longer running,
so an exception handler should be used unless the thread is known to
be blocked.</BLOCKQUOTE>
<PRE><STRONG>val</STRONG> getLocal: 'a Universal.tas -> 'a option
<STRONG>val</STRONG> setLocal: 'a Universal.tag * 'a -> unit</PRE>
<BLOCKQUOTE>
Get and set thread-local store for the calling thread. The store is a
tagged associative memory which is initially empty for a new thread.
A thread can call setLocal to add or replace items in its store and
call getLocal to return values if they exist. The Universal structure
contains functions to make new tags as well as injection, projection
and test functions.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> setAttributes: threadAttribute list -> unit</PRE><BLOCKQUOTE>
Thread attributes are initially set when the thread is forked but can
be changed by thread itself using this call. Unspecified attributes
remain unchanged.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> getAttributes: unit -> threadAttribute list</PRE>
<BLOCKQUOTE>Get the values of attributes for the current thread.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> numProcessors: unit -> int</PRE>
<BLOCKQUOTE> Return the number of processors configured on the machine.</BLOCKQUOTE>
<H3><font face="Arial, Helvetica, sans-serif">The Mutex substructure</font></H3>
<P STYLE="margin-bottom: 0cm">A mutex provides simple mutual exclusion. A thread
can lock a mutex and until it unlocks it no other thread will be able to lock
it. Locking and unlocking are intended to be fast in the situation when there
is no other process attempting to lock the mutex. Mutexes are non-recursive:
if a thread tries to lock a mutex that it has already locked it will deadlock.
Note: a thread should never attempt to lock or unlock a mutex if it may receive
an asynchronous interrupt. It should always set its interrupt state to either
InterruptDefer or InterruptSynch before calling these functions. An asynchronous
interrupt may leave the mutex in an indeterminate state.</P>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG><a name="mutex_type"></a>type</STRONG> mutex</PRE>
<BLOCKQUOTE>
The type of a mutex</BLOCKQUOTE>
<PRE><STRONG>val</STRONG> mutex: unit -> mutex</PRE>
<BLOCKQUOTE>Create a new mutex.</BLOCKQUOTE>
<STRONG>val</STRONG> lock: mutex -> unit</PRE>
<BLOCKQUOTE> Lock a mutex. If the mutex is currently locked the thread is blocked
until it is unlocked. If a thread tries to lock a mutex that it has previously
locked the thread will deadlock. N.B. "lock" is not an interruption
point (a point where synchronous interrupts are delivered) even though a thread
can be blocked indefinitely. If the thread attempting to lock the mutex is handling
interrupts asynchronously an asynchronous interrupt may be delivered before
or after the lock is taken.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> unlock: mutex -> unit</PRE><BLOCKQUOTE>
Unlock a mutex and allow any waiting threads to run. The behaviour if
the mutex was not previously locked by the calling thread is
undefined.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> trylock: mutex -> bool</PRE><BLOCKQUOTE>
Attempt to lock the mutex without blocking. Returns true if the mutex
was not previously locked and has now been locked by the calling
thread. Returns false if the mutex was previously locked, including
by the calling thread.</BLOCKQUOTE>
<H3><font face="Arial, Helvetica, sans-serif">The ConditionVar substructure</font></H3>
<P STYLE="margin-bottom: 0cm">Condition variables. Condition
variables are used to provide communication between threads. A
condition variable is used in conjunction with a mutex and usually a
reference to establish and test changes in state. The normal use is
for one thread to lock a mutex, test the reference and then wait on
the condition variable, releasing the lock on the mutex while it does
so. Another thread may then lock the mutex, update the reference,
unlock the mutex, and signal the condition variable. This wakes up
the first thread and reacquires the lock allowing the thread to test
the updated reference with the lock held. More complex communication
mechanisms, such as blocking channels, can be written in terms of
condition variables.
</P>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>type</STRONG> conditionVar</PRE><BLOCKQUOTE>
The type of a condition variable</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> conditionVar: unit -> conditionVar</PRE><BLOCKQUOTE>
Make a new condition variable.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG><a name="wait"></a>val</STRONG> wait: conditionVar * Mutex.mutex -> unit</PRE>
<BLOCKQUOTE>
<p>Release the mutex and block until the condition variable is signalled. When
wait returns the mutex will have been re-acquired.</p>
<p>If the thread is handling interrupts synchronously this function can be interrupted
using the "Thread.interrupt" function or, if the thread is set to
accept broadcast interrupts, "Thread.broadcastInterrupt". The thread
will re-acquire the mutex before the exception is delivered. An exception
will only be delivered in this case if the interrupt is sent before the condition
variable is signalled. If the interrupt is sent after the condition variable
is signalled the function will return normally even if it has not yet re-acquired
the mutex. The interrupt state will be delivered on the next call to "wait",
"Thread.testInterrupt" or other blocking call.</p>
<p>A thread should never call this function if it may receive an asynchronous
interrupt. It should always set its interrupt state to either InterruptSynch
or InterruptDefer beforehand. An asynchronous interrupt may leave the condition
variable and the mutex in an indeterminate state and could lead to deadlock.</p>
</BLOCKQUOTE>
<BLOCKQUOTE>
<p>A condition variable should only be associated with one mutex at a time.
All the threads waiting on a condition variable should pass the same mutex
as argument.</p>
</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG><a name="waitUntil"></a>val</STRONG> waitUntil: conditionVar * Mutex.mutex * Time.time -> bool</PRE>
<BLOCKQUOTE> As wait except that it blocks until either the condition variable
is signalled or the time (absolute) is reached. Either way the mutex is reacquired
so there may be a further delay if it is held by another thread. Returns true
if the condition variable had been signalled and false if the time had expired
before the variable was signalled.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> signal: conditionVar -> unit</PRE>
<BLOCKQUOTE> Wake up one thread if any are waiting on the condition variable.
If there are several threads waiting for the condition variable one will be
selected to run and will run as soon as it has re-acquired the lock.</BLOCKQUOTE>
<PRE STYLE="margin-bottom: 0.5cm"><STRONG>val</STRONG> broadcast: conditionVar -> unit</PRE><BLOCKQUOTE>
Wake up all threads waiting on the condition variable.</BLOCKQUOTE>
<BLOCKQUOTE STYLE="margin-left: 0cm"><BR><BR>
</BLOCKQUOTE>
<ul class="nav">
<li><a href="SingleAssignment.html">Previous</a></li>
<li><a href="Basis.html">Up</a></li>
<li><a href="Universal.html">Next</a></li>
</ul>
</BODY>
</HTML>
|