Check out the new USENIX Web site. next up previous
Next: Planned Enhancements Up: Flow Of Control Features Previous: Threads

Flow Control Primitives

Some minor modifications to the Nickle language to help support threads, continuations, and exceptions have proven to be particularly convenient. Threads are created by a low-precedence fork operator: the semantic is that the expression to which the fork operator is applied will be evaluated in the new thread returned by the operator. Any thread can retrieve the result of this computation via the Thread::join() built-in function. The principal alternative to making the fork operator a syntactic part of the language was to make it a built-in function accepting a closure or continuation. This is much less convenient for the user.

Another important syntactic feature concerns the semantics of operations under locks or other temporary invariants. Java provides a finally clause of its try block, whose primary purpose is to ensure that the lock is restored. This choice has some unfortunate consequences. Consider typical Java code to execute a function under a lock of some sort, shown in Figure 4. First, this code is syntactically complicated. There are many blocks, and the flow of control is not obvious. Second, even though no catch clauses are present the try is necessary merely to obtain the finally clause. Third, the establishment of the lock is syntactically indistinguishable from the rest of the surrounding code, which means that the language can have no idea whether locks and unlocks are matched, and can provide no assistance with locking errors.

Figure 4: Locking in Java using finally.
\begin{figure}\begin{verbatim}if (get_lock(&l)) {
try {
locked_operation();...
...
}
} else {
throw new LockException(''failed'');
}\end{verbatim}\end{figure}

In order to remedy these deficiencies, Nickle separates the invariant-management functionality of try from the exception-handling functionality. The Nickle twixt statement is syntactically similar to a C for loop, but with two arguments instead of three, and an optional else clause. A natural reimplementation of Figure 4 using twixt is shown in Figure 5. With this syntax, the get_lock(l) and release_lock(l) operations are syntactically distinguished, and their correspondence is visually obvious. Equally obvious is the fact that the exception is raised as the result of the failure of get_lock(). Finally, the gratuitous try clause has been eliminated, and the nesting has been regularized.

Figure 5: Locking in Nickle using twixt.
\begin{figure}\begin{verbatim}twixt(get_lock(l); release_lock(l))
locked_operation();
else
raise lock_exception(''failed'');\end{verbatim}\end{figure}

As a final feature, consider the situation where a continuation is captured using Nickle's setjmp() inside locked_operation(). Because the get_lock(l) operation is known to be protecting the context in which the continuation is captured, Nickle will re-execute it (!) upon longjmp() back to the continuation. This would be difficult or impossible in most languages.


next up previous
Next: Planned Enhancements Up: Flow Of Control Features Previous: Threads
Bart Massey 2001-04-19