Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 957 lines (570 sloc) 48.872 kB
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
1 =encoding utf8
2
3 =head1 TITLE
4
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
5 Synopsis 17: Concurrency
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
6
7 =head1 VERSION
8
9 Created: 3 Nov 2013
10
9b2b9cb @moritz S17: supplies are .done(), not .close()d
moritz authored
11 Last Modified: 27 December 2014
12 Version: 25
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
13
9f20af7 @raiph Fix typos and whitespace
raiph authored
14 This synopsis is based around the concurrency primitives and tools currently being implemented in Rakudo on MoarVM and the JVM. It covers both things that are already implemented today, in addition to things expected to be implemented in the near future (where "near" means O(months)).
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
15
16 =head1 Design Philosophy
17
18 =head2 Focus on composability
19
9f20af7 @raiph Fix typos and whitespace
raiph authored
20 Perl 6 generally prefers constructs that compose well, enabling large problems to be solved by putting together solutions for lots of smaller problems. This also helps make it easier to extend and refactor code.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
21
9f20af7 @raiph Fix typos and whitespace
raiph authored
22 Many common language features related to parallel and asynchronous programming lack composability. For example:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
23
24 =over
25
7b0188b @moritz [new 17] whitespace fixes
moritz authored
26 =item *
27
9f20af7 @raiph Fix typos and whitespace
raiph authored
28 Locks do not compose, since two independently correct operations using locks may deadlock when performed together.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
29
7b0188b @moritz [new 17] whitespace fixes
moritz authored
30 =item *
31
9f20af7 @raiph Fix typos and whitespace
raiph authored
32 Callback-centric approaches tend to compose badly, with chains of asynchronous operations typically leading to deeply nested callbacks. This essentially is just leaving the programmer to do a CPS transform of their own logical view of the program by hand.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
33
7b0188b @moritz [new 17] whitespace fixes
moritz authored
34 =item *
35
9f20af7 @raiph Fix typos and whitespace
raiph authored
36 Directly spawning threads on a per-component basis tends to compose badly, as when a dozen such components are used together the result is a high number of threads with no ability to centrally schedule or handle errors.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
37
38 =back
39
9f20af7 @raiph Fix typos and whitespace
raiph authored
40 In Perl 6, concurrency features aimed at typical language users should have good composability properties, both with themselves and also with other language features.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
41
42 =head2 Boundaries between synchronous and asynchronous should be explicit
43
9f20af7 @raiph Fix typos and whitespace
raiph authored
44 Asynchrony happens when we initiate an operation, then continue running our own idea of "next thing" without waiting for the operation to complete. This differs from synchronous programming, where calling a sub or method causes the caller to wait for a result before continuing.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
45
9f20af7 @raiph Fix typos and whitespace
raiph authored
46 The vast majority of programmers are much more comfortable with synchrony, as in many senses it's the "normal thing". As soon as we have things taking place asynchronously, there is a need to coordinate the work, and doing so tends to be domain specific. Therefore, placing the programmer in an asynchronous situation when they didn't ask for it is likely to lead to confusion and bugs. We should try to make places where asynchrony happens clear.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
47
9f20af7 @raiph Fix typos and whitespace
raiph authored
48 It's also worthwhile trying to make it easy to keep asynchronous things flowing asynchronously. While synchronous code is pull-y (for example, eating its way through iterable things, blocking for results), asynchronous code is push-y (results get pushed to things that know what to do next).
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
49
9f20af7 @raiph Fix typos and whitespace
raiph authored
50 Places where we go from synchronous to asynchronous, or from asynchronous to synchronous, are higher risk areas for bugs and potential bottlenecks. Thus, Perl 6 should try to provide features that help minimize the need to make such transitions.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
51
52 =head2 Implicit parallelism is OK
53
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
54 Parallelism is primarily about taking something we could do serially and using multiple CPU cores in order to get to a result more quickly. This leads to a very nice property: a parallel solution to a problem should give the same answer as a serial solution.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
55
9f20af7 @raiph Fix typos and whitespace
raiph authored
56 While under the hood there is asynchrony and the inherent coordination it requires, on the outside a problem solved using parallel programming is still, when taken as a whole, a single, synchronous operation.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
57
9f20af7 @raiph Fix typos and whitespace
raiph authored
58 Elsewhere in the specification, Perl 6 provides several features that allow the programmer to indicate that parallelizing an operation will produce the same result as evaluating it serially:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
59
60 =over
61
7b0188b @moritz [new 17] whitespace fixes
moritz authored
62 =item *
63
9f20af7 @raiph Fix typos and whitespace
raiph authored
64 Hyper operators (L<S03/Hyper operators>) express that parallel operator application is safe.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
65
7b0188b @moritz [new 17] whitespace fixes
moritz authored
66 =item *
67
8a85b31 @lizmat Some punctuation
lizmat authored
68 Junctions (L<S09/Junctions>) may auto-thread in parallel.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
69
7b0188b @moritz [new 17] whitespace fixes
moritz authored
70 =item *
71
9f20af7 @raiph Fix typos and whitespace
raiph authored
72 Feeds (L<S06/Feed operators>) form pipelines and express that the stages may be executed in parallel in a producer-consumer style relationship (though each stage is in itself not parallelized).
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
73
7b0188b @moritz [new 17] whitespace fixes
moritz authored
74 =item *
75
9f20af7 @raiph Fix typos and whitespace
raiph authored
76 C<hyper> and C<race> list operators (L<S02/The hyper operator>) express that iteration may be done in parallel; this is a generalization of hyper operators.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
77
78 =back
79
80 =head2 Make the hard things possible
81
9f20af7 @raiph Fix typos and whitespace
raiph authored
82 The easy things should be easy, and able to be built out of primitives that compose nicely. However, such things have to be built out of what VMs and operating systems provide: threads, atomic instructions (such as CAS), and concurrency control constructs such as mutexes and semaphores. Perl 6 is meant to last for decades, and the coming decades will doubtless bring new ways do do parallel and asynchronous programming that we do not have today. They will still, however, almost certainly need to be built out of what is available.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
83
9f20af7 @raiph Fix typos and whitespace
raiph authored
84 Thus, the primitive things should be provided for those who need to work on such hard things. Perl 6 should not hide the existence of OS-level threads, or fail to provide access to lower level concurrency control constructs. However, they should be clearly documented as I<not> the way to solve the majority of problems.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
85
86 =head1 Schedulers
87
9f20af7 @raiph Fix typos and whitespace
raiph authored
88 Schedulers lie at the heart of all concurrency in Perl 6. While most users are unlikely to immediately encounter schedulers when starting to use Perl 6's concurrency features, many of them are implemented in terms of it. Thus, they will be described first here to avoid lots of forward references.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
89
9f20af7 @raiph Fix typos and whitespace
raiph authored
90 A scheduler is something that does the C<Scheduler> role. Its responsibility is taking code objects representing tasks that need to be performed and making sure they get run, as well as handling any time-related operations (such as, "run this code every second").
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
91
9f20af7 @raiph Fix typos and whitespace
raiph authored
92 The current default scheduler is available as C<$*SCHEDULER>. If no such dynamic variable has been declared, then C<$PROCESS::SCHEDULER> is used. This defaults to an instance of C<ThreadPoolScheduler>, which maintains a pool of threads and distributes scheduled work amongst them. Since the scheduler is dynamically scoped, this means that test scheduler modules can be developed that poke a C<$*SCHEDULER> into C<EXPORT>, and then provide the test writer with control over time.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
93
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
94 The C<cue> method takes a C<Callable> object and schedules it.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
95
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
96 $*SCHEDULER.cue: { say "Golly, I got scheduled!" }
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
97
9f20af7 @raiph Fix typos and whitespace
raiph authored
98 Various options may be supplied as named arguments. (All references to time are taken to be in seconds, which may be fractional.) You may schedule an event to fire off after some number of seconds:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
99
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
100 $*SCHEDULER.cue: in=>10, { say "10s later" }
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
101
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
102 or at a given absolute time, specified as an C<Instant>:
103
ff201e6 @lizmat Fix copy-n-pasto
lizmat authored
104 $*SCHEDULER.cue: at=>$instant, { say "at $instant" }
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
105
9f20af7 @raiph Fix typos and whitespace
raiph authored
106 If a scheduled item dies, the scheduler will catch this exception and pass it to a C<handle_uncaught> method, a default implementation of which is provided by the C<Scheduler> role. This by default will report the exception and cause the entire application to terminate. However, it is possible to replace this:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
107
108 $*SCHEDULER.uncaught_handler = sub ($exception) {
109 $logger.log_error($exception);
110 }
111
9f20af7 @raiph Fix typos and whitespace
raiph authored
112 For more fine-grained handling, it is possible to schedule code along with a code object to be invoked with the thrown exception if it dies:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
113
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
114 $*SCHEDULER.cue:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
115 { upload_progress($stuff) },
d702a9e @cedric-vincent Fix .cue parameter names.
cedric-vincent authored
116 catch => -> $ex { warn "Could not upload latest progress" }
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
117
9f20af7 @raiph Fix typos and whitespace
raiph authored
118 Use C<:every> to schedule a task to run at a fixed interval, possibly with a delay before the first scheduling.
98c34a7 @lizmat Introduce .cue(:times(42)) and .cue(:stop($var))
lizmat authored
119
120 # Every second, from now
121 $*SCHEDULER.cue: :every(1), { say "Oh wow, a kangaroo!" };
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
122
98c34a7 @lizmat Introduce .cue(:times(42)) and .cue(:stop($var))
lizmat authored
123 # Every 0.5s, but don't start for 2s.
124 $*SCHEDULER.cue: { say "Kenya believe it?" }, :every(0.5), :in(2);
125
d702a9e @cedric-vincent Fix .cue parameter names.
cedric-vincent authored
126 Since this will cause the given task to be executed at the given interval ad infinitum, there are two ways to make sure the scheduling of the task is halted at a future time. The first is provided by specifying the C<:times> parameter in the .cue:
98c34a7 @lizmat Introduce .cue(:times(42)) and .cue(:stop($var))
lizmat authored
127
128 # Every second, from now, but only 42 times
d702a9e @cedric-vincent Fix .cue parameter names.
cedric-vincent authored
129 $*SCHEDULER.cue: :every(1), :times(42), { say "Oh wow, a kangaroo!" };
98c34a7 @lizmat Introduce .cue(:times(42)) and .cue(:stop($var))
lizmat authored
130
9f20af7 @raiph Fix typos and whitespace
raiph authored
131 The second is by specifying code that will be checked at the end of each interval. The task will be stopped as soon as it returns a True value. You can do this with the C<:stop> parameter.
98c34a7 @lizmat Introduce .cue(:times(42)) and .cue(:stop($var))
lizmat authored
132
133 # Every second, from now, until stopped
134 my $stop;
b4aedd1 @lizmat $*SCHEDULER.cue takes a callable as :stop
lizmat authored
135 $*SCHEDULER.cue: :every(1), :stop({$stop}), { say "Oh wow, a kangaroo!" };
98c34a7 @lizmat Introduce .cue(:times(42)) and .cue(:stop($var))
lizmat authored
136 sleep 10;
137 $stop = True; # task stopped after 10 seconds
138
9f20af7 @raiph Fix typos and whitespace
raiph authored
139 The C<.cue> method returns a C<Cancellation> object, which can also be used to stop a repeating cue:
dcada73 @lizmat Mention Cancellation object returned by .cue
lizmat authored
140
141 my $c = $*SCHEDULER.cue: :every(1), { say "Oh wow, a kangaroo!" };
142 sleep 10;
143 $c.cancel; # task stopped after 10 seconds
144
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
145 Schedulers also provide counts of the number of operations in various states:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
146
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
147 say $*SCHEDULER.loads;
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
148
9f20af7 @raiph Fix typos and whitespace
raiph authored
149 This returns, in order, the number of cues that are not yet runnable due to delays, the number of cues that are runnable but not yet assigned to a thread, and the number of cues that are now assigned to a thread (and presumably running).
f597d20 @TimToady Scheduler refinements/defilements
TimToady authored
150 [Conjecture: perhaps these should be separate methods.]
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
151
9f20af7 @raiph Fix typos and whitespace
raiph authored
152 Schedulers may optionally provide further introspection in order to support tools such as debuggers.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
153
9f20af7 @raiph Fix typos and whitespace
raiph authored
154 There is also a C<CurrentThreadScheduler>, which always schedules things on the current thread. It provides the same methods, just no concurrency, and any exceptions are thrown immediately. This is mostly useful for forcing synchrony in places that default to asynchrony. (Note that C<.loads> can never return anything but 0 for the currently running cues, since they're waiting on the current thread to stop scheduling first!)
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
155
156 =head1 Promises
157
9f20af7 @raiph Fix typos and whitespace
raiph authored
158 A C<Promise> is a synchronization primitive for an asynchronous piece of work that will produce a single result (thus keeping the promise) or fail in some way (thus breaking the promise).
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
159
160 The simplest way to use a C<Promise> is to create one:
161
162 my $promise = Promise.new;
163
164 And then later C<keep> it:
165
3a9b7a1 @lizmat Promise.keep/break no longer need explicit values
lizmat authored
166 $promise.keep; # True
167 $promise.keep(42); # a specific return value for kept Promise
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
168
169 Or C<break> it:
170
3a9b7a1 @lizmat Promise.keep/break no longer need explicit values
lizmat authored
171 $promise.break; # False
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
172 $promise.break(X::Some::Problem.new); # With exception
173 $promise.break("I just couldn't do it"); # With message
174
9f20af7 @raiph Fix typos and whitespace
raiph authored
175 The current status of a C<Promise> is available through the C<status> method, which returns an element from the C<PromiseStatus> enumeration.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
176
4134f88 @lizmat Remove Redeemed/.redeem
lizmat authored
177 enum PromiseStatus (:Planned(0), :Kept(1), :Broken(2));
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
178
9f20af7 @raiph Fix typos and whitespace
raiph authored
179 The result itself can be obtained by calling C<result>. If the C<Promise> was already kept, the result is immediately returned. If the C<Promise> was broken then the exception that it was broken with is thrown. If the C<Promise> is not yet kept or broken, then the caller will block until this happens.
f136000 @lizmat Add "Redeemed" status and .redeem to Promise
lizmat authored
180
e32d821 @cedric-vincent s/excuse/cause/ for Promise.
cedric-vincent authored
181 A C<Promise> will boolify to whether the C<Promise> is already kept or broken. There is also an C<cause> method for extracting the exception from a C<Broken> C<Promise> rather than having it thrown.
39ac0ca @lizmat Eradicate Promise.has_result in favor of .Bool
lizmat authored
182
183 if $promise {
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
184 if $promise.status == Kept {
185 say "Kept, result = " ~ $promise.result;
186 }
187 else {
e32d821 @cedric-vincent s/excuse/cause/ for Promise.
cedric-vincent authored
188 say "Broken because " ~ $promise.cause;
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
189 }
190 }
191 else {
192 say "Still working!";
193 }
194
bd42b63 @TimToady s/cause/excuse/
TimToady authored
195 You can also simply use a switch:
196
197 given $promise.status {
5027a94 @lizmat Let's revert back to before I got into this
lizmat authored
198 when Planned { say "Still working!" }
199 when Kept { say "Kept, result = ", $promise.result }
e32d821 @cedric-vincent s/excuse/cause/ for Promise.
cedric-vincent authored
200 when Broken { say "Broken because ", $promise.cause }
bd42b63 @TimToady s/cause/excuse/
TimToady authored
201 }
202
9f20af7 @raiph Fix typos and whitespace
raiph authored
203 There are various convenient "factory" methods on C<Promise>. The most common is C<start>.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
204
2f50e05 @TimToady use 'start' for initiating asynchronicity
TimToady authored
205 my $p = Promise.start(&do_hard_calculation);
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
206
415fa4c @lizmat Revert "S17: sub start is deprecated"
lizmat authored
207 This creates a C<Promise> that runs the supplied code, and calls C<keep> with its result. If the code throws an exception, then C<break> is called with the C<Exception>. Most of the time, however, the above is simply written as:
208
209 my $p = start {
210 # code here
211 }
212
213 Which is implemented by calling C<Promise.start>.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
214
9f20af7 @raiph Fix typos and whitespace
raiph authored
215 There is also a method to create a C<Promise> that is kept after a number of seconds, or at a specific time:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
216
c45281d @jnthn Promise.in/Promise.at.
jnthn authored
217 my $kept_in_10s = Promise.in(10);
218 my $kept_in_duration = Promise.in($duration);
219 my $kept_at_instant = Promise.at($instant);
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
220
9f20af7 @raiph Fix typos and whitespace
raiph authored
221 The C<result> is always C<True> and such a C<Promise> can never be broken. It is mostly useful for combining with other promises.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
222
e32d821 @cedric-vincent s/excuse/cause/ for Promise.
cedric-vincent authored
223 There are also a couple of C<Promise> combinators. The C<anyof> combinator creates a C<Promise> that is kept whenever any of the specified C<Promise>s are kept. If the first promise to produce a result is instead broken, then the resulting C<Promise> is also broken. The cause is passed along. When the C<Promise> is kept, it has a C<True> result.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
224
415fa4c @lizmat Revert "S17: sub start is deprecated"
lizmat authored
225 my $calc = start { ... }
0d03b52 @lizmat Some more s/alarm/in/
lizmat authored
226 my $timeout = Promise.in(10);
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
227 my $timecalc = Promise.anyof($calc, $timeout);
228
9f20af7 @raiph Fix typos and whitespace
raiph authored
229 There is also an C<allof> combinator, which creates a C<Promise> that will be kept when all of the specified C<Promise>s are kept, or broken if any of them are broken.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
230
7b9010f @TimToady Conjectures, s/keeper/vow/ to stay non-commital
TimToady authored
231 [Conjecture: there should be infix operators for these resembling the junctionals.]
232
9f20af7 @raiph Fix typos and whitespace
raiph authored
233 The C<then> method on a C<Promise> is used to request that a certain piece of code should be run, receiving the C<Promise> as an argument, when the C<Promise> is kept or broken. If the C<Promise> is already kept or broken, the code is scheduled immediately. It is possible to call C<then> more than once, and each time it returns a C<Promise> representing the completion of both the original C<Promise> as well as the code specified in C<then>.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
234
235 my $feedback_promise = $download_promise.then(-> $res {
236 given $res.status {
237 when Kept { say "File $res.result().name() download" }
e32d821 @cedric-vincent s/excuse/cause/ for Promise.
cedric-vincent authored
238 when Broken { say "FAIL: $res.cause()" }
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
239 }
240 });
241
9f20af7 @raiph Fix typos and whitespace
raiph authored
242 [Conjecture: this needs better syntax to separate the "then" policies from the "else" policies (and from "catch" policies?), and to avoid a bunch of switch boilerplate. We already know the givens here...]
243
244 One risk when working with C<Promise>s is that another piece of code will sneak in and keep or break a C<Promise> it should not. The notion of a promise is user-facing. To instead represent the promise from the viewpoint of the promiser, the various built-in C<Promise> factory methods and combinators use C<Promise::Vow> objects to represent that internal resolve to fulfill the promise. ("I have vowed to keep my promise to you.") The C<vow> method on a C<Promise> returns an object with C<keep> and C<break> methods. It can only be called once during a C<Promise> object's lifetime. Since C<keep> and C<break> on the C<Promise> itself just delegate to C<self.vow.keep(...)> or C<self.vow.break(...)>, obtaining the vow before letting the C<Promise> escape to the outside world is a way to take ownership of the right to keep or break it. For example, here is how the C<Promise.in> factory is implemented:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
245
0d03b52 @lizmat Some more s/alarm/in/
lizmat authored
246 method in(Promise:U: $seconds, :$scheduler = $*SCHEDULER) {
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
247 my $p = Promise.new(:$scheduler);
039bbfd @lizmat Some code example tweaks
lizmat authored
248 my $v = $p.vow;
249 $scheduler.cue: { $v.keep(True) }, :in($seconds);
7b9010f @TimToady Conjectures, s/keeper/vow/ to stay non-commital
TimToady authored
250 $p;
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
251 }
252
9f20af7 @raiph Fix typos and whitespace
raiph authored
253 The C<await> function is used to wait for one or more C<Promise>s to produce a result.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
254
255 my ($a, $b) = await $p1, $p2;
256
9f20af7 @raiph Fix typos and whitespace
raiph authored
257 This simply calls C<result> on each of the C<Promise>s, so any exception will be thrown.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
258
6e8b79e @lizmat Move introduction of outcome {} after Channel
lizmat authored
259 =head1 Channels
260
9f20af7 @raiph Fix typos and whitespace
raiph authored
261 A C<Channel> is essentially a concurrent queue. One or more threads can put values into the C<Channel> using C<send>:
6e8b79e @lizmat Move introduction of outcome {} after Channel
lizmat authored
262
263 my $c = Channel.new;
264 $c.send($msg);
265
266 Meanwhile, others can C<receive> them:
267
268 my $msg = $c.receive;
269
9f20af7 @raiph Fix typos and whitespace
raiph authored
270 Channels are ideal for producer/consumer scenarios, and since there can be many senders and many receivers, they adapt well to scaling certain pipeline stages out over multiple workers also. [Conjectural: The two feed operators C<< ==> >> and C<< <== >> are implemented using Channel to connect each of the stages.]
6e8b79e @lizmat Move introduction of outcome {} after Channel
lizmat authored
271
9f20af7 @raiph Fix typos and whitespace
raiph authored
272 A C<Channel> may be "forever", but it is possible to close it to further sends by telling it to C<close>:
6e8b79e @lizmat Move introduction of outcome {} after Channel
lizmat authored
273
274 $c.close();
275
9f20af7 @raiph Fix typos and whitespace
raiph authored
276 Trying to C<send> any further messages on a closed channel will throw the C<X::Channel::SendOnDone> exception. Closing a channel has no effect on the receiving end until all sent values have been received. At that point, any further calls to receive will throw C<X::Channel::ReceiveOnDone>. The C<done> method returns a C<Promise> that is kept when a sender has called C<close> and all sent messages have been received. Note that multiple calls to a channel return the same promise, not a new one.
6e8b79e @lizmat Move introduction of outcome {} after Channel
lizmat authored
277
9f20af7 @raiph Fix typos and whitespace
raiph authored
278 While C<receive> blocks until it can read, C<poll> takes a message from the channel if one is there or immediately returns C<Nil> if nothing is there.
6e8b79e @lizmat Move introduction of outcome {} after Channel
lizmat authored
279
116e309 @lizmat s/winner/earliest/ as suggested earlier today
lizmat authored
280 There is also a C<earliest> statement:
7136f93 @lizmat Make winner {} about Channel's only
lizmat authored
281
116e309 @lizmat s/winner/earliest/ as suggested earlier today
lizmat authored
282 earliest * {
7136f93 @lizmat Make winner {} about Channel's only
lizmat authored
283 more $c1 { say "First channel got a value" }
284 more $c2 { say "Second channel got a value" }
285 }
286
9f20af7 @raiph Fix typos and whitespace
raiph authored
287 That will invoke the closure associated with the first channel that receives a value.
7136f93 @lizmat Make winner {} about Channel's only
lizmat authored
288
9f20af7 @raiph Fix typos and whitespace
raiph authored
289 It's possible to add a timer using the keyword C<wait> followed by the number of seconds to wait (which may be fractional). As a degenerate case, in order to avoid blocking at all you may use a C<wait 0>. The timeout is always checked last, to guarantee that the other entries are all tried at least once before timing out.
7136f93 @lizmat Make winner {} about Channel's only
lizmat authored
290
116e309 @lizmat s/winner/earliest/ as suggested earlier today
lizmat authored
291 my $gotone = earliest * {
7136f93 @lizmat Make winner {} about Channel's only
lizmat authored
292 more $c1 { say "First channel got a value" }
293 more $c2 { say "Second channel got a value" }
294 wait 0 { say "Not done yet"; Nil }
295 }
296
297 The construct as a whole returns the result of whichever block was selected.
298
213ff41 @lumimies Fix broken markup
lumimies authored
299 It's also possible to process a variadic list of channels together, using generic code that works over some set of the channels (use C<*> to represent any of them). The index and the received value are passed to the code as named arguments C<$:k> and C<$:v> (possibly via priming if the code is instantiated ahead of time).
7136f93 @lizmat Make winner {} about Channel's only
lizmat authored
300
116e309 @lizmat s/winner/earliest/ as suggested earlier today
lizmat authored
301 earliest * {
7136f93 @lizmat Make winner {} about Channel's only
lizmat authored
302 more @channels { say "Channel $:k received, result was: ", $:v }
303 }
304
9f20af7 @raiph Fix typos and whitespace
raiph authored
305 In this case C<$:k> returns the index of the channel, base 0. Likewise C<$:v> returns the value.
7136f93 @lizmat Make winner {} about Channel's only
lizmat authored
306
9f20af7 @raiph Fix typos and whitespace
raiph authored
307 The C<earliest> construct also automatically checks the C<.done> promise corresponding to the channel, so it can also be used in order to write a loop to receive from a channel until it is closed:
5027a94 @lizmat Let's revert back to before I got into this
lizmat authored
308
309 gather loop {
116e309 @lizmat s/winner/earliest/ as suggested earlier today
lizmat authored
310 earliest $channel {
5027a94 @lizmat Let's revert back to before I got into this
lizmat authored
311 more * { take $_ }
312 done * { last }
313 }
314 }
59480b5 @timo fix formatting of a text paragraph
timo authored
315
9f20af7 @raiph Fix typos and whitespace
raiph authored
316 This is such a common pattern that we make a channel in list context behave that way:
6e8b79e @lizmat Move introduction of outcome {} after Channel
lizmat authored
317
318 for @$channel -> $val { ... }
319 for $channel.list -> $val { ... }
320
9f20af7 @raiph Fix typos and whitespace
raiph authored
321 (Note that this is not a combinator, but a means for transfering data from the reactive realm to the lazy realm. Some reasonable amount of buffering is assumed between the two.)
6e8b79e @lizmat Move introduction of outcome {} after Channel
lizmat authored
322
31f98d5 @jnthn Channels and Supplies should be top-level sections
jnthn authored
323 =head1 Supplies
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
324
9f20af7 @raiph Fix typos and whitespace
raiph authored
325 Channels are good for producer/consumer scenarios, but because each worker blocks on receive, it is not such an ideal construct for doing fine-grained processing of asynchronously produced streams of values. Additionally, there can only be one receiver for each value. Supplies exist to address both of these issues.
326
327 A C<Supply> pushes or pumps values to one or more receivers who have registered their interest. There are two types of Supplies: C<live> and C<on demand>. When tapping into a C<live> supply, the tap will only see values that are pumped B<after> the tap has been created. Such supplies are normally infinite in nature, such as mouse movements. Closing the tap does not stop events from occurring, it just means nobody is listening. All tappers see the same stream. A tap on an C<on demand> supply will initiate the production of values, and tapping the supply again may result in a new set of values. For example, C<Supply.interval> produces a fresh timer with the appropriate interval each time it is tapped. If the tap is closed, the timer stops pushing out new values.
328
329 Anything that does the C<Supply> role can be tapped (that is, subscribed to) by calling the C<tap> method on it. This takes up to three callables as arguments, the optional ones expresses as named arguments:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
330
d0fb631 @TimToady s/fail/catch/ on receiving end, tap has named args
TimToady authored
331 $supply.tap: -> $value { say "Got a $value" },
194620a @TimToady change fail/catch to quit/quit
TimToady authored
332 done => { say "Reached the end" },
333 quit => {
724cff7 @lizmat Fix TAB pollution
lizmat authored
334 when X::FooBar { die "Major oopsie" };
335 default { warn "Supply shut down early: $_" }
194620a @TimToady change fail/catch to quit/quit
TimToady authored
336 }
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
337
9f20af7 @raiph Fix typos and whitespace
raiph authored
338 The first, known as the C<emit> closure, is invoked whenever a value is emitted into the thing that has been tapped. The optional named parameter C<done> specifies the code to be invoked when all expected values have been produced and no more values will be emitted. The optional named parameter C<quit> specifies the code to be invoked if there is an error. This also means there will be no further values.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
339
9f20af7 @raiph Fix typos and whitespace
raiph authored
340 The simplest Supply is a C<Supply> class, which is punned from the role. It creates a C<live> supply. On the "pumping" end, this has corresponding methods C<emit>, C<done>, and C<quit>, which notify all current taps.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
341
039bbfd @lizmat Some code example tweaks
lizmat authored
342 my $s = Supply.new;
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
343
039bbfd @lizmat Some code example tweaks
lizmat authored
344 my $t1 = $s.tap({ say $_ });
83d4beb @TimToady Prefer "emit" to "drip"
TimToady authored
345 $s.emit(1); # 1\n
346 $s.emit(2); # 2\n
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
347
039bbfd @lizmat Some code example tweaks
lizmat authored
348 my $t2 = $s.tap({ say 2 * $_ },
72242eb @moritz Fix example for $s.tap, mentioned in #77
moritz authored
349 :done({ say "End" }));
83d4beb @TimToady Prefer "emit" to "drip"
TimToady authored
350 $s.emit(3); # 3\n6\n
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
351
9f20af7 @raiph Fix typos and whitespace
raiph authored
352 The object returned by C<tap> represents the subscription. To stop subscribing, call C<close> on it.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
353
039bbfd @lizmat Some code example tweaks
lizmat authored
354 $t1.close;
83d4beb @TimToady Prefer "emit" to "drip"
TimToady authored
355 $s.emit(4); # 8\n
039bbfd @lizmat Some code example tweaks
lizmat authored
356 $s.done; # End\n
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
357
9f20af7 @raiph Fix typos and whitespace
raiph authored
358 This doesn't introduce any asynchrony directly. However, it is possible for values to be pumped into a C<Supply> from an asynchronous worker. In fact, it is possible for many threads to safely pump values into a supply. In the event this happens, the callback of the tap may be executed on many threads at the same time.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
359
9fd217a @lizmat Rename Supply.for -> from-list
lizmat authored
360 The C<Supply> class has various methods that produce more interesting kinds of
361 C<Supply>. These default to working asynchronously.
d0b6770 @lizmat s/Supply/Publish/ where needed
lizmat authored
362
9fd217a @lizmat Rename Supply.for -> from-list
lizmat authored
363 =over 4
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
364
9fd217a @lizmat Rename Supply.for -> from-list
lizmat authored
365 =item from-list
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
366
9fd217a @lizmat Rename Supply.for -> from-list
lizmat authored
367 my $fl = Supply.from-list(^10);
368
369 Takes a (potentially lazy) list of values, and returns an on demand C<Supply>
370 that, when tapped, will iterate over the values and invoke the C<emit> callable
371 for each of them, and any C<done> callable at the end. If the iteration at some
372 point produces an exception, then the C<quit> callable will be invoked to pass
373 along the exception.
374
375 =item interval
376
377 my $e1 = Supply.interval(1); # Once a second, starting now
378 my $e5i10 = Supply.interval(5, 10); # Each 5 seconds, starting in 10 seconds
379
380 Produces an on demand C<Supply> that, when tapped, will produce an ascending
381 value at a regular time interval.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
382
b8a36cb @jnthn Some additions/improvements to Supply spec.
jnthn authored
383 Take the returned tap object and close it to stop the ticks:
384
9fd217a @lizmat Rename Supply.for -> from-list
lizmat authored
385 my $e1 = Supply.interval(1).tap(&say);
b8a36cb @jnthn Some additions/improvements to Supply spec.
jnthn authored
386 # ...later...
9b2b9cb @moritz S17: supplies are .done(), not .close()d
moritz authored
387 $e1.done();
b8a36cb @jnthn Some additions/improvements to Supply spec.
jnthn authored
388
9fd217a @lizmat Rename Supply.for -> from-list
lizmat authored
389 =back
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
390
9f20af7 @raiph Fix typos and whitespace
raiph authored
391 Supplies are mathematically dual to iterators, and so it is possible to define the same set of operations on them as are available on lazy lists. The key difference is that, while C<grep> on a lazy list I<pulls> a value to process, working synchronously, C<grep> on a Supply has values I<pushed> through it, and pushes those that match the filter onwards to anything that taps it.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
392
9f20af7 @raiph Fix typos and whitespace
raiph authored
393 The following methods are available on an instantiated C<Supply> (C<$s> in these examples):
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
394
395 =over
396
276721d @lizmat Spec Supply.list
lizmat authored
397 =item list
398
c2543da @lizmat Spec Supply.wait
lizmat authored
399 my @l := $s.list;
400
276721d @lizmat Spec Supply.list
lizmat authored
401 Produces a lazy C<List> with the values of the C<Supply>.
402
c2543da @lizmat Spec Supply.wait
lizmat authored
403 =item wait
404
405 $s.wait;
406
407 Waits until the specified C<Supply> is C<done> or C<quit>.
408
02e59c9 @lizmat Formally spec Supply.(Channel|Promise)
lizmat authored
409 =item Channel
410
411 my $c = $s.Channel;
412
413 Produces a C<Channel> of the values of the given C<Supply>.
414
415 =item Promise
416
417 my $p = $s.Promise;
418
9f20af7 @raiph Fix typos and whitespace
raiph authored
419 Produces a C<Promise> that will be kept for the next value of the given C<Supply>, or will be broken when the C<Supply> is done before a value is produced.
4c3f6c6 @lizmat Spec Supply.Promise(:last)
lizmat authored
420
5d6ef10 @lizmat Re-imagine Supply.Promise(:last) as Supply.last
lizmat authored
421 =item last
422
850f8ed @lizmat Supply.last can give the last N more's
lizmat authored
423 my $l = $s.last(42); # default: 1
5d6ef10 @lizmat Re-imagine Supply.Promise(:last) as Supply.last
lizmat authored
424
9f20af7 @raiph Fix typos and whitespace
raiph authored
425 Produces a C<Supply> that will only emit the last N values of the given C<Supply> when it is C<done>. Default is the final value.
02e59c9 @lizmat Formally spec Supply.(Channel|Promise)
lizmat authored
426
877b5f9 @lizmat Spec Supply.grab
lizmat authored
427 =item grab
428
429 my $g = $s.grab( { .sort } ); # sort the values of a Supply
430
9f20af7 @raiph Fix typos and whitespace
raiph authored
431 Produces a C<Supply> will grab all values emitted by the given C<Supply> until it is done. It will then call the given closure and then C<emit> each of the return values of the closure, and then C<done> the Supply that was produced.
877b5f9 @lizmat Spec Supply.grab
lizmat authored
432
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
433 =item flat
7b0188b @moritz [new 17] whitespace fixes
moritz authored
434
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
435 my $f = $s.flat;
436
437 Produces a C<Supply> in which all values of the original supply are flattened.
438
439 =item do
440
441 my $seen;
442 my $d = $s.do( {$seen++} );
443
9f20af7 @raiph Fix typos and whitespace
raiph authored
444 Produces a C<Supply> that is identical to the original supply, but will execute the given code for its side-effects. It promises that only one thread will ever be executing the code object passed to it at a time; others will block behind it.
b806d66 @lizmat Spec Supply.act
lizmat authored
445
446 =item act
447
448 my $seen;
627f2a8 @hoelzro Fix -> vs . method call typo in S17
hoelzro authored
449 $s.act( {$seen++} );
b806d66 @lizmat Spec Supply.act
lizmat authored
450
9f20af7 @raiph Fix typos and whitespace
raiph authored
451 A special case of C<Supply>.do, that will also create a tap on the given C<Supply>, so that you only need to worry about writing the side-effect code.
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
452
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
453 =item grep
7b0188b @moritz [new 17] whitespace fixes
moritz authored
454
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
455 my $g = $s.grep( * > 5 );
63266bc @lizmat Some more about Supply.grep
lizmat authored
456 my $g = $s.grep(Int);
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
457
9f20af7 @raiph Fix typos and whitespace
raiph authored
458 Produces a C<Supply> that only provides values that you want. Takes either a C<Callable> (which is supposed to return a C<True> value to pass on emitted values) or a value to be smartmatched against.
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
459
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
460 =item map
7b0188b @moritz [new 17] whitespace fixes
moritz authored
461
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
462 my $m = $s.map( * * 5 );
463
464 Produces a C<Supply> that provides its original's Supply values multiplied by 5.
465
466 my $m2 = $s.map( { $_ xx 2 } );
467
468 Produces a C<Supply> that provides its original's Supply values twice.
469
221ef4b @lizmat Don't use 'uniq', use 'unique' instead
lizmat authored
470 =item unique
7b0188b @moritz [new 17] whitespace fixes
moritz authored
471
221ef4b @lizmat Don't use 'uniq', use 'unique' instead
lizmat authored
472 my $u = $s.unique( :as( {$_} ), :with( &[===] ), :expires(1) );
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
473
9f20af7 @raiph Fix typos and whitespace
raiph authored
474 Produces a C<Supply> that only provides unique values, as defined by the optional C<as> and C<with> named parameters (same as L<List.unique>). The optional C<expires> parameter specifies how long to wait (in seconds) before "resetting" and not considering a value to have been seen, even if it's the same as an old value.
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
475
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
476 =item squish
477
ecfddec @masak [S17] spec :expires named for .uniq
masak authored
478 my $q = $s.squish( :as( {$_} ), :with( &[===] ), :expires(1) );
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
479
9f20af7 @raiph Fix typos and whitespace
raiph authored
480 Produces a C<Supply> that only provides sequentially different values, as defined by the optional C<as> and C<with> named parameters (same as L<List.squish>). The optional C<expires> parameter specifies how long to wait (in seconds) before "resetting" and not squishing a new value with an old one, even if they are the same.
5ac4ae0 @lizmat Elaborate on Supply.(flat|do|map|grep|uniq|squish)
lizmat authored
481
2ac69b9 @lizmat Spec Supply.(min|max)
lizmat authored
482 =item max
483
484 my $a = $s.max(&by); # default &infix:<cmp>
485
9f20af7 @raiph Fix typos and whitespace
raiph authored
486 Produces a C<Supply> that produces the B<maximum> values of the specified C<Supply>. In other words, from a continuously ascending C<Supply> it will produce all the values. From a continuously descending C<Supply> it will only produce the first value. The optional parameter specifies the comparator, just as with C<Any.max>.
2ac69b9 @lizmat Spec Supply.(min|max)
lizmat authored
487
488 =item min
489
082012d @lizmat Spec Supply.elems
lizmat authored
490 my $i = $s.min(&by); # default &infix:<cmp>
2ac69b9 @lizmat Spec Supply.(min|max)
lizmat authored
491
9f20af7 @raiph Fix typos and whitespace
raiph authored
492 Produces a C<Supply> that produces the B<minimum> values of the specified C<Supply>. In other words, from a continuously descending C<Supply> it will produce all the values. From a continuously ascending C<Supply> it will only produce the first value. The optional parameter specifies the comparator, just as with C<Any.min>.
e5079f8 @lizmat Spec Supply.minmax
lizmat authored
493
494 =item minmax
495
496 my $m = $s.minmax(&by); # default &infix:<cmp>
497
9f20af7 @raiph Fix typos and whitespace
raiph authored
498 Produces a C<Supply> that produces the C<Range>s with the B<minimum> and B<maximum> values of the specified C<Supply>. The optional parameter specifies the comparator, just as with C<Any.minmax>.
2ac69b9 @lizmat Spec Supply.(min|max)
lizmat authored
499
9f36e02 @lizmat s/buffering/batch/ jnthn++
lizmat authored
500 =item batch
d6cdde4 @lizmat Spec Supply.(buffering|delayed) and up Version
lizmat authored
501
9f36e02 @lizmat s/buffering/batch/ jnthn++
lizmat authored
502 my $b = $s.batch( :elems(100), :seconds(1) );
d6cdde4 @lizmat Spec Supply.(buffering|delayed) and up Version
lizmat authored
503
9f20af7 @raiph Fix typos and whitespace
raiph authored
504 Produces a C<Supply> that batches the values of the given Supply by either the number of elements (using the C<elems> named parameter) or the maximum number of seconds (using the C<seconds> named parameter) or both. Values are grouped in a single array element when flushed.
dda1c40 @lizmat Split off .rotor from .buffering
lizmat authored
505
e155c08 @lizmat Speculate about Supply.throttle($elems,$seconds)
lizmat authored
506 =item throttle
507
508 my $t = $s.throttle( $elems, $seconds );
509
510 Produces a C<Supply> that throttles emitting the values of the given Supply
511 on the created C<Supply> by the number of elements (specified by the first
512 parameter) per number of seconds (specified by the second parameter).
513
082012d @lizmat Spec Supply.elems
lizmat authored
514 =item elems
515
516 my $e = $s.elems($seconds?); # default: see all
517
9f20af7 @raiph Fix typos and whitespace
raiph authored
518 Produces a C<Supply> that produces the number of elements seen in the given C<Supply>. You can also specify an interval to only see the number of elements seen once per that interval.
082012d @lizmat Spec Supply.elems
lizmat authored
519
dda1c40 @lizmat Split off .rotor from .buffering
lizmat authored
520 =item rotor
521
84b405c @lizmat Adapt Supply.rotor to match List.rotor
lizmat authored
522 my $b = $s.rotor(@cycle);
692aa10 @lizmat Spec the Supply.buffering( :overlap )
lizmat authored
523
84b405c @lizmat Adapt Supply.rotor to match List.rotor
lizmat authored
524 Produces a "rotoring" C<Supply> with the same semantics as List.rotor.
d6cdde4 @lizmat Spec Supply.(buffering|delayed) and up Version
lizmat authored
525
526 =item delayed
527
528 my $d = $s.delayed( 3.5 ); # delay supply 3.5 seconds
529
9f20af7 @raiph Fix typos and whitespace
raiph authored
530 Produces a C<Supply> that passes on the values of the given Supply with the given delay (in seconds).
d6cdde4 @lizmat Spec Supply.(buffering|delayed) and up Version
lizmat authored
531
da4e9a6 @lizmat Supply.unchanged -> Supply.stable
lizmat authored
532 =item stable
cd4164c @lizmat Spec Supply.unchanged
lizmat authored
533
da4e9a6 @lizmat Supply.unchanged -> Supply.stable
lizmat authored
534 my $u = $s.stable( $seconds, :$scheduler );
cd4164c @lizmat Spec Supply.unchanged
lizmat authored
535
9f20af7 @raiph Fix typos and whitespace
raiph authored
536 Produces a C<Supply> that only passes on a value if it wasn't superseded by another value in the given time (in seconds). Optionally uses another scheduler than the default scheduler, using the C<scheduler> named parameter.
cd4164c @lizmat Spec Supply.unchanged
lizmat authored
537
965ec59 @lizmat Change order to Supply.(start|migrate|schedule_on)
lizmat authored
538 =item start
539
540 my $t = $s.start( {...} );
541
9f20af7 @raiph Fix typos and whitespace
raiph authored
542 Takes a closure and, for each supplied value, schedules the closure to run on another thread. It then emits a Supply (resulting in us having a supply of supplies) that will either have a single value emitted and then be done if the async work completes successfully, or quit if the work fails. Useful for kicking off work on the thread pool if you do not want to block up the thread pushing values at you (maybe 'cus you are reacting to UI events, but have some long-running work to kick off). Usually used in combination with C<migrate>.
965ec59 @lizmat Change order to Supply.(start|migrate|schedule_on)
lizmat authored
543
a3348d4 @lizmat Attempt at speccing Supply.migrate
lizmat authored
544 =item migrate
545
965ec59 @lizmat Change order to Supply.(start|migrate|schedule_on)
lizmat authored
546 my $m = $t.migrate;
a3348d4 @lizmat Attempt at speccing Supply.migrate
lizmat authored
547
bcacc15 @lizmat Kebab-case Supply.schedule-on
lizmat authored
548 Produces a continuous C<Supply> from a C<Supply>, in which each value is a C<Supply>. As soon as a new C<Supply> appears, it will close the current C<Supply> and provide values from the new C<Supply>. Can be used in combination with C<schedule-on>.
a3348d4 @lizmat Attempt at speccing Supply.migrate
lizmat authored
549
bcacc15 @lizmat Kebab-case Supply.schedule-on
lizmat authored
550 =item schedule-on
339fada @lizmat Stub spec Supply.(schedule_on|start)
lizmat authored
551
bcacc15 @lizmat Kebab-case Supply.schedule-on
lizmat authored
552 my $o = $m.schedule-on( $scheduler );
89aa2ca @lizmat Spec Supply.(start|schedule-on), channeling jnthn++
lizmat authored
553
9f20af7 @raiph Fix typos and whitespace
raiph authored
554 This allows a C<Supply>'s emit/done/quit to be scheduled on another scheduler. Useful in GUI situations, for example, where the final stage of some work needs to be done on some UI scheduler in order to have UI updates run on the UI thread.
339fada @lizmat Stub spec Supply.(schedule_on|start)
lizmat authored
555
2cdc917 @lizmat Spec Supply.reduce
lizmat authored
556 =item reduce
557
558 my $r = $s.reduce( {...} );
559
9f20af7 @raiph Fix typos and whitespace
raiph authored
560 Produces a C<Supply> that will emit each reduction from the given C<Supply>, just like C<reduce> on C<List>s.
2cdc917 @lizmat Spec Supply.reduce
lizmat authored
561
075aec9 @lizmat Spec Supply.lines/words
lizmat authored
562 =item lines
563
564 my $l = $s.lines; # chomp lines
565 my $l = $s.lines( :!chomp ); # do *not* chomp lines
566
9f20af7 @raiph Fix typos and whitespace
raiph authored
567 Produces a C<Supply> that will emit the characters coming in line by line from a C<Supply> that's usually created by some asynchronous I/O operation. The optional C<:chomp> named parameter indicates whether to remove line separators: the default is C<True>.
075aec9 @lizmat Spec Supply.lines/words
lizmat authored
568
569 =item words
570
571 my $w = $s.words;
572
9f20af7 @raiph Fix typos and whitespace
raiph authored
573 Produces a C<Supply> that will emit the characters coming in word by word from a C<Supply> that's usually created by some asynchronous I/O operation.
075aec9 @lizmat Spec Supply.lines/words
lizmat authored
574
0b2e726 @lizmat Preliminary spec of Supply.classify
lizmat authored
575 =item classify
576
806f1cc @lizmat Spec Supply.classify slightly differently
lizmat authored
577 my $c = $s.classify( {.WHAT} ); # one Supply per type of value
0b2e726 @lizmat Preliminary spec of Supply.classify
lizmat authored
578 my $h = $s.classify( %mapper );
579 my $a = $s.classify( @mapper );
580
9f20af7 @raiph Fix typos and whitespace
raiph authored
581 Produces a C<Supply> in which the emit values are C<Pair>s consisting of the classification value and the C<Supply> to which values of the given C<Supply> will be emitted. Similar to C<List.classify>, but does not support multi-level classification.
0b2e726 @lizmat Preliminary spec of Supply.classify
lizmat authored
582
a375266 @lizmat Spec Supply.categorize
lizmat authored
583 =item categorize
584
585 my $c = $s.categorize( {@categories} );
586 my $h = $s.categorize( %mapper );
587 my $a = $s.categorize( @mapper );
588
9f20af7 @raiph Fix typos and whitespace
raiph authored
589 Produces a C<Supply> in which the emitted values are C<Pair>s consisting of zero or more classification values and the C<Supply> to which values of the given C<Supply> will be emitted. Similar to C<List.categorize>.
a375266 @lizmat Spec Supply.categorize
lizmat authored
590
419d760 @lizmat Spec Supply.(sort|reverse)
lizmat authored
591 =item reverse
592
593 my $r = $s.reverse;
594
9f20af7 @raiph Fix typos and whitespace
raiph authored
595 Produces a C<Supply> that emits the values of the given Supply in reverse order. Please note that this C<Supply> will only start delivering values when the given C<Supply> is C<done>.
419d760 @lizmat Spec Supply.(sort|reverse)
lizmat authored
596
597 =item sort
598
599 my $o = $s.sort(&by); # default &infix:<cmp>
600
9f20af7 @raiph Fix typos and whitespace
raiph authored
601 Produces a C<Supply> that emits the values of the given Supply in sorted order. Please note that this C<Supply> will only start delivering values when the given C<Supply> is C<done>. Optionally accepts a comparator C<Block>.
419d760 @lizmat Spec Supply.(sort|reverse)
lizmat authored
602
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
603 =back
604
9f20af7 @raiph Fix typos and whitespace
raiph authored
605 There are some combinators that deal with bringing multiple supplies together:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
606
607 =over
608
7b0188b @moritz [new 17] whitespace fixes
moritz authored
609 =item C<merge>
610
05ea68b @lizmat Add examples for Supply.(merge|zip)
lizmat authored
611 my $m = $s1.merge($s2);
612
f143d1a @lizmat Supply.(zip|merge) can be called as class method
lizmat authored
613 my $m = Supply.merge(@s); # also as class method
614
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
615 Produces a C<Supply> containing the values produced by given and the specified supply or supplies, and triggering C<done> once all of the supplies have done so.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
616
7b0188b @moritz [new 17] whitespace fixes
moritz authored
617 =item C<zip>
618
f143d1a @lizmat Supply.(zip|merge) can be called as class method
lizmat authored
619 my $z = $s1.zip($s2); # defaults to :with( &[,] )
620
621 my $z = Supply.zip(@s, :with( &[,] )); # also as class method
05ea68b @lizmat Add examples for Supply.(merge|zip)
lizmat authored
622
9f20af7 @raiph Fix typos and whitespace
raiph authored
623 Produces a C<Supply> that pairs together items from the given and the specified supply or supplies, using C<< infix:<,> >> by default or any other user-supplied function with the C<with> named parameter.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
624
2a277a7 @timo spec zip-latest
timo authored
625 =item C<zip-latest>
626
627 my $z = $s1.zip-latest($s2); # like zip, defaults to :with( &[,] )
628
629 my $z = Supply.zip-latest(@s, :with( &[,] )); # also a method on Supply.
630
2fd8ca0 @lizmat Spec Supply.zip-latest(:initial(42,63)) named
lizmat authored
631 my $z = Supply.zip-latest( @s, :initial(42,63) ); # initial state
632
9f20af7 @raiph Fix typos and whitespace
raiph authored
633 Produces a C<Supply> that will emit tuples of values as soon as any combined Supply produces a value. Before any tuples are emitted, all supplies have to have produced at least one value. By default, it uses C<< infix:<,> >> to produce the tuples, but the named parameter C<with> can override that.
2a277a7 @timo spec zip-latest
timo authored
634
9f20af7 @raiph Fix typos and whitespace
raiph authored
635 The named parameter C<initial> can optionally be used to indicate the initial state of the values to be emitted.
2fd8ca0 @lizmat Spec Supply.zip-latest(:initial(42,63)) named
lizmat authored
636
2a277a7 @timo spec zip-latest
timo authored
637 =back
638
639 [TODO: plenty more of these: while, until...]
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
640
9f20af7 @raiph Fix typos and whitespace
raiph authored
641 These combinators that involve multiple supplies need care in their implementation, since values may arrive at any point on each, and possibly at the same time. To help write such combinators, the C<on> meta-combinator is useful. C<on> taps many supplies, and ensures that only B<one> callback will be running at a time, freeing the combinator writer of worrying about synchronization issues.
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
642
9f20af7 @raiph Fix typos and whitespace
raiph authored
643 The C<on> combinator takes a block that receives the C<Supply> it will generate (and return) as the parameter. That block is supposed to return list of C<Pairs>, in which the keys are one or more Supplies. And the values are either a C<Block> (to be called for each value for that C<Supply>), or a hash with Pairs for C<emit>, C<done> and C<quit>.
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
644
9f20af7 @raiph Fix typos and whitespace
raiph authored
645 A simple combinator for C<Pair>ing values from two Supplies ($a and $b), would look like this:
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
646
647 my $result = on -> $res {
648 my @as;
649 my @bs;
650 on -> $res {
651 $a => sub ($val) {
652 @as.push($val);
653 if @as && @bs {
83d4beb @TimToady Prefer "emit" to "drip"
TimToady authored
654 $res.emit( @as.shift => @bs.shift );
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
655 }
656 },
657 $b => sub ($val) {
658 @bs.push($val);
659 if @as && @bs {
83d4beb @TimToady Prefer "emit" to "drip"
TimToady authored
660 $res.emit( @as.shift => @bs.shift );
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
661 }
662 }
663 }
664 }
d4ca726 @TimToady some typos/fossils in combine
TimToady authored
665
9f20af7 @raiph Fix typos and whitespace
raiph authored
666 Thus there is never any race or other thread-safety problems with mutating the C<@as> and C<@bs>. The default behaviour, if a C<Callable> is specified along with the supply, is to use it for C<emit> and provide a default C<done> and C<quit>. The default C<done> triggers C<done> on the result C<Supply>.
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
667
9f20af7 @raiph Fix typos and whitespace
raiph authored
668 Note that the code blocks for both Supplies are identical. There must be a better way of doing this. And indeed, there is: you can also specify more than one C<Supply> per block. The same as above implemented using that:
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
669
670 my $result = on -> $res {
671 my @values = ([],[]);
672 ($a,$b) => sub ($val,$index) {
673 @values[$index].push($val);
674 if all(@values) {
83d4beb @TimToady Prefer "emit" to "drip"
TimToady authored
675 $res.emit( (@values>>.shift) );
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
676 }
677 }
678 }
679
9f20af7 @raiph Fix typos and whitespace
raiph authored
680 Note that the block that is being called for each value from any of the Supplies also receives an index value to be able to group the values received. By default, any C<done> or C<quit> will be immediately propagated. This is basically how C<zip> is implemented.
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
681
9f20af7 @raiph Fix typos and whitespace
raiph authored
682 Sometimes, we want the resulting C<Supply> to be C<done> only when all specified Supplies are done. This is possible by specifying a hash with keys for C<emit>, C<done> and/or C<quit>, instead of just a C<Callable>. Given an array @s with Supplies:
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
683
684 my $done = 0;
685 my $result = on -> $res {
686 @s => {
83d4beb @TimToady Prefer "emit" to "drip"
TimToady authored
687 emit => -> \val { $res.more(val) },
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
688 done => { $res.done if ++$done == +@s }
689 }
690 }
691
9f20af7 @raiph Fix typos and whitespace
raiph authored
692 This is essentially how C<merge> is implemented. Note that if we don't need the index (as indicated by its absence in the signature of the C<Callable>s), it will not be passed.
0d7e427 @lizmat Spec 'on' more elaborately
lizmat authored
693
9f20af7 @raiph Fix typos and whitespace
raiph authored
694 A C<quit> handler can be provided in a similar way, although the default - convey the failure to the result supply - is normally what is wanted. The exception is writing combinators related to error handling.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
695
f47db42 @lizmat Spec signal() and some POSIX signals cleanup
lizmat authored
696 =head1 System events exposed as Supplies
697
9f20af7 @raiph Fix typos and whitespace
raiph authored
698 System events, such as signals, or mouse events, can be exposed as Supplies. Because of lack of portability, these will most likely be implemented as third-party modules.
f47db42 @lizmat Spec signal() and some POSIX signals cleanup
lizmat authored
699
9f20af7 @raiph Fix typos and whitespace
raiph authored
700 Basic signal support is offered by the C<signal> function, which takes one or more C<Signal> enums, and an optional C<scheduler> named parameter. It produces a C<Supply> which, when tapped, will C<emit> any signal coming in. For example:
f47db42 @lizmat Spec signal() and some POSIX signals cleanup
lizmat authored
701
702 signal(SIGINT).tap( { say "Thank you for your attention"; exit 0 } );
703
9f20af7 @raiph Fix typos and whitespace
raiph authored
704 would catch Control-C, thank you, and then exit. Of course, you don't need to exit immediately. Here's an example of how you would make sure that an iteration in a loop is completed before exiting:
2281be7 @lizmat A better example of signal().tap usage
lizmat authored
705
706 for @todo {
707 state $quitting;
708 state $tap = signal(SIGINT).tap( { $quitting = True } );
709 LAST $tap.close;
710 LEAVE exit(0) if $quitting;
711 ... # code to protect
712 }
713
714 This probably could use some syntactic sugar.
715
9f20af7 @raiph Fix typos and whitespace
raiph authored
716 The list of supported C<Signals> can be found by checking C<Signal::.keys>, as you would any enum.
f47db42 @lizmat Spec signal() and some POSIX signals cleanup
lizmat authored
717
718 =head1 I/O features exposed as Supplies
719
9f20af7 @raiph Fix typos and whitespace
raiph authored
720 Various I/O-related things are also exposed as supplies. For example, it is possible to get notifications on changes to files or files (directly) in a directory, using:
b8a36cb @jnthn Some additions/improvements to Supply spec.
jnthn authored
721
4b6a1fc @lizmat Kebab-case IO::Notification.watch_path
lizmat authored
722 IO::Notification.watch-path(".").tap(-> $file {
b8a36cb @jnthn Some additions/improvements to Supply spec.
jnthn authored
723 say "$file changed";
724 });
725
9f20af7 @raiph Fix typos and whitespace
raiph authored
726 This is quite a mouthful, so there is a shortcut available with the C<IO> coercer and the C<watch> method:
4df4368 @lizmat Spec Str.IO.watch -> IO::Notification.watch_path
lizmat authored
727
728 ".".IO.watch.tap: -> $file { say "$file changed" };
729
4b6a1fc @lizmat Kebab-case IO::Notification.watch_path
lizmat authored
730 Note that since I/O callbacks are, by default, scheduled on the thread pool, then it's possible that your callback will be executing twice on the same thread. One way to cope is with C<act>:
b8a36cb @jnthn Some additions/improvements to Supply spec.
jnthn authored
731
4df4368 @lizmat Spec Str.IO.watch -> IO::Notification.watch_path
lizmat authored
732 ".".IO.watch.act(-> $file {
b8a36cb @jnthn Some additions/improvements to Supply spec.
jnthn authored
733 state %changes;
734 say "$file changed (change {++%changes{$file}})";
735 });
736
9f20af7 @raiph Fix typos and whitespace
raiph authored
737 It can also take C<done> and C<quit> named parameters; these go to the tap, while the C<emit> closure is put in a C<do>. A C<Tap> is returned, which may be closed in the usual way. (Note that the name C<act> is also a subtle reference to actor semantics.)
b8a36cb @jnthn Some additions/improvements to Supply spec.
jnthn authored
738
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
739 =head1 Inter-Process Communication exposed as Promises and Supplies
740
9f20af7 @raiph Fix typos and whitespace
raiph authored
741 Starting external processes is rather easy: C<shell()>, C<run()> and C<qx//>. Having external processes run asynchronously, is slightly more involved. But not much. The workhorse of asynchronous IPC in Perl 6 is C<Proc::Async>:
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
742
59a1d61 @lizmat Better spec :ENV and :w for Proc::Async.new
lizmat authored
743 my $proc = Proc::Async.new( $path, @args );
744
9f20af7 @raiph Fix typos and whitespace
raiph authored
745 If you like to B<send> data to the process, you need to open it with the C<:w> named parameter.
59a1d61 @lizmat Better spec :ENV and :w for Proc::Async.new
lizmat authored
746
747 my $proc = Proc::Async.new( $path, @args, :w );
748
9f20af7 @raiph Fix typos and whitespace
raiph authored
749 By default, the current environment (as available in C<%*ENV>) will be set for the external process. You can override this with the :ENV named parameter:
59a1d61 @lizmat Better spec :ENV and :w for Proc::Async.new
lizmat authored
750
751 my $proc = Proc::Async.new( $path, @args, :ENV(%hash) );
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
752
9f20af7 @raiph Fix typos and whitespace
raiph authored
753 The returned object can then be called whenever needed to start the external process. However, before you do that, one needs to be clear what to do about the output of the external process. Getting information back from the external process's C<STDOUT> or C<STDERR>, is done by a C<Supply> that either gets characters or bytes.
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
754
5123177 @lizmat Make .stdout/.stderr suppliers more generic
lizmat authored
755 $proc.stdout.act(&say); # simply pass it on to our $*OUT as chars
756 $proc.stderr.act(&note); # and $*ERR as chars, but could be any code
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
757
758 or:
759
d21d857 @lizmat Be more like other IO: :bytes -> :bin FROGGS++
lizmat authored
760 $proc.stdout(:bin).act: { # process STDOUT bytes };
761 $proc.stderr(:bin).act: { # process STDERR bytes };
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
762
9f20af7 @raiph Fix typos and whitespace
raiph authored
763 So, to make sure no information will be lost, you need to create and tap the supplies B<before> the process is started.
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
764
9f20af7 @raiph Fix typos and whitespace
raiph authored
765 To start the external process, you need to call the C<.start> method. It returns a C<Promise> that becomes C<Kept> (and True) if the process concludes successfully, or C<Broken> (and False) if the process failed for some reason.
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
766
767 my $done = $proc.start( :$scheduler = $*SCHEDULER );
768
9f20af7 @raiph Fix typos and whitespace
raiph authored
769 To send data to the running process, you can use the C<.print>, C<.say> and C<.write> methods on the C<Proc::Async> object:
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
770
771 my $printed = $proc.print( "Hello world\n", :$scheduler = $*SCHEDULER );
772 my $said = $proc.say( "Hello world", :$scheduler = $*SCHEDULER );
773 my $written = $proc.write( $buffer, :$scheduler = $*SCHEDULER );
774
9f20af7 @raiph Fix typos and whitespace
raiph authored
775 They all also return a C<Promise> that is C<Kept> when communication with the process was successful.
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
776
9f20af7 @raiph Fix typos and whitespace
raiph authored
777 Some programs expect their C<STDIN> to be closed to signify the end of their processing. This can be achieved with the C<.close-stdin> method:
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
778
a729e2b @lizmat Don't spec new stuff with _ anymore
lizmat authored
779 $proc.close-stdin;
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
780
9f20af7 @raiph Fix typos and whitespace
raiph authored
781 Finally, if your process as going awry, you can stop it with the C<.kill> method:
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
782
783 $proc.kill; # sends HUP signal to process
784 $proc.kill("SIGINT"); # send INT signal
9103f96 @lizmat Also allow bare int's to be passed to .signal
lizmat authored
785 $proc.kill(1); # if you just know the signal number on your system
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
786
9f20af7 @raiph Fix typos and whitespace
raiph authored
787 The parameter should be something that is acceptable to the Kernel.signal method.
3536408 @lizmat Initial stab at speccing Proc::Async
lizmat authored
788
a6275df @lizmat There is no event loop, only subscribables:$*POSIX
lizmat authored
789 =head1 The Event Loop
790
9f20af7 @raiph Fix typos and whitespace
raiph authored
791 There is no event loop. Previous versions of this synopsis mentioned an event loop that would be underlying all concurrency. In this version, this is not the case.
a6275df @lizmat There is no event loop, only subscribables:$*POSIX
lizmat authored
792
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
793 =head2 Threads
794
9f20af7 @raiph Fix typos and whitespace
raiph authored
795 VM-level threads, which typically correspond to OS-level threads, are exposed through the C<Thread> class. Whatever underlies it, a C<Thread> should always be backed by something that is capable of being scheduled on a CPU core (that is, it may I<not> be a "green thread" or similar). Most users will not need to work with C<Thread>s directly. However, those building their own schedulers may well need to do so, and there may be other exceptional circumstances that demand such low-level control.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
796
9f20af7 @raiph Fix typos and whitespace
raiph authored
797 The easiest way to start a thread is with the C<start> method, which takes a C<Callable> and runs it on a new thread:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
798
b608ca3 @softmoth Use Thread.start() consistently (vs. Thread.run())
softmoth authored
799 my $thread = Thread.start({
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
800 say "Gosh, I'm in a thread!";
801 });
802
803 It is also possible to create a thread object, and set it running later:
804
805 my $thread = Thread.new(code => {
806 say "A thread, you say?";
807 });
808 # later...
a42c74b @jnthn Re-de-confuse Thread start/run.
jnthn authored
809 $thread.run();
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
810
9f20af7 @raiph Fix typos and whitespace
raiph authored
811 Both approaches result in C<$thread> containing a C<Thread> object. At some point, C<finish> should be called on the thread, from the thread that started it. This blocks until the thread has completed.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
812
2f50e05 @TimToady use 'start' for initiating asynchronicity
TimToady authored
813 say "Certainly before the thread is started";
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
814 my $thread = Thread.start({ say "In the thread" });
815 say "This could come before or after the thread's output";
2d03d45 @TimToady tweak .join to .finish (to complement .start)
TimToady authored
816 $thread.finish();
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
817 say "Certainly after all the above output";
818
9f20af7 @raiph Fix typos and whitespace
raiph authored
819 As an alternative to C<finish>, it is possible to create a thread whose lifetime is bounded by that of the overall application. Such threads are automatically terminated when the application exits. In a scenario where the initial thread creates an application lifetime thread and no others, then the exit of the initial thread will cause termination of the overall program. Such a thread is created by either:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
820
821 my $thread = Thread.new(:code({ ... }), :app_lifetime);
822
1ec306f @lizmat Amplify the difference between Thread.(start|new)
lizmat authored
823 Or just, by using the C<start> method:
824
825 my $thread = Thread.start({ ... }, :app_lifetime);
826
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
827 The property can be introspected:
828
829 say $thread.app_lifetime; # True/False
830
831 Each thread also has a unique ID, which can be obtained by the C<id> property.
832
833 say $thread.id;
834
9f20af7 @raiph Fix typos and whitespace
raiph authored
835 This should be treated as an opaque number. It can not be assumed to map to any particular operating system's idea of thread ID, for example. For that, use something that lets you get at OS-level identifiers (such as calling an OS API using NativeCall).
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
836
fabf7b2 @lizmat Elaborate on how to give a thread a name
lizmat authored
837 A thread may also be given a name.
838
1ec306f @lizmat Amplify the difference between Thread.(start|new)
lizmat authored
839 my $thread = Thread.start({ ... }, :name<Background CPU Eater>);
fabf7b2 @lizmat Elaborate on how to give a thread a name
lizmat authored
840
9f20af7 @raiph Fix typos and whitespace
raiph authored
841 This can be useful for understanding its usage. Uniqueness is not enforced; indeed, the default is "<anon>".
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
842
843 A thread stringifies to something of the form:
844
845 Thread<id>(name)
846
847 For example:
848
849 Thread<1234>(<anon>)
850
9f20af7 @raiph Fix typos and whitespace
raiph authored
851 The currently executing thread is available through C<$*THREAD>. This is even available in the initial thread of the program, in this case by falling back to C<$PROCESS::THREAD>, which is the initial thread of the process.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
852
9f20af7 @raiph Fix typos and whitespace
raiph authored
853 Finally, the C<yield> method can be called on C<Thread> (not on any particular thread) to hint to the OS that the thread has nothing useful to do for the moment, and so another thread should run instead.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
854
855 =head2 Atomic Compare and Swap
856
9f20af7 @raiph Fix typos and whitespace
raiph authored
857 The Atomic Compare and Swap (CAS) primitive is directly supported by most modern hardware. It has been shown that it can be used to build a whole range of concurrency control mechanisms (such as mutexes and semaphores). It can also be used to implement lock-free data structures. It is decidedly a primitive, and not truly composable due to risk of livelock. However, since so much can be built out of it, Perl 6 provides it directly.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
858
859 A Perl 6 implementation of CAS would look something like this:
860
861 sub cas($ref is rw, $expected, $new) {
862 my $seen = $ref;
863 if $ref === $expected {
864 $ref = $new;
865 }
866 return $seen;
867 }
868
9f20af7 @raiph Fix typos and whitespace
raiph authored
869 Except that it happens atomically. For example, a crappy non-reentrant mutex could be implemented as:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
870
871 class CrappyMutex {
872 has $!locked = 0;
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
873
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
874 method lock() {
875 loop {
876 return if cas($!locked, 0, 1) == 0;
877 }
878 }
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
879
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
880 method unlock() {
881 $!locked = 0;
882 }
883 }
884
9f20af7 @raiph Fix typos and whitespace
raiph authored
885 Another common use of CAS is in providing lock-free data structures. Any data structure can be made lock-free as long as you're willing to never mutate it, but build a fresh one each time. To support this, there is another C<&cas> candidate that takes a scalar and a block. It calls the block with the seen initial value. The block returns the new, updated value. If nothing else updated the value in the meantime, the reference will be updated. If the CAS fails because another update got in first, the block will be run again, passing in the latest value.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
886
71fd6d3 @lizmat Introduce $a (+=) 1 as sugar for cas $a, {$_ + 1}
lizmat authored
887 So, atomically incrementing a variable is done thusly:
888
889 cas $a, { $_.succ }; # $a++
890
891 or more generally for all assignment meta-operators:
892
893 cas $a, { $_ * 5 }; # $a *= 5
894
9f20af7 @raiph Fix typos and whitespace
raiph authored
895 Another example, implementing a top-5 news headlines list to be accessed and updated without ever locking, as:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
896
897 class TopHeadlines {
898 has $!headlines = []; # Scalar holding array, as CAS needs
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
899
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
900 method headlines() {
901 $!headlines
902 }
22f4608 @paultcochrane Purge trailing whitespace in S06 -- S17
paultcochrane authored
903
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
904 method add_headline($headline) {
905 cas($!headlines, -> @current {
906 my @new = $headline, @current;
907 @new.pop while @new.elems > 5;
908 @new
909 });
910 }
911 }
912
9f20af7 @raiph Fix typos and whitespace
raiph authored
913 It's the programmer's duty to ensure that the original data structure is never mutated and that the block has no side-effects (since it may be run any number of times).
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
914
7f3f23c @timo semaphore spec draft
timo authored
915 =head1 Low-level primitives
916
9f20af7 @raiph Fix typos and whitespace
raiph authored
917 Perl6 offers high-level concurrency methods, but in extreme cases, like if you need to implement a fundamentally different mechanism, these primitives are available.
7f3f23c @timo semaphore spec draft
timo authored
918
919 =head2 Locks
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
920
9f20af7 @raiph Fix typos and whitespace
raiph authored
921 Locks are unpleasant to work with, and users are pushed towards higher level synchronization primitives. However, those need to be implemented via lower level constructs for efficiency. As such, a simple lock mechanism - as close to what the execution environment offers as possible - is provided by the C<Lock> class. Note that it is erroneous to rely on the exact representation of an instance of this type (for example, don't assume it can be mixed into). Put another way, treat C<Lock> like a native type.
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
922
923 A C<Lock> is instantiated with C<new>:
924
925 $!lock = Lock.new;
926
927 The best way to use it is:
928
9209af6 @jnthn $lock.run => $lock.protect
jnthn authored
929 $!lock.protect: {
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
930 # code to run with the lock held
2f50e05 @TimToady use 'start' for initiating asynchronicity
TimToady authored
931 }
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
932
9f20af7 @raiph Fix typos and whitespace
raiph authored
933 This acquires the lock, runs the code passed, and then releases the lock. It ensures the lock will be released even if an exception is thrown. It is also possible to do:
0b11765 @jnthn Add a heretical S17, written from scratch.
jnthn authored
934
935 {
936 $!lock.lock();
937 # do stuff
938 LEAVE $!lock.unlock()
939 }
940
9f20af7 @raiph Fix typos and whitespace
raiph authored
941 When using the C<lock> and C<unlock> methods, the programmer must ensure that the lock is unlocked. C<Lock> is reentrant. Naturally, it's easy to introduce deadlocks. Again, this is a last resort, intended for those who are building first resorts.
97fc551 @TimToady add vim line
TimToady authored
942
7f3f23c @timo semaphore spec draft
timo authored
943 =head2 Semaphore
944
9f20af7 @raiph Fix typos and whitespace
raiph authored
945 The C<Semaphore> class implements traditional semaphores that can be initiated with a fixed number of permits and offers the operations C<acquire> to block on a positive number of permits to become available and then reduce that number by one, C<tryacquire> to try to acquire a permit, but return C<False> instead of blocking if there are no permits available yet. The last operation is C<release>, which will increase the number of permits by one.
7f3f23c @timo semaphore spec draft
timo authored
946
947 The initial number of permits may be negative, positive or 0.
948
9f20af7 @raiph Fix typos and whitespace
raiph authored
949 Some implementations allow for race-free acquisition and release of multiple permits at once, but this primitive does not offer that capability.
7f3f23c @timo semaphore spec draft
timo authored
950
3d64f1a @lucasbuchala Move AUTHORS sections to end of file
lucasbuchala authored
951 =head1 AUTHORS
952
953 Jonathan Worthington <jnthn@jnthn.net>
954 Elizabeth Mattijsen <liz@dijkmat.nl>
955
97fc551 @TimToady add vim line
TimToady authored
956 =for vim:set expandtab sw=4:
Something went wrong with that request. Please try again.