ParaSail has had “structured concurrency” since its inception in 2009. A very recent article published in the new journal, describing ParaSail may be found here: http://programming-journal.org/2019/3/7/
The main ParaSail web page is at: http://parasail-lang.org where a link to a blog on the design of ParaSail may be found (parasail-programming-language on blogspot).
ParaSail supports automatic thread cancellation by returning or exiting from a concurrent loop or other concurrent control structure. This is discussed in the paper mentioned above, and is illustrated in examples that come with the ParaSail release (available at the parasail-lang web page), such as the breadth-first search operation in the Directed-Graph module (dgraph.psl), shown below. Note the “return” statement which cancels other threads participating in the parallel search, once a desired node is found:
func BFS(DGraph; Start : Node_Set; Is_Target : func (DGraph; Node_Id) -> Boolean) -> optional Node_Id is // Do a breadth-first search on graph for node that satisfies Is_Target. // Start is set of nodes to start from. // Return Node_Id for first node that satisfies Is_Target. // Return null if no such node. var Seen : Array<Atomic<Boolean>, Indexed_By => Node_Id> := Create(1..Length(DGraph.G), Initial_Value => Create(#false)); // NOTE: This is only an approximate breadth-first search. // It is possible for some parts of the search to get "ahead" // of other parts in terms of depth. A barrier would need // to be created if it was important that the result returned // represented the shallowest node that satisfied Is_Target. *Outer* for Next_Set => Start loop // Start with the initial set of roots for N in Next_Set concurrent loop // Look at each node in set if not Value(Seen[N]) then // Not yet seen; mark it to prevent re-checking this node. // NOTE: Race condition here, but we want to avoid overhead // of comp-and-swap, and race condition is "benign" Set_Value(Seen[N], #true); if Is_Target(DGraph, N) then // Found a node that satisfies Is_Target // This "return" will cancel other concurrent threads return N; end if; // Continue with successors of this node continue loop Outer with Next_Set => DGraph.G[N].Succs; end if; end loop; end loop Outer; // No node found return null; end func BFS;
Comments very welcome!