1
1
@tangler ex1 = examples/ex1.flx
2
2
@tangler ex2 = examples/ex2.flx
3
+ @tangler ex3 = examples/ex3.flx
4
+ @tangler ex4 = examples/ex4.flx
3
5
@title coroutines
4
6
@h1 A low level example.
5
7
I will show a simple low level example of coroutines, it will be a
@@ -207,7 +209,52 @@ Although this is not possible in Felix, you can write the option type @{opt} and
207
209
the @{None} case to signal end of data. In fact, in Felix you can write <em>any</em>
208
210
type down a channel, even channels!
209
211
210
- @h1 Felix is a coroutine
212
+ @h1 Asynchronous I/O
213
+ Although coroutines are <em>synchronous</em> Felix supports an extension
214
+ which allows two kinds of <em>asynchronous</em> I/O.
215
+
216
+ First, the library contains routines which can perform operations on sockets
217
+ including @{read}, @{write}, @{bind}, and @{connect}. That subsystem was used
218
+ to construct @{flx_web} which is the fastest web server in existence and can handle
219
+ enormous loads. It outperforms other webservers because it associates a fibre,
220
+ rather than a thread, with each connection, so context switching is lightning fast
221
+ and occurs in user space.
222
+
223
+ The socket system also uses advanced socket state notificantion services such
224
+ as @{kqueue} on MacOS and @{epoll} on Linux.
225
+
226
+ However we will demonstrate a simpler service, namely the system alarm closk
227
+ because it has a really simply API.
228
+ @tangle ex3
229
+ println$ "Begin Spawning";
230
+
231
+ spawn_fthread {
232
+ println "Start1"; sleep (2.0); println$ "End1";
233
+ };
234
+
235
+ spawn_fthread {
236
+ println "Start2"; sleep (1.0); println$ "End2";
237
+ };
238
+
239
+ sleep (0.25);
240
+ println$ "Mainline terminates";
241
+ @
242
+ And here's the output:
243
+ @pre
244
+ Begin Spawning
245
+ Start1
246
+ Start2
247
+ Mainline terminates
248
+ End2
249
+ End1
250
+ @
251
+
252
+ The @{sleep} procedure <em>suspends</em> the coroutine, it does not block
253
+ the pthread running the coroutine system. So aftedr the first coroutine
254
+ goes to sleep, the mainline continues and spawns the second coroutine.
255
+ But then, the mainline itself completes and suicides by fall through.
256
+
257
+ @h2 Felix is a coroutine
211
258
Most programming language generate programs which consist of single top level procedure,
212
259
for example @{main()} in C. It is a subroutine called by the startup code which in turn
213
260
was invoked by the operating system.
@@ -219,7 +266,111 @@ add a procedure @{flx_main()} to your code and that will also be run after
219
266
the initialisation of the library.
220
267
221
268
However there is something else different: the Felix mainline code is not
222
- a subroutine, it is a coroutine!
269
+ a subroutine, it is a coroutine! The mainline can happily terminate and
270
+ leave other fibres running: there's really nothing special about your
271
+ main program code, it's just another coroutine.
272
+
273
+ The Felix startup code creates a scheduler to run coroutines, and your
274
+ program code is just the initial coroutine. So your program terminates
275
+ when there is nothing left to do precisely because it's just another
276
+ coroutine.
277
+
278
+ @h2 Running subsystems
279
+ So far we have worked with the main scheduler, which terminates when there
280
+ is nothing left to do. So I want to show you a little problem and
281
+ how to solve it:
282
+ @tangle ex4
283
+ var x = list[int] (1,2,3,4,5);
284
+ fun addup (acc: int) (elt:int) => acc + elt;
285
+ var y = fold_left addup 0 x;
286
+ println$ y;
287
+ @
288
+ This is the purely functional way to add up a list. It is a client server application
289
+ where the fold operation is the service provider and the @{addup}
290
+ function is the client. However the technical implementation has a severe
291
+ problem: the client if forced to write a function to do the work, which can
292
+ only handle one element at a time, and has a single state variable, @{acc},
293
+ the accumulator. In other words, the client has to provide a callback,
294
+ which is a slave to master fold routine.
295
+
296
+ Here is another way to do the same thing:
297
+ @tangle ex4
298
+ var acc = 0;
299
+ for elt in x do
300
+ acc = acc + elt;
301
+ done
302
+ @
303
+ In this case we're using an iterator over the list which is provided
304
+ by the library, and that iterator is a slave to the client code
305
+ which is master: the iterator is called by the client.
306
+
307
+ Both these solutions work, and they are in fact dual or <em>control inverse</em>:
308
+ each one is the other one turned inside out with respect to control relations.
309
+ The client is a slave in the first solution whilst the iterator is a slave
310
+ in the second.
311
+
312
+ There is way to do this without slavery .. using coroutines of course!
313
+ @tangle ex4
314
+ begin
315
+ chip adder connector io pin inp: %<int {
316
+ var acc = 0;
317
+ while true do
318
+ var elt = read io.inp;
319
+ acc = acc + elt;
320
+ println$ acc;
321
+ done
322
+ }
323
+ (source_from_list x |-> adder)();
324
+ end
325
+ @
326
+ The chip @{source_from_list} comes from the standard library, it just writes
327
+ each element of a list down its output channel.
328
+
329
+ The code terminates when you expect and the last result printed is the answer.
330
+ But we just want the answer!
331
+
332
+ So here is our first attempt:
333
+ @tangle ex4
334
+ begin
335
+ var acc = 0;
336
+ chip adder connector io pin inp: %<int {
337
+ while true do
338
+ sleep (0.025);
339
+ var elt = read io.inp;
340
+ acc = acc + elt;
341
+ done
342
+ }
343
+
344
+ (source_from_list x |-> adder)();
345
+ println$ "Attempt1: " + acc.str;
346
+ end
347
+ @
348
+ Of course this doesn't work we get:
349
+ @pre
350
+ Attempt1: 0
351
+ @
352
+ because as we know, the mainline is a coroutine, and it continues on whilst
353
+ the @{adder} is sleeping. In the abstract semantics when there is a set
354
+ of coroutines waiting to run, the scheduler can run <em>any one</em> of them
355
+ so even without the sleep, it could complete the mainline before completing
356
+ the pipeline.
357
+
358
+
359
+ @tangle ex4
360
+ begin
361
+ var acc = 0;
362
+ chip adder connector io pin inp: %<int {
363
+ while true do
364
+ var elt = read io.inp;
365
+ acc = acc + elt;
366
+ done
367
+ }
368
+
369
+ run (source_from_list x |-> adder);
370
+ println$ "Attempt2: " + acc.str;
371
+ end
372
+ @
373
+
223
374
224
375
225
376
@h1 The concept of fibration
0 commit comments