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