[% setvar title Thread Programming Model %]

This file is part of the Perl 6 Archive

Note: these documents may be out of date. Do not use as reference!

To see what is currently happening visit http://www.perl6.org/

TITLE

Thread Programming Model

VERSION

  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

ABSTRACT

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.

CHANGES

v4

v3

Frozen

v2

SYNOPSIS

  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 &sub;

  # non-blocking
  $ok = lock $scalar;
  $ok = lock @array
  $ok = lock %hash;
  $ok = lock &sub;
  
  unlock $scalar;
  unlock @array
  unlock %hash;
  unlock &sub;

  cond_wait      $mutex;
  cond_signal    $mutex;
  cond_broadcast $mutex;
  
  Thread::delay($seconds);
  Thread::alarm($time);
  

DESCRIPTION

Thread

Critical section

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

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.

A consequence of these rules is that only one thread at a time may have locks applied to a variable.

Condition variables

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

Timers

IMPLEMENTATION

All of these features should be doable if threads are built into Perl.

DISCUSSION

This interface is an amalgam of

Here are some issues to consider

Thread creation

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.

join

The calling context of join can't be propagated into the thread, for several reasons.

Not allowing multiple joins on a thread might help with the first problem; I can't see any way around the second.

Critical sections

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.

Locked variables

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

This RFC currently documents the last alternative.

Condition variables

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/O

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 ...

Event, Semaphore, and Queue

You can build all these from mutexes and condition variables.

Wait functions

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.

die

I dropped the $thread->eval call from this interface, and didn't say what happens if a thread dies. There are several possibilities

==

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.

Thread IDs

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).

Detach

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.

Import

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.

Timer

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 Thread

C++ 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.

REFERENCES

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

uw7doc.sco.com