To see what is currently happening visit http://www.perl6.org/
Thread Programming Model
Maintainer: Steven McDougall <swmcd@world.std.com> Date: 31 Aug 2000 Last Modified: 28 Sep 2000 Version: 4 Mailing List: perl6-language-flow@perl.org Number: 185 Status: Developing Frozen since: v3 Unfrozen since: v4
This RFC describes the programming interface to Perl6 threads. It documents the function calls, operators, classes, methods, or whatever else the language provides for programming with threads.
try for lock in boolean contextEvent for condition variableswait_any and wait_allSemaphore and QueueFrozen
asynclock, try, and unlockSemaphore, Event, and Timer down into Thread::Queue use Thread;
$sub = sub { ... };
$thread = new Thread \&func , @args;
$thread = new Thread $sub , @args;
$thread = new Thread sub { ... }, @args;
async { ... };
$result = join $thread;
$thread = this Thread;
@threads = all Thread;
$thread1 == $thread2 and ...
Thread::yield();
critical { ... }; # one thread at a time in this block
# blocking
lock $scalar;
lock @array
lock %hash;
lock ⊂
# non-blocking
$ok = lock $scalar;
$ok = lock @array
$ok = lock %hash;
$ok = lock ⊂
unlock $scalar;
unlock @array
unlock %hash;
unlock ⊂
cond_wait $mutex;
cond_signal $mutex;
cond_broadcast $mutex;
Thread::delay($seconds);
Thread::alarm($time);
new Thread \&func, @argsExecutes func(@args) in a separate thread. The return value is
a reference to the Thread object that manages the thread.
The subroutine executes in its enclosing lexical context. This means that lexical variables declared in that context may be shared between threads. See RFC 178 for examples.
new Thread $sub, @argsnew Thread sub { ... }, @argsExecutes an anonymous subroutine in a separate thread, passing it
@args. The return value is a reference to the Thread object that
manages the thread.
The subroutine is a closure. References to variables in its lexical
context are bound when the sub operator executes. See RFC 178 for
examples.
async BLOCKExecutes BLOCK in a separate thread. Syntactically, async BLOCK
works like do BLOCK. async creates a Thread object to manage
the thread, but it does not return a reference to it. If you want the
Thread object, use one of the new Thread forms shown above.
The BLOCK executes in its enclosing lexical context. This means that lexical variables declared in that context may be shared between threads.
this ThreadReturns a reference to the Thread object that manages the current
thread.
all ThreadReturns a list of references to all existing Thread objects in the
program. This includes Thread objects created for async blocks.
join $threadjoin $threadBlocks until $thread terminates. May be called repeatedly, by any number of threads.
Returns the last expression evaluated in $thread. This expression is evaluated in list context inside the thread.
If join is called in list context, it returns the entire list; if
join is called in scalar context, it returns the first element of
the list.
Evaluates to true iff $thread1 and $thread2 reference the same
Thread object.
Thread::yield()Gives the interpreter an opportunity to switch to another thread. The interpreter is not obligated to take this opportunity, and the calling thread may regain control after an arbitrarily short period of time.
critical is a new keyword. Syntactically, it works like do.
critical { ... };
The interpreter guarantees that only one thread at a time can execute
a critical block.
lock applies a lock to a variable.
In void context, it blocks until it acquires the lock.
In non-void context, it does not block, and returns true or
false according as the thread does or does not acquire the lock.
lock $scalarlock @arraylock %hashlock &subApplies a lock to a variable.
If there are no locks applied to the variable, applies a lock and returns immediately. If there are locks applied by another thread, blocks until there are no locks applied. If there are locks applied by the calling thread, applies another lock and returns immediately.
The lock is automatically removed at the end of the lexical scope in
which the lock operator executes.
lock $scalarlock @arraylock %hashlock &subTries to apply a lock to a variable.
If there are no locks applied to the variable, applies a lock and returns true. If there are locks applied by another thread, returns false. If there are locks applied by the calling thread, applies another lock and returns true.
The lock is automatically removed at the end of the lexical scope in
which the lock operator executes.
unlock $scalarunlock @arrayunlock %hashunlock &subRemoves a lock from a variable.
If there are locks applied by the calling thread, removes one. If there are locks applied by another thread, does nothing. If there are no locks applied to the variable, does nothing.
unlock never blocks.
A consequence of these rules is that only one thread at a time may have locks applied to a variable.
cond_wait $mutex$mutex may be any variable.
$mutex must be locked before calling cond_wait. cond_wait
atomically unlocks $mutex and blocks waiting for a signal from a
cond_signal($mutex) or cond_broadcast($mutex) call.
When a signal from a cond_signal($mutex) or
cond_broadcast($mutex) call is received, cond_wait atomically
unblocks the calling thread and reacquires a lock on $mutex.
cond_signal $mutexSends a signal to one (arbitrarily chosen) thread that is blocked in
a wait_cond($mutex) call.
cond_broadcast $mutexSends a signal to all threads that are blocked in
wait_cond($mutex) calls.
This only documents the interface to the cond_xxx calls. Using
condition variables to synchronize threads requires additional code.
See, for example,
uw7doc.sco.com
Thread::delay $secondsBlocks for $seconds seconds. $seconds may be a floating point number, so this interface supports whatever time resolution the platform provides.
Thread::alarm $timeBlocks until $time seconds after the epoch. $time may be a floating point number, so this interface supports whatever time resolution the platform provides.
All of these features should be doable if threads are built into Perl.
This interface is an amalgam of
Thread.pm interface from Perl 5.6.0Here are some issues to consider
Threads are created by
new Thread \&func
new Thread sub { ... }
async { ... }
We arguably don't need three different ways to create threads. However, the different syntaxes fit into the language in slightly different ways, and I'm not sure which one I'd be willing to give up. The first is the most fundamental; losing it would be a serious inconvenience. Perl generally allows an anonymous subroutine where ever it allows a code ref, so the second also seems appropriate. And the third allows us to create threads with the kind of lightweight syntax that makes Perl such a lucid language.
joinThe calling context of join can't be propagated into the thread,
for several reasons.
join can be
called repeatedly in different contexts.join can
return the last expression evaluated in the thread, but it can't
retroactively affect the context in which that expression was evaluated.Not allowing multiple joins on a thread might help with the first
problem; I can't see any way around the second.
This interface provides the
critical { ... }
construct. In principle, we don't need this: you can do the same thing with scoped locks
sub foo
{
lock &foo;
...
}
sub bar
{
{
lock $bar::a;
...
}
{
lock $bar::b;
...
}
}
Nonetheless, critical sections have several attractive features.
Efficiency matters, because critical sections are used to manage things that are...well...critical. Important, global, high-contention resources like memory managers and process schedulers. Granted, these are poor examples for Perl, but you get the idea.
Whether to implement critical depends partly on whether serializing
execution of a block of code is common enough to merit its own keyword
and syntax. Threads.pm in Perl 5.6.0 documents a :locked attribute
for subroutines; given a choice, I'd rather have critical than
:locked.
Version 3 of this RFC proposed try to acquire a lock without
blocking. try does not block, and returns true or false
according as the thread does or does not acquire the lock.
It was pointed out that the keyword try will likely be taken for
exception handling. There are several ways we could avoid conflict
try_lock $mutex and ...
try down into the Thread:: package.Thread::try $mutex and ...
lock on its calling context.This RFC currently documents the last alternative.
I found out how to use condition variables. See, for example,
uw7doc.sco.com
You can build events out of condition variables, and a lot of other
things besides. I'm pretty sure you can build wait_any with
condition variables. It seems like you should be able to build
wait_all, but I haven't yet figured out how.
In any case, I'd rather have condition variables than a menagerie of other synchronization primitives.
I dropped the I/O section, because you can use condition variables to block on I/O in a controlled fashion
async { connect($sock, $addr); $connected=1; cond_signal($sock); }
async { Thread::delay(10); $timed_out=1; cond_signal($sock); }
async { <STDIN>; $canceled =1; cond_signal($sock); }
lock $sock;
cond_wait($sock);
$connected and ...
$timed_out and ...
$canceled and ...
You can build all these from mutexes and condition variables.
I dropped the wait functions from this interface. You can build
wait_any from condition variables. I'm not sure whether or not you
can build wait_all. However, a built-in wait_all function would
be limited to waiting on existing synchronization primitives. This
could make it somewhat rigid, and possibly of limited utility.
dieI dropped the $thread->eval call from this interface, and didn't
say what happens if a thread dies. There are several possibilities
joins it. This has a
certain logic to it, but it suffers from the fact that a program
needn't join its threads, so it doesn't guarantee that exceptions
will actually be handled.$@ on stderr and exits. This is what C++
does. It ensures that exceptions won't just disappear into the void;
however, it also causes a good deal of anxiety and paranoia, because
any thread can potentially blow your program out of the water. (I
speak from experience here.)$@ when a thread dies. Returning $@ to join is probably
the Wrong Thing.==I dropped $thread->equal in favor of overloading == to
compare threads. This seems more natural, and should be easy to
implement if threads are built into the language.
I dropped thread IDs from the interface. You don't want thread IDs. Thread IDs are an implementation artifact. Carrying around explicit numerical indices isn't the Perl way. They were broken anyway (wrap at 2^32, with no guarantee of uniqueness after that).
I dropped detach from the interface. Detach is an artifact of
languages that require programmers to manage their own storage. It has
rigorous semantics, there's no going back, and if you get it wrong,
you either leak threads or you crash.
In Perl, detachment is more a state of mind. We have threads, and we
have Thread objects to manage them. The thread holds a reference on
its Thread object until it terminates. The Thread object holds a
reference on its thread as long as the Thread object exists.
If there are no user-visible references to a Thread object (i.e.
the only reference on the Thread object is the one held by the
thread), then the thread is said to be detached. A call to
Thread->all or Thread->this could recover a reference to
the Thread object of a detached thread; when this happens, the
thread is no longer detached.
In any case, you don't have to worry about it. Like so many others,
detach is a problem that Perl doesn't have.
To minimize namespace pollution, we could @EXPORT_OK the functions that appear in this interface.
use Threads qw(yield delay alarm)
On the other hand, if they get moved into the core the issue may be moot.
There are two kinds of timers: relative and absolute. Obviously, you
can always build one kind out of the other, but I wanted to
distinguish them with different constructors. I named the constructors
delay and alarm, respectively. These are short, and read fairly
naturally.
this ThreadC++ partisans will get brain freeze reading code like
my $thread = this Thread;
but that's not why I traded in self for this. Really, it's not.
I did it because it reads more naturally to me.
RFC 1: Implementation of Threads in Perl
RFC 27: Coroutines for Perl
RFC 31: Subroutines: Co-routines
RFC 47: Universal Asynchronous I/O
RFC 178: Lightweight Threads
Threads.pm
PThreads info page