1Perl6::Bible::S17(3) User Contributed Perl Documentation Perl6::Bible::S17(3)
2
3
4
6 Synopsis_17 - Concurrency [DRAFT]
7
9 Elizabeth Mattijsen <liz@dijkmat.nl>
10 Audrey Tang <autrijus@autrijus.org>
11
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
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
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)