]> Dogcows Code - chaz/talk-event-driven-programming-in-perl/blob - slides.html
8904bd24addaf2a9151822c1795c18636db47498
[chaz/talk-event-driven-programming-in-perl] / slides.html
1 <!DOCTYPE html>
2 <html><head><meta charset="utf-8"><title>Event-driven Programming in Perl</title><link rel="stylesheet" href="css/common.css"><link rel="stylesheet" href="css/slides.css"></head><body><textarea id="source">
3
4 class: center, middle
5 name: title
6
7 # Event-driven Programming in Perl
8
9 Charles McGarvey
10
11 ???
12 - Hi. I'm Charles McGarvey.
13 - Been a Perl programmer for a long time...
14
15 ---
16 class: center, middle
17 name: bluehost
18
19 ???
20 - My employer is hiring.
21 - It's a pretty cool employer...
22
23 ---
24 ## "Normal" [userspace] programs
25
26 .big[
27 1. Parse args.
28 2. Read input.
29 3. Do stuff.
30 4. Write results as output.
31 ]
32
33 ---
34 class: pizza
35 background-image: url(img/pizza.jpg)
36 background-size: 100%
37
38 --
39 ```bash
40 # orderpizza --crust-type=thin \
41 --toppings=cheese,pepperoni,mushrooms
42 ```
43
44 --
45 ```perl
46 # source code of the orderpizza program
47
48 my $order = get_order_from_args(@ARGV);
49
50 my $ingredients = gather_ingredients($order);
51
52 my $pizza = prepare_and_bake($order, $ingredentials);
53
54 print($pizza);
55 ```
56
57 ???
58 But some programs are long-lived.
59
60 ---
61 ## Event-driven programs
62
63 .big[
64 1. Events happen.
65 2. When events happen, some code runs and does something.
66 ]
67
68 ???
69 At it's core, event-driven programming is this simple.
70
71 But there are some complications and things to knows, which is why this talk exists.
72
73 ---
74 class: center, middle
75
76 ![Help desk](img/helpdesk.jpg)
77
78 .small[
79 Image by Soniachat8.
80 This image is licensed under the [Creative Commons Attribution-Share Alike 4.0 International](https://creativecommons.org/licenses/by-sa/4.0/deed.en) license.
81 ]
82
83 ---
84 name: graph-eventloop
85 class: center, middle
86
87 ## Event loop
88
89 ![Event loop](img/eventloop.svg)
90
91 ???
92 We'll refine this model as we go.
93
94 ---
95 class: ex-hwinterrupts
96
97 ## Hardware interrupts
98
99 ```bash
100 # cat /proc/interrupts
101 CPU0 CPU1
102 0: 51 0 IR-IO-APIC 2-edge timer
103 1: 685006 5 IR-IO-APIC 1-edge i8042
104 8: 0 0 IR-IO-APIC 8-edge rtc0
105 9: 1724419 6314 IR-IO-APIC 9-fasteoi acpi
106 12: 12300601 138 IR-IO-APIC 12-edge i8042
107 16: 0 0 IR-IO-APIC 16-fasteoi i801_smbus
108 120: 0 0 DMAR-MSI 0-edge dmar0
109 121: 0 0 DMAR-MSI 1-edge dmar1
110 122: 7009890 45112 IR-PCI-MSI 327680-edge xhci_hcd
111 123: 44 3 IR-PCI-MSI 360448-edge mei_me
112 124: 509 0 IR-PCI-MSI 1048576-edge rtsx_pci
113 125: 80130 0 IR-PCI-MSI 2621440-edge nvme0q0, nvme0q1
114 126: 121892439 2961357 IR-PCI-MSI 32768-edge i915
115 127: 49 100 IR-PCI-MSI 514048-edge snd_hda_intel:card0
116 128: 0 79412 IR-PCI-MSI 2621441-edge nvme0q2
117
118 ...
119 ```
120
121 ???
122 When a HW interrupt happens, the appropriate code to run is locate in the interrupt service routine
123
124 ---
125 class: ex-pressenter
126
127 ## Your first event-driven program
128
129 .middle[
130 ```perl
131 print "Press <Enter> to continue.";
132 <STDIN>;
133
134 handle_enter_keypress_event();
135 ```
136 ]
137
138 ---
139 class: ex-pressenter
140
141 ## Your first event-driven program
142
143 .middle[
144 ```perl
145 print "Press <Enter> to continue.";
146 *<STDIN>;
147
148 handle_enter_keypress_event();
149 ```
150 ]
151
152 .big[
153 - Wait for an event
154 ]
155
156 ---
157 class: ex-pressenter
158
159 ## Your first event-driven program
160
161 .middle[
162 ```perl
163 print "Press <Enter> to continue.";
164 *<STDIN>;
165
166 handle_enter_keypress_event();
167 ```
168 ]
169
170 .big[
171 - Wait for an event
172 - Event source
173 ]
174
175 ---
176 class: ex-pressenter
177
178 ## Your first event-driven program
179
180 .middle[
181 ```perl
182 print "Press <Enter> to continue.";
183 <STDIN>;
184
185 *handle_enter_keypress_event();
186 ```
187 ]
188
189 .big[
190 - Wait for an event
191 - Event source
192 - Event handler
193 ]
194
195 ---
196 class: ex-signals
197
198 ## Signals
199
200 ```perl
201 use POSIX;
202
203 $SIG{USR1} = sub { handle_usr1_signal_event() };
204 $SIG{ALRM} = sub { handle_timer_event() };
205 $SIG{QUIT} = sub { exit() };
206
207 alarm(3);
208
209 while (1) { POSIX::pause() }
210 ```
211
212 ---
213 class: ex-signals
214
215 ## Signals
216
217 ```perl
218 use POSIX;
219
220 *$SIG{USR1} = sub { handle_usr1_signal_event() };
221 *$SIG{ALRM} = sub { handle_timer_event() };
222 *$SIG{QUIT} = sub { exit() };
223
224 alarm(3);
225
226 while (1) { POSIX::pause() }
227 ```
228
229 ???
230 Notice that this program is capable of handling more than just one event handler at a time.
231
232 ---
233 class: ex-signals
234
235 ## Signals
236
237 ```perl
238 use POSIX;
239
240 $SIG{USR1} = sub { handle_usr1_signal_event() };
241 $SIG{ALRM} = sub { handle_timer_event() };
242 $SIG{QUIT} = sub { exit() };
243
244 alarm(3);
245
246 *while (1) { POSIX::pause() }
247 ```
248
249 .big[
250 - Program yields time to the kernel
251 ]
252
253 ???
254 This example has a loop, but it's not really doing anything.
255
256 But actually it is doing something very important: It's yielding its processing time to the kernel.
257
258 And kernel informs the program of events.
259
260 You might be able to use `sleep` as well, but some implementations of libc
261 in the past have implemented `sleep` using `alarm`.
262
263 Don't know how common that is nowadays, but old habbits...
264
265 ---
266 class: ex-gui
267
268 ## Graphical user interface example
269
270 ```perl
271 use Gtk3 '-init';
272
273 my $window = Gtk3::Window->new;
274
275 $window->signal_connect('key-press-event' => \&handle_keypress_event);
276 $window->signal_connect('delete-event' => \&Gtk3::main_quit);
277
278 $window->show_all;
279
280 Gtk3::main();
281 ```
282
283 ???
284 A user interface happens to be a good place to use evented code because humans are spontaneous and unpredictable.
285
286 ---
287 class: ex-gui
288
289 ## Graphical user interface example
290
291 ```perl
292 use Gtk3 '-init';
293
294 my $window = Gtk3::Window->new;
295
296 *$window->signal_connect('key-press-event' => \&handle_keypress_event);
297 *$window->signal_connect('delete-event' => \&Gtk3::main_quit);
298
299 $window->show_all;
300
301 Gtk3::main();
302 ```
303
304 .big[
305 - Event handlers
306 ]
307
308 ---
309 class: ex-gui
310
311 ## Graphical user interface example
312
313 ```perl
314 use Gtk3 '-init';
315
316 my $window = Gtk3::Window->new;
317
318 $window->signal_connect('key-press-event' => \&handle_keypress_event);
319 $window->signal_connect('delete-event' => \&Gtk3::main_quit);
320
321 $window->show_all;
322
323 *Gtk3::main();
324 ```
325
326 .big[
327 - Event handlers
328 - Yield and wait for events
329 ]
330
331 ---
332 class: event-types
333
334 ## Types of events
335
336 - IO
337
338 ???
339 Data available on a socket, or a new connection.
340
341 Server or client, data across a wire cannot typically be relied upon to arrive in a predictable fashion, so an
342 event-driven architect makes a lot of sense for network applications.
343
344 --
345 - Signals
346
347 ???
348 As far as my program is concerned, it can receive a signal or message from another program at any time.
349
350 --
351 - Timer
352
353 ???
354 If I need something to happen to happen in five minutes or at a specific absolute time, using the idea of an alarm clock
355 is tempting. I can set an alarm and pretend that the clock itself is a source of events.
356
357 --
358 - User input
359
360 ???
361 Human beings of course are masters of spontaneity.
362
363 Are they going to press a button on the keyboard next, or move the mouse? If my program is connected to a microphone,
364 maybe the human is going to start talking to the program. The program has to be ready for anything, so defining and
365 accepting "events" for all the different ways that a human can interact with the program is a good way to go.
366
367 --
368 - Anything that can happen spontaneously in the real world.
369
370 ???
371 Lots of other external systems (besides humans) can "generate" events.
372
373 Lot of this requires kernel facilities. Speaking of which, how are these types of things implemented?
374
375 ---
376 class: syscalls
377
378 ## How event-driven userspace code works
379
380 ### syscalls
381
382 - [`pause`](http://man.he.net/man2/pause) - Sleeps until signal
383
384 --
385 - [`select`](http://man.he.net/man2/select), [`poll`](http://man.he.net/man2/poll), [`epoll`](http://man.he.net/man7/epoll), [`kqueue`](https://www.freebsd.org/cgi/man.cgi?format=ascii&sektion=2&query=kqueue) - Monitor multiple file descriptors
386
387 --
388 - [`clock_gettime`](http://man.he.net/man2/clock_gettime) - What time is it now?
389
390 ---
391 class: center, middle
392
393 ## Reactor pattern
394
395 ---
396 ## Reactor pattern
397
398 .big[
399 - Queues events asynchronously.
400 - Demultiplexes and dispatches synchronously.
401 ]
402
403 ---
404 name: graph-reactor
405 class: center, middle
406
407 ## Reactor pattern
408
409 ![Reactor](img/reactor.svg)
410
411 ---
412 class: ex-basicreactor1
413
414 ## Using a reactor
415
416 ```perl
417 my $reactor = My::Reactor->new;
418
419 # Set up event handlers
420 $reactor->slurp_file($filepath, \&handle_slurp_event);
421 $reactor->timer(5, \&handle_timer_event);
422 $reactor->listen($socket, \&handle_new_connect_event);
423 ...
424
425 $reactor->run_loop;
426 ```
427
428 ---
429 class: ex-basicreactor2
430
431 ## The basic reactor implementation
432
433 ```perl
434 our $timers = [...];
435 our $io_handles = [...];
436
437 while (1) {
438 my $next_timer = find_next_timer($timers);
439
440 poll($io_handles, $next_timer->time_from_now);
441
442 handle_ready_io_handles($io_handles);
443 handle_expired_timers($timers);
444 }
445 ```
446
447 ---
448 class: ex-basicreactor2
449
450 ## The basic reactor implementation
451
452 ```perl
453 our $timers = [...];
454 our $io_handles = [...];
455
456 while (1) {
457 my $next_timer = find_next_timer($timers);
458
459 * poll($io_handles, $next_timer->time_from_now);
460
461 handle_ready_io_handles($io_handles);
462 handle_expired_timers($timers);
463 }
464 ```
465
466 ---
467
468 ## Reactor examples on CPAN
469
470 .big[
471 - [`POE::Loop::IO_Poll`](https://metacpan.org/source/POE::Loop::IO_Poll)
472 - [`POE::Loop::Select`](https://metacpan.org/source/POE::Loop::Select)
473 - [`AnyEvent::Loop`](https://metacpan.org/source/AnyEvent::Loop)
474 - [`IO::Async::Loop::Poll`](https://metacpan.org/source/IO::Async::Loop::Poll)
475 - [`IO::Async::Loop::Select`](https://metacpan.org/source/IO::Async::Loop::Select)
476 - [`Mojo::Reactor::Poll`](https://metacpan.org/source/Mojo::Reactor::Poll)
477 ]
478
479 ???
480 These links, which will be available to you with the slides, link directly to the source code of these modules on
481 metacpan so you can take a look at how they work.
482
483 ---
484 name: not-all-roses
485 class: center, middle
486
487 ![Thorns](img/thorn.jpg)
488
489 ## Watch out for the thorns...
490
491 ???
492 There are some special considerations you need to take when writing event-driven code.
493
494 ---
495 class: center, middle
496
497 ## Exceptions for error handling
498
499 ---
500 class: center, middle
501
502 ### Problem: No exception handler up the call stack
503
504 ---
505 class: ex-exceptions
506
507 ## Rule: Don't die/throw in event handlers.
508
509 --
510 ### Error callback pattern
511
512 ```perl
513 do_something_asynchronously(
514 callback => sub { ... },
515 on_error => sub { ... },
516 );
517 ```
518
519 ---
520 class: ex-exceptions
521
522 ## Rule: Don't die/throw in event handlers.
523
524 ### Use promises
525
526 ```perl
527 my $promise = do_something_asynchronously();
528
529 $promise->on_done(sub { ... });
530 $promise->on_fail(sub { ... });
531 ```
532
533 ---
534 class: center, middle
535
536 ## `SIGPIPE`
537
538 ---
539 class: sigpipe
540 ## `SIGPIPE`
541
542 - Sent to your program when it writes to a pipe that was closed.
543
544 --
545 - Default signal handler terminates the program.
546
547 ---
548 class: ex-sigpipe
549
550 ## Solution: Ignore `SIGPIPE`
551
552 ```perl
553 $SIG{PIPE} = 'IGNORE';
554 ```
555
556 ???
557 Some event loops do this for you.
558
559 --
560 .big[
561 Look for `EPIPE` from syscalls (like [`write`](http://man.he.net/man2/write)) instead.
562
563 (You *are* checking return codes from your system calls... right?)
564 ]
565
566 ---
567 class: center, middle
568
569 ## Use [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
570
571 ???
572 If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
573
574 ---
575 class: center, middle
576
577 ### Yes, Perl can do it, too!
578
579 ---
580 class: ex-asyncawait
581
582 ## Without async and await
583
584 ```perl
585 use Future;
586
587 sub do_two_things {
588 return do_first_thing()->then(sub {
589 my $first = shift;
590
591 return do_second_thing($first)->then(sub {
592 my $second = shift;
593
594 return Future->done([$first, $second]);
595 });
596 });
597 }
598 ```
599
600 ---
601 class: ex-asyncawait
602
603 ## With async and await
604
605 ```perl
606 use Future::AsyncAwait;
607
608 async sub do_two_things
609 {
610 my $first = await do_first_thing();
611
612 my $second = await do_second_thing($first);
613
614 return [$first, $second];
615 }
616 ```
617
618 ???
619 There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
620 ---
621
622 ## Events in the world
623
624
625
626 ---
627 class: center, middle
628 name: conclusion
629
630 ## Conclusion:
631
632 ### Perl is fun.
633
634 ---
635 class: center, middle
636 name: last
637
638 ### Thanks.
639
640 </textarea><script src="https://gnab.github.io/remark/downloads/remark-latest.min.js"></script><script>var slideshow = remark.create({countIncrementalSlides: true, highlightLanguage: '', highlightLines: true, highlightStyle: 'hybrid', ratio: '16:9', /*slideNumberFormat: '',*/ navigation: {scroll: false, touch: false, click: false}})</script><script src="js/common.js"></script><script src="js/slides.js"></script></body></html>
641 <!-- vim: set ts=4 sts=4 sw=4 tw=120 et ft=markdown nowrap: -->
This page took 0.055866 seconds and 3 git commands to generate.