Previous: Type Conversions Up: Synchronization Next: Memory Management
Synchronization for threads of control created with the spawn command must be explicitly programmed. This can be done using single-assignment variables (in conjunction with pointers or C++ reference arguments). For example, a barrier between a spawning thread of control and the function that it spawns might be programmed as follows:
void independent (sync int* b) { ... *b = 1; }int main() { sync int Barrier; spawn independent(&Barrier); ... if (Barrier == 1) //spawning thread waits here until //function independent() has set sync value {;} ... }
It is important to recall that the semantics of the spawn statement requires that all the function arguments be evaluated prior to the function beginning execution. This means that if one of the arguments is a single-assignment variable, the spawned function will not begin execution until that variable has been defined. Consider the following code:
void f (sync int* p, sync int n) { *p = n; }int main() { sync int A[3], B[3]; A[2] = 1; B[2] = 1; par { f(&A[0],A[1]); //OK f(&A[1],A[2]); } spawn f(&B[0],B[1]); //program suspends here forever spawn f(&B[1],B[2]); ... }
The parallel block executes correctly because initialization (e.g. argument evaluation) and execution of both instances of the function f() are composed in parallel with each other. The spawn statements following this parallel block, however, must be executed in strict sequential order. Completing the first spawn statement means evaluating the arguments to the function f() -- that is, the address of B[0] and the value of B[1]. Because B[1] is an undefined single-assignment variable, the spawn statement itself suspends. Execution does not proceed to the next statement.
The sharing of references and pointers to single-assignment
variables by concurrent threads of execution can be extremely
useful. By contrast, the sharing of references and pointers to mutables
by concurrent threads of execution can be dangerous.
Because the spawned function is
composed in parallel with the spawning function,
pass-by-reference semantics for mutable variables can lead
to dangerous sharing between concurrent threads of control
(see Chapter ,
Section
).