Skip to content

Commit 5027a94

Browse files
committed
Let's revert back to before I got into this
1 parent 9089b24 commit 5027a94

File tree

1 file changed

+70
-101
lines changed

1 file changed

+70
-101
lines changed

S17-concurrency.pod

Lines changed: 70 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -259,15 +259,13 @@ which returns an element from the C<PromiseStatus> enumeration.
259259
enum PromiseStatus (:Planned(0), :Kept(1), :Broken(2));
260260

261261
The result itself can be obtained by calling C<result>. If the C<Promise> was
262-
already kept, the result is immediately returned. If the C<Promise> was
263-
broken then the exception that it was broken with is thrown.
264-
265-
If the C<Promise> is not yet kept or broken, then the caller will block until
266-
any of these status changes happen.
262+
already kept, the result is immediately returned. If the C<Promise> was broken
263+
then the exception that it was broken with is thrown. If the C<Promise> is not
264+
yet kept or broken, then the caller will block until this happens.
267265

268266
A C<Promise> will boolify to whether the C<Promise> is already kept or broken.
269-
There is also an C<excuse> method for extracting the exception from a
270-
C<Broken> C<Promise> rather than having it thrown.
267+
There is also an C<excuse> method for extracting the exception from a C<Broken>
268+
C<Promise> rather than having it thrown.
271269

272270
if $promise {
273271
if $promise.status == Kept {
@@ -284,9 +282,9 @@ C<Broken> C<Promise> rather than having it thrown.
284282
You can also simply use a switch:
285283

286284
given $promise.status {
287-
when Planned { say "Still working!" }
288-
when Kept { say "Kept, result = ", $promise.result }
289-
when Broken { say "Broken because ", $promise.excuse }
285+
when Planned { say "Still working!" }
286+
when Kept { say "Kept, result = ", $promise.result }
287+
when Broken { say "Broken because ", $promise.excuse }
290288
}
291289

292290
There are various convenient "factory" methods on C<Promise>. The most common
@@ -354,13 +352,13 @@ a promise is user-facing. To instead represent the promise from the
354352
viewpoint of the promiser, the various built-in C<Promise> factory methods
355353
and combinators use C<Promise::Vow> objects to represent that internal
356354
resolve to fulfill the promise. ("I have vowed to keep my promise
357-
to you.") The C<vow> method on a C<Promise> returns an object with C<keep>,
358-
and C<break> methods. It can only be called once during a C<Promise>
359-
object's lifetime. Since C<keep> and C<break> on the C<Promise> itself just
360-
delegate to C<self.vow.keep(...)> or C<self.vow.break(...)>, obtaining the
361-
vow before letting the C<Promise> escape to the outside world is a way to
362-
take ownership of the right to keep or break it. For example, here is how
363-
the C<Promise.in> factory is implemented:
355+
to you.") The C<vow> method on a C<Promise> returns an object with C<keep>
356+
and C<break> methods. It can only be called once during a C<Promise> object's
357+
lifetime. Since C<keep> and C<break> on the C<Promise> itself just delegate
358+
to C<self.vow.keep(...)> or C<self.vow.break(...)>, obtaining the vow
359+
before letting the C<Promise> escape to the outside world is a way to take
360+
ownership of the right to keep or break it. For example, here is how the
361+
C<Promise.in> factory is implemented:
364362

365363
method in(Promise:U: $seconds, :$scheduler = $*SCHEDULER) {
366364
my $p = Promise.new(:$scheduler);
@@ -375,7 +373,45 @@ result.
375373
my ($a, $b) = await $p1, $p2;
376374

377375
This simply calls C<result> on each of the C<Promise>s, so any exception will
378-
be thrown.
376+
be thrown. There is also a C<winner> statement [keywords still negotiable]:
377+
378+
winner * {
379+
done $p1 { say "First promise got a value" }
380+
done $p2 { say "Second promise got a value" }
381+
}
382+
383+
That will invoke the closure associated with the first promise that
384+
produces a result, either kept or broken.
385+
386+
It's possible to add a timer using the keyword C<wait> followed
387+
by the number of seconds to wait (which may be fractional). As a
388+
degenerate case, in order to avoid blocking at all you may use a
389+
C<wait 0>. The timeout is always checked last, to guarantee that
390+
the other entries are all tried at least once before timing out.
391+
392+
my $gotone = winner * {
393+
done $p1 { say "First promise got a value"; $p1 }
394+
done $p2 { say "Second promise got a value"; $p2 }
395+
wait 0 { say "Not done yet"; Nil }
396+
}
397+
398+
The construct as a whole returns the result of whichever block was selected.
399+
400+
It's also possible to process a variadic list of promises together,
401+
using generic code that works over some set of the promises (use C<*>
402+
to represent any of them). The index and promise are passed to the
403+
code as named arguments C<$:v> and <$:k> (possibly via priming if
404+
the code is instantiated ahead of time).
405+
406+
winner * {
407+
done @promises { say "Promise $:k was kept, result was: ", $:v.result }
408+
}
409+
410+
In this case C<$:k> returns the index of the promise, base 0.
411+
Likewise C<$:v> returns the promise object itself. Conjecture: the result
412+
should be bound to C<$_> to make it work more like channels below.
413+
414+
[Conjecture: we should allow different cases for success or failure.]
379415

380416
=head1 Channels
381417

@@ -411,6 +447,22 @@ calls to a channel return the same promise, not a new one.
411447
While C<receive> blocks until it can read, C<poll> takes a message from the
412448
channel if one is there or immediately returns C<Nil> if nothing is there.
413449

450+
The C<winner> construct also works on channels, and will try to receive a value
451+
from the first C<Channel> that has one available. It also automatically checks
452+
the C<.done> promise corresponding to the channel, so it can also be used in order
453+
to write a loop to receive from a channel until it is closed:
454+
455+
gather loop {
456+
winner $channel {
457+
more * { take $_ }
458+
done * { last }
459+
}
460+
}
461+
462+
This works because C<more> only ever works on channels, while C<done>
463+
only ever works on promises, so it knows to check the promise of
464+
channel C<$c> rather than C<$c> itself.
465+
414466
This is such a common pattern that we make a channel in list context behave
415467
that way:
416468

@@ -421,89 +473,6 @@ that way:
421473
the reactive realm to the lazy realm. Some reasonable amount of buffering
422474
is assumed between the two.)
423475

424-
=head1 Outcome
425-
426-
There is an C<outcome> statement [keywords still negotiable]. In its
427-
simplest form it just takes a list of C<Promise>s and / or C<Channel>s and
428-
returns a lazy list of all the values returned by all Promises and Channels
429-
specified:
430-
431-
my @values = outcome @p, @c;
432-
433-
If a C<Promise> is broken, then the value returned is a C<Failure> object
434-
with the C<.excuse>. Please note that this simple form will block until all
435-
values have been found. You can also put this simple form in a loop for
436-
processing:
437-
438-
for outcome( @p, @c ) -> $value {
439-
# process $value
440-
}
441-
442-
in which case values will be processed as they become available.
443-
444-
For more control, one can also specify a closure, in which there is more
445-
control over how a C<Promise> and / or a C<Channel> are being processed.
446-
In such a case, the returned lazy list with values is generally not needed:
447-
448-
my @values;
449-
outcome {
450-
whenever $p1 { say "p1 fired"; @values.push: $_ }
451-
whenever @p,@c { @values.push: $_ }
452-
}
453-
454-
Please note that for C<Promise>s, the C<whenever> will only be called once.
455-
For C<Channel>s, it will be called as soon as a value is present on the
456-
Channel until the Channel is C<.closed>.
457-
458-
There is also a C<done> keyword that can be used to specify code to be run
459-
when a C<Channel> has closed:
460-
461-
outcome {
462-
...
463-
done @c { say "Channel $_ has closed" }
464-
}
465-
466-
It's possible to add a timer using the keyword C<wait> followed by the
467-
number of seconds to wait (which may be fractional) since the last value
468-
found by a C<whenever> has been seen. The timeout is always checked last,
469-
to guarantee that the other entries are all tried at least once before
470-
timing out.
471-
472-
outcome {
473-
...
474-
wait 5 { say "Not done yet" }
475-
}
476-
477-
As a degenerate case, in order to avoid blocking at all you may use a
478-
C<wait 0>. Together with C<last>, this allows one to exit the C<outcome>
479-
prematurely whenever there is nothing happening:
480-
481-
loop {
482-
outcome {
483-
...
484-
wait 0 { say "Giving up for now"; last }
485-
}
486-
# do other stuff
487-
}
488-
489-
Please note that you will to somehow make sure that C<Promise> that has been
490-
kept or broken, will not be seen again in the next loop iteration. Something
491-
like this:
492-
493-
my @values;
494-
my @seen;
495-
loop {
496-
outcome {
497-
whenever @p {
498-
if !@seen.first: { $_ === $:promise } {
499-
@values.push: $_;
500-
@seen.push: $:promise;
501-
}
502-
wait 0 { say "Giving up for now"; last }
503-
}
504-
# do other stuff
505-
}
506-
507476
=head1 Supplies
508477

509478
Channels are good for producer/consumer scenarios, but because each worker

0 commit comments

Comments
 (0)