1Perl6::Bible::S17(3)  User Contributed Perl Documentation Perl6::Bible::S17(3)
2
3
4

NAME

6       Synopsis_17 - Concurrency [DRAFT]
7

AUTHOR

9        Elizabeth Mattijsen <liz@dijkmat.nl>
10        Audrey Tang <autrijus@autrijus.org>
11

VERSION

13        Maintainer: Elizabeth Mattijsen <liz@dijkmat.nl>
14        Date: 13 Jun 2005
15        Last Modified: 13 Nov 2005
16        Number: 0
17        Version: 1
18

SKETCH

20       This is a rough sketch of how concurrency works in Perl 6.
21
22       (actually these are just random notes, put here under the release-early
23       release-often principle, slowly being integrated in a more textual
24       format.  Patches welcome!)
25

OVERVIEW

27       Concurrency can take many forms in Perl 6.  With varying degrees of
28       explicitness and control capabilities.  This document attempts to
29       describe what these capabilities are and in which form they can be
30       accessed in Perl 6.
31
32   Processes, threads, fibers?
33       Concurrency comes in many shapes and forms.  Most Perl users are used
34       to the concept of a "process" or a "thread" (usually depending on the
35       OS they work on).  Some systems even are familiar with very lightweight
36       threads called "fibers".
37
38       When discussing issues about concurrency with different people, it soon
39       becomes apparent that everybody has his own set of "understandings"
40       about what each word means, which doesn't make it any easier to
41       describe Perl 6 concurrency.
42
43       It seemed the most natural to use the word "thread" to describe a
44       process which has its own context, but also shares context with 0 or
45       more concurrently running processes.  Depending on your OS, or even
46       specific version of your OS, this could still be a single "process"
47       from the OS's point of view.  Or it could contain an OS process for
48       each thread.  Or any mixture of these two implementations.
49
50       In this document we try to be agnostic about this: all we know in Perl
51       6 are "threads", which have their own context and share context with
52       other concurrently running "threads".  Whether they be process, threads
53       or fibres at the OS level should not matter at the Perl 6 level.
54
55       And for sake of consistency, an unthreaded "normal" program is
56       considered to be also running in a single thread.
57
58   Variables
59       In the past, there have been two models for concurrent processes in
60       Perl.  In general, these are referred to as "5.005 threads" ("perldoc
61       perlothrtut") and "ithreads" ("perldoc perlthrtut").
62
63       The main difference between these two models from a programmer's point
64       of view, is that variables in "5.005 threads" are shared by default.
65       Whereas in the "ithreads" model, only variables that have been
66       indicated to be "shared", are actually shared between threads.  All
67       other variable values are actually copies of the variable's value in
68       the "parent" thread.
69
70       With regards to variables, the concurrency model of Perl 6 is closer to
71       the "5.005 threads" model than it is to the "ithreads" model.  In fact,
72       all variables "visible" to a particular scope in Perl 6 will be
73       accessible and modifiable from all of the concurrent processes that
74       start from that scope.  In that sense, one could consider the
75       "ithreads" model as a historical diversion: the Perl 6 concurrency
76       picks up where the "5.005 threads" path left off.
77
78       (EM: maybe point out that the "ithreads" behaviour can be simulated
79       with some kind of copy-on-write magic to be automagically added to all
80       variable access inside a thread, except for those with an explicit "is
81       shared" attribute?)
82
83   No user accessible locks
84       Differently from any current concurrent process implementation in Perl,
85       there are no user accessible locks.  Instead, the concept of Software
86       Transactionable Memory is used.  This is in concept similar to the use
87       of
88
89        BEGIN TRANSACTION
90        ... do your uninterruptible actions
91        COMMIT
92
93       in the database world.  More interestingly, this also includes the
94       concept of rollback:
95
96        BEGIN TRANSACTION
97        ... do your stuff, but impossible to complete: ROLLBACK
98
99       This causes the state of the process to be reverted to the state at the
100       moment the BEGIN TRANSACTION was executed.
101
102       Perl 6 supports this concept through Code blocks which are marked "is
103       atomic".  These sections are guaranteed to either be completed totally
104       (when the Code block is exited), or have their state reverted to the
105       state at the start of the Code block (with the retry statement).
106
107       (EM: maybe point out if / how old style locks can be "simulated", for
108       those needing a migration path?)
109
110   Atomic Code blocks
111           my ($x, $y);
112           sub c is atomic {
113               $x -= 3;
114               $y += 3;
115               if $x < 10 { retry }
116           };
117
118           $e = &c.retry_with( &d ); #
119           $e();
120
121           if $i { is atomic; ...  } else { ...; }
122
123       A Code block can be marked as "is atomic".  This means that code
124       executed inside that scope is guaranteed not to be interrupted in any
125       way.
126
127       The start of a block marked "is atomic" also becomes a "checkpoint" to
128       which execution can return (in exactly the same state) if a problem
129       occurs (a.k.a. a retry is done) inside the scope of the Code block.
130
131       retry
132
133       The "retry" function basically restores the state of the thread at the
134       last checkpoint and will wait there until an external event allows it
135       to potentially run that atomic section of code again without having to
136       retry again.
137
138       If there are no external events possible that could restart execution,
139       an exception will be raised.
140
141       The last checkpoint is either the last atomic / non-atomic boundary, or
142       the most immediate caller constructed with "retry_with".
143
144       retry_with
145
146       The "retry_with" method on an atomic Code object causes a checkpoint to
147       be made for "retry", creating an alternate execution path to be
148       followed when a "retry" is done.
149
150       limitations
151
152       Because Perl 6 must be able to revert its state to the state it had at
153       the checkpoint, it is not allowed to perform any non-revertable
154       actions.  These would include reading / writing from file handles that
155       do not support "seek" (such as sockets).  Attempting to do so will
156       cause a fatal error to occur.
157
158       If you're not interested in revertability, but are interested in
159       uninteruptability, you could use the "is critical" trait.
160
161   Critical Code blocks
162        sub tricky is critical {
163            # code accessing external info, not to be interrupted
164        }
165
166        if ($update) {
167            is critical;
168            # code accessing external info, not to be interrupted
169        }
170
171       A Code block marked "is critical" can not be interrupted in any way.
172       But since it is able to access non-revertible data structures (such as
173       non-seekable file handles), it cannot do a "retry" as it would be
174       impossible to restore the state to the beginning of the Code block.
175
176   Mixing Atomic and Critical
177       Both "atomic" as well as "critical" propagate down the call chain.
178       This means that any subroutine that in itself is not "atomic" or
179       "critical" becomes uninterruptible if called inside a code block that
180       is marked as "atomic" or "critical".
181
182       Atomic Code blocks called inside the call chain of a "critical" code
183       block do not pose a problem, as they are more restrictive.
184
185       Any code that attempts to perform any non-revertible action (e.g.
186       reading from a socket) will cause a fatal error when called inside the
187       call chain of an Atomic Code block.
188
189   Co-Routines
190       The execution of co-routine (or "coro" for short) could be considered
191       as a short "side-step" from the normal path of execution, much like the
192       normal calling of a subroutine.
193
194       The main difference with a normal subroutine, is that the co-routine
195       supports a special type of return, called "yield".
196
197       (EM: not sure whether the "threads->yield" causes so much mental
198       interference that we should use something else for "yield" in the coro
199       context.  And whether we should have a seperate "coro" keyword at all:
200       after all, the "yield" could be in a normal subroutine called from a
201       coro, so it's not like the compiler would be allowed to flag "yield" in
202       a sub as an error).
203
204       #######################################################################
205       Below here still the more or less unorganized stuff
206
207       CORE::GLOBAL::exit; # kills all the threads
208
209       # We intententionally do not list cross-machine parallelism Conc::
210       classes here.  # Consult your local 6PAN mirror with a time machine.
211       use Conc::Processes; # fork() or createProcess based implementation use
212       Conc::Threads;   # maybe it just exports &async to override the default
213       one, yay use Conc::Multiplex; # this is default
214
215       my $thr = async {
216           ...do something...
217           END { } };
218
219       Conc::Thread.this Conc::Proc.this
220
221       Conc object # name is still up for grabs!  - numify to TIDs (as in
222       pugs) - stringify to something sensible (eg. "<Conc:tid=5>"); -
223       enumerable with Conc.list - Conc.yield (if this is to live but
224       deprecated, maybe call it sleep(0)?)  - sleep() always respects other
225       threads, thank you very much - standard methods:
226           - .join    # wait for invocant to finish (always item cxt)
227           - .die     # throw exception in the invocant thread
228           - .alarm   # set up alarms
229           - .alarms  # query existing alarms
230           - .suspend # pause a thread; fail if already paused
231           - .resume  # revive a thread; fail if already running
232           - .detach  # survives parent thread demise (promoted to process)
233                      # process-local changes no longer affects parent
234                      # tentatively, the control methods still applies to it
235                      # including wait (which will always return undef)
236                      # also needs to discard any atomicity context -
237       attributes:
238           - .started  # time
239           - .finished # time
240           - .waiting  # suspened (not diff from block on wakeup signal)
241                       # waiting on a handle, a condition, a lock, et cetera
242                       # otherwise returns false for running threads
243                       # if it's finished then it's undef(?)
244           - .current_continuation
245                       # the CC currently running in that thread
246
247       - "is throttled" trait
248
249           method throttled::trait_auxillary:<is> ($limit=1, :$key=gensym()) {
250               # "is throttled" limits max connection to this Code object
251               # the throttling is shared among closures with the same key
252               # the limit may differ on closures with the same key.
253               # if the counter with the "key" equals or exceeds a closure's limit,
254               # the closure can't be entered until it's released
255               # (this can be trivially implmented using atomic+retry)
256           }
257
258           class Foo {
259               method a is throttled(:limit(3) :key<blah>) { ... }
260               method b is throttled(:limit(2) :key<blah>) { ... }
261           }
262           my Foo $f .= new;
263           async { $f.a }
264           async { $f.b }
265
266       - Thread::Status - IO objects and containers gets concurrency love!
267           - $obj.wake_on_readable
268           - $obj.wake_on_writable
269           - $obj.wake_on_either_readable_or_writable_or_passed_time(3); #
270       fixme fixme
271           - $obj.wake_on:{.readable} # busy wait, probably
272
273           my @a is Array::Chan = 1..Inf;
274           async { @a.push(1) };
275           async { @a.blocking_shift({ ... }) };
276           async { @a.unshift({ ... }) };
277
278       Communication abstractions - shared, transactional variables by default
279
280       # program will wait for _all_ threads # unjoined threads will be joined
281       at the beginning of the END block batch # of the parent thread that
282       spawned them
283
284       ### INTERFACE BARRIER ### module Blah; {
285
286           is atomic;   # retry/orelse/whatever other rollback stuff
287                        # limitation: no external IO (without lethal warnings anyway)
288                        # can't do anything irreversible
289
290           is critical; # free to do anything irreversible
291                        # means "don't interrupt me"
292                        # in system with critical section, no interrupts from
293                        # other threads will happen during execution
294                        # you can't suspend me
295
296           my $boo is export;
297           $boo = 1;
298
299           # We decree that this part forms the static interface
300           # it's run once during initial compilation under the
301           # Separate Compilation doctrine and the syms sealed off
302           # to form part fo bytecode syms headers
303           %CALLER::<&blah> = { 1 }; # work - adds to export set
304           die "Eureka!" if %CALLER::<$sym>; # never dies
305
306           # BEGIN { $boo = time };
307
308           sub IMPORT {
309               # VERY DYNAMIC!
310
311               our $i = time;
312               %CALLER::<&blah> = { 1 }; # work - adds to export set
313               die "Eureka!" if %CALLER::<$sym>; # probes interactively
314           }
315       }
316       ### INTERFACE BARRIER ###
317
318       my $sym; threads.new({
319           use Blah;
320           BEGIN { require(Blah).import }
321
322           my $boo; BEGIN { eval slurp<Blah.pm>; $boo := $Blah::boo };
323
324           ...
325       });
326
327   Signals
328       Asynchronous exceptions are just like user-initiated exceptions with
329       "die", so you can also catch it with regular "CATCH" blocks as
330       specified in S04.
331
332       To declare your main program catches INT signals, put a CATCH block
333       anywhere in the toplevel to handle exceptions like this:
334
335        CATCH {
336            when Error::Signal::INT { ... }
337        }
338
339   Alarm
340       An alarm is just a pre-arranged exception to be delivered to your
341       program.
342
343       By the time alarm has arrived, the current block may have already
344       finished executing, so you would need to set up CATCH blocks in places
345       where an alarm can rise to handle it properly.
346
347       You can request an alarm using the number of seconds, or with a target
348       date.  It returns a proxy alarm object that you can do interesting
349       things with.
350
351           multi Alarm *alarm (Num $seconds = $CALLER::_, &do = {die Sig::ALARM}, :$repeat = 1)
352           multi Alarm *alarm (Date $date, &do = {die Sig::ALARM}, :$repeat = 1)
353
354       Perl 6's "alarm" has three additional features over traditional alarms:
355
356       Multiple and Lexical Alarms
357
358       One can set up multiple alarms using repeated alarm calls:
359
360           {
361               my $a1 = alarm(2);
362               my $a2 = alarm(2);
363               sleep 10;
364               CATCH {
365                   is critical; # if you don't want $a2 to be raised inside this
366                   when Sig::ALARM { ... }
367               }
368           }
369
370       To stop an alarm, call "$alarm.stop".  The "alarms" method for Conc
371       objects (including process and threads) returns a list of alarms
372       currently scheduled for that concurrent context.
373
374       When an alarm object is garbage collected, the alarm is stopped
375       automatically.  Under void context, the implicit alarm object can only
376       be stopped by querying ".alarms" on the current process.
377
378       We are not sure what alarm(0) would mean.  Probably a deprecation
379       warning?
380
381       Repeated Alarms
382
383       If you request a repeated alarm using the "repeated" named argument, it
384       will attempt to fire off the alarm that many times.  However, the alarm
385       will be supressed when inside a "CATCH" block that's already handling
386       the exception raised by same alarm.
387
388       To repeat 0 times is to not fire off any alarms at all.  To repeat +Inf
389       times is to repeat over and over again.
390
391       Callbacks in Alarms
392
393       You can arrange a callback (like JavaScript's setTimeOut) in "alarm",
394       which will then be invoked with the then-current code as caller.
395
396       If you set up such a callback to another Conc object, what happens is
397       just like when you called ".die" on behalf of that object -- namely,
398       the callback closure, along with anything it referenced, is shared to
399       the target Conc context.
400
401       Unlike in Perl 5's ithreads where you cannot share anything after the
402       fact, this allows passing shared objects in an "ad-hoc" fashion across
403       concurrent parts of the program.  Under the default (multiplexing)
404       concurrency model, this is basically a no-op.
405
406   Continuations
407       Coroutines
408
409       ## braindump of coro meeting by Liz and Autri, more to follow
410
411       - Coros are _like_ processes
412
413       coro dbl { yield $_ * 2; yield $_; return }; my @x = 1..10; my %y = map
414       &dbl, @x; # 2 => 2, 6 => 4, 10 => 6, ...
415
416       coro perm (@x) {
417           @x.splice(rand(@x),1).yield while @x; }
418
419       my &p1 := &perm.start(1..10); my &p2 := &perm.start(1..20);
420
421       p1(); p1(); p2(); p2();
422
423       coro foo { yield 42 };
424
425       (1..10).pick;
426
427       coro foo ($x) {
428           yield $x;
429           yield $x+2;
430           cleanup();
431           while (2) {
432               while (1) {
433                   &?SUB.kill; # seppuku
434               }
435           } } # implicit falloff return + return() means startover without
436       yielding
437         # return() means yielding and restart + no implicit falloff (I LIKE
438       THIS)
439
440       &foo.finished; # true on return() and false on midway yield()
441
442       foo(4); # and that's all she wrote
443
444       coro foo ($x) {
445           yield $x;
446           # this point with $x bound to 10
447           yield $x+1;
448           return 5;
449           ... # this is never reached, I think we all agree }
450
451       # If you don't want your variables to get rebound, use "is copy": coro
452       foo ($x is copy) {...} # which is sugar for coro foo ($x) {
453         {
454           my $x := $OUTER::x;
455           ...;
456           # Further calls of &foo rebound $OUTER::x, not $x.
457         } }
458
459       sub foo {
460           return undef if rand;
461           ...  }
462
463       use overload {
464           '&{}' => sub { ... } }
465
466       class Coro is Conc::Multiplex does Code {
467           method postcircumfix:<( )> {
468               # start the thread, block stuff (we are in the caller's
469       context)
470           } }
471
472       class Hash is extended {
473           method postcircumfix:<( )> (&self: *@_) {
474               &self = ./start(@_);
475           }
476           method start {
477               # remember self
478               # upon return() or normal falloff, restore self
479           } }
480
481       %ENV(123);
482
483       &foo_continued := &foo.start(10); &foo.start(20);
484
485       foo(10);    # returns 10
486
487       foo();      # be "insufficient param" error or just return 11?
488       foo(20);    # returns 21
489
490       # continuation coros multi foo () { ...no rebinding... } multi foo ($x)
491       { ...rebinding... }
492
493       &foo.kill;
494
495       my $first_ret = zoro( type => <even> );
496       &zoro.variant(:type<even>).kill; &zoro.variant(type => 'even').kill;
497
498       zoro( type => <odd> );
499
500       zoro( even => 1 ); zoro( odd => 1 );
501
502       multi coro zoro ($type where 'even') {} multi coro zoro ($type where
503       'odd') {}
504
505       multi coro zoro ($even is named) {} multi coro zoro ($odd is named) {}
506
507       # iblech's thoughts: # Coroutine parameters should never be rebound.
508       Instead, yield(...)s return # value is an Arglist object containing the
509       new arguments: coro bar ($a, $b) {
510           ...;
511           my $new_set_of_args = yield(...);
512           my $sum_of_old_a_and_new_a = $a + $new_set_of_args<$a>;
513           ...; } bar(42, 23);  # $a is 42, $b is 23 bar(17, 19);  # $a still
514       42, $b still 19,
515                     # $new_set_of_args is \(a => 17, b => 19)
516
517   Junctive Autothreading and Hyper Operations
518       Live in userland for the time being.
519
520   Interprocess Communication
521   I/O Considerations
522       File Descriptors
523
524       Sockets
525
526
527
528perl v5.12.0                      2006-02-28              Perl6::Bible::S17(3)
Impressum