]> Dogcows Code - chaz/talk-event-driven-programming-in-perl/blob - slides.html
add slides on exception handling
[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 name: graph-eventloop
75 class: center, middle
76
77 ## Event loop
78
79 ![Event loop](img/eventloop.svg)
80
81 ???
82 We'll refine this model as we go.
83
84 ---
85 class: ex-hwinterrupts
86
87 ## Hardware interrupts
88
89 ```bash
90 # cat /proc/interrupts
91 CPU0 CPU1
92 0: 51 0 IR-IO-APIC 2-edge timer
93 1: 685006 5 IR-IO-APIC 1-edge i8042
94 8: 0 0 IR-IO-APIC 8-edge rtc0
95 9: 1724419 6314 IR-IO-APIC 9-fasteoi acpi
96 12: 12300601 138 IR-IO-APIC 12-edge i8042
97 16: 0 0 IR-IO-APIC 16-fasteoi i801_smbus
98 120: 0 0 DMAR-MSI 0-edge dmar0
99 121: 0 0 DMAR-MSI 1-edge dmar1
100 122: 7009890 45112 IR-PCI-MSI 327680-edge xhci_hcd
101 123: 44 3 IR-PCI-MSI 360448-edge mei_me
102 124: 509 0 IR-PCI-MSI 1048576-edge rtsx_pci
103 125: 80130 0 IR-PCI-MSI 2621440-edge nvme0q0, nvme0q1
104 126: 121892439 2961357 IR-PCI-MSI 32768-edge i915
105 127: 49 100 IR-PCI-MSI 514048-edge snd_hda_intel:card0
106 128: 0 79412 IR-PCI-MSI 2621441-edge nvme0q2
107
108 ...
109 ```
110
111 ???
112 When a HW interrupt happens, the appropriate code to run is locate in the interrupt service routine
113
114 ---
115 class: ex-pressenter
116
117 ## Your first event-driven program
118
119 .middle[
120 ```perl
121 print "Press <Enter> to continue.";
122 <STDIN>;
123
124 handle_enter_keypress_event();
125 ```
126 ]
127
128 ---
129 class: ex-pressenter
130
131 ## Your first event-driven program
132
133 .middle[
134 ```perl
135 print "Press <Enter> to continue.";
136 *<STDIN>;
137
138 handle_enter_keypress_event();
139 ```
140 ]
141
142 .big[
143 - Wait for an event
144 ]
145
146 ---
147 class: ex-pressenter
148
149 ## Your first event-driven program
150
151 .middle[
152 ```perl
153 print "Press <Enter> to continue.";
154 *<STDIN>;
155
156 handle_enter_keypress_event();
157 ```
158 ]
159
160 .big[
161 - Wait for an event
162 - Event source
163 ]
164
165 ---
166 class: ex-pressenter
167
168 ## Your first event-driven program
169
170 .middle[
171 ```perl
172 print "Press <Enter> to continue.";
173 <STDIN>;
174
175 *handle_enter_keypress_event();
176 ```
177 ]
178
179 .big[
180 - Wait for an event
181 - Event source
182 - Event handler
183 ]
184
185 ---
186 class: ex-signals
187
188 ## Signals
189
190 ```perl
191 use POSIX;
192
193 $SIG{USR1} = sub { handle_usr1_signal_event() };
194 $SIG{ALRM} = sub { handle_timer_event() };
195 $SIG{QUIT} = sub { exit() };
196
197 alarm(3);
198
199 while (1) { POSIX::pause() }
200 ```
201
202 ---
203 class: ex-signals
204
205 ## Signals
206
207 ```perl
208 use POSIX;
209
210 *$SIG{USR1} = sub { handle_usr1_signal_event() };
211 *$SIG{ALRM} = sub { handle_timer_event() };
212 *$SIG{QUIT} = sub { exit() };
213
214 alarm(3);
215
216 while (1) { POSIX::pause() }
217 ```
218
219 ???
220 Notice that this program is capable of handling more than just one event handler at a time.
221
222 ---
223 class: ex-signals
224
225 ## Signals
226
227 ```perl
228 use POSIX;
229
230 $SIG{USR1} = sub { handle_usr1_signal_event() };
231 $SIG{ALRM} = sub { handle_timer_event() };
232 $SIG{QUIT} = sub { exit() };
233
234 alarm(3);
235
236 *while (1) { POSIX::pause() }
237 ```
238
239 .big[
240 - Program yields time to the kernel
241 ]
242
243 ???
244 This example has a loop, but it's not really doing anything.
245
246 But actually it is doing something very important: It's yielding its processing time to the kernel.
247
248 And kernel informs the program of events.
249
250 You might be able to use `sleep` as well, but some implementations of libc
251 in the past have implemented `sleep` using `alarm`.
252
253 Don't know how common that is nowadays, but old habbits...
254
255 ---
256 class: ex-gui
257
258 ## Graphical user interface example
259
260 ```perl
261 use Gtk3 '-init';
262
263 my $window = Gtk3::Window->new;
264
265 $window->signal_connect('key-press-event' => \&handle_keypress_event);
266 $window->signal_connect('delete-event' => \&Gtk3::main_quit);
267
268 $window->show_all;
269
270 Gtk3::main();
271 ```
272
273 ???
274 A user interface happens to be a good place to use evented code because humans are spontaneous and unpredictable.
275
276 ---
277 class: ex-gui
278
279 ## Graphical user interface example
280
281 ```perl
282 use Gtk3 '-init';
283
284 my $window = Gtk3::Window->new;
285
286 *$window->signal_connect('key-press-event' => \&handle_keypress_event);
287 *$window->signal_connect('delete-event' => \&Gtk3::main_quit);
288
289 $window->show_all;
290
291 Gtk3::main();
292 ```
293
294 .big[
295 - Event handlers
296 ]
297
298 ---
299 class: ex-gui
300
301 ## Graphical user interface example
302
303 ```perl
304 use Gtk3 '-init';
305
306 my $window = Gtk3::Window->new;
307
308 $window->signal_connect('key-press-event' => \&handle_keypress_event);
309 $window->signal_connect('delete-event' => \&Gtk3::main_quit);
310
311 $window->show_all;
312
313 *Gtk3::main();
314 ```
315
316 .big[
317 - Event handlers
318 - Yield and wait for events
319 ]
320
321 ---
322 class: event-types
323
324 ## Types of events
325
326 - IO
327
328 ???
329 Data available on a socket, or a new connection.
330
331 Server or client, data across a wire cannot typically be relied upon to arrive in a predictable fashion, so an
332 event-driven architect makes a lot of sense for network applications.
333
334 --
335 - Signals
336
337 ???
338 As far as my program is concerned, it can receive a signal or message from another program at any time.
339
340 --
341 - Timer
342
343 ???
344 If I need something to happen to happen in five minutes or at a specific absolute time, using the idea of an alarm clock
345 is tempting. I can set an alarm and pretend that the clock itself is a source of events.
346
347 --
348 - User input
349
350 ???
351 Human beings of course are masters of spontaneity.
352
353 Are they going to press a button on the keyboard next, or move the mouse? If my program is connected to a microphone,
354 maybe the human is going to start talking to the program. The program has to be ready for anything, so defining and
355 accepting "events" for all the different ways that a human can interact with the program is a good way to go.
356
357 --
358 - Anything that can happen spontaneously in the real world.
359
360 ???
361 Lots of other external systems (besides humans) can "generate" events.
362
363 Lot of this requires kernel facilities. Speaking of which, how are these types of things implemented?
364
365 ---
366 class: syscalls
367
368 ## How event-driven userspace code works
369
370 ### syscalls
371
372 - [`pause`](http://man.he.net/man2/pause) - Sleeps until signal
373
374 --
375 - [`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
376
377 --
378 - [`clock_gettime`](http://man.he.net/man2/clock_gettime) - What time is it now?
379
380 ---
381
382 ## Reactor pattern
383
384 .big[
385 - Queues events asynchronously.
386 - Demultiplexes and dispatches synchronously.
387 ]
388
389 ---
390 name: graph-reactor
391 class: center, middle
392
393 ## Reactor pattern
394
395 ![Reactor](img/reactor.svg)
396
397 ---
398 class: ex-basicreactor
399
400 ## The basic reactor
401
402 ```perl
403 our $timers = [...];
404 our $io_handles = [...];
405
406 while (1) {
407 my $next_timer = find_next_timer($timers);
408
409 poll($io_handles, $next_timer->time_from_now);
410
411 handle_ready_io_handles($io_handles);
412 handle_expired_timers($timers);
413 }
414 ```
415
416 ---
417 class: ex-basicreactor
418
419 ## The basic reactor
420
421 ```perl
422 our $timers = [...];
423 our $io_handles = [...];
424
425 while (1) {
426 my $next_timer = find_next_timer($timers);
427
428 * poll($io_handles, $next_timer->time_from_now);
429
430 handle_ready_io_handles($io_handles);
431 handle_expired_timers($timers);
432 }
433 ```
434
435 ---
436
437 ## Reactor examples on CPAN
438
439 .big[
440 - [`POE::Loop::IO_Poll`](https://metacpan.org/source/POE::Loop::IO_Poll)
441 - [`POE::Loop::Select`](https://metacpan.org/source/POE::Loop::Select)
442 - [`AnyEvent::Loop`](https://metacpan.org/source/AnyEvent::Loop)
443 - [`IO::Async::Loop::Poll`](https://metacpan.org/source/IO::Async::Loop::Poll)
444 - [`IO::Async::Loop::Select`](https://metacpan.org/source/IO::Async::Loop::Select)
445 - [`Mojo::Reactor::Poll`](https://metacpan.org/source/Mojo::Reactor::Poll)
446 ]
447
448 ---
449 name: not-all-roses
450 class: center, middle
451
452 ![Thorns](img/thorn.jpg)
453
454 ## Watch out for the thorns...
455
456 ???
457 There are some special considerations you need to take when writing event-driven code.
458
459 ---
460 class: center, middle
461
462 ## Exceptions for error handling
463
464 ---
465 class: center, middle
466
467 ### Problem: No exception handler up the call stack
468
469 ---
470 class: ex-exceptions
471
472 ## Rule: Don't die/throw in event handlers.
473
474 --
475 ### Error callback pattern
476
477 ```perl
478 do_something_asynchronously(
479 callback => sub { ... },
480 on_error => sub { ... },
481 );
482 ```
483
484 ---
485 class: ex-exceptions
486
487 ## Rule: Don't die/throw in event handlers.
488
489 ### Use promises
490
491 ```perl
492 my $promise = do_something_asynchronously();
493
494 $promise->on_done(sub { ... });
495 $promise->on_fail(sub { ... });
496 ```
497
498 ---
499 class: center, middle
500
501 ## Use [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
502
503 ???
504 If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
505
506 ---
507 class: center, middle
508
509 ### Yes, Perl can do it, too!
510
511 ---
512 class: ex-asyncawait
513
514 ## Without async and await
515
516 ```perl
517 use Future;
518
519 sub do_two_things {
520 return do_first_thing()->then(sub {
521 my $first = shift;
522
523 return do_second_thing($first)->then(sub {
524 my $second = shift;
525
526 return Future->done([$first, $second]);
527 });
528 });
529 }
530 ```
531
532 ---
533 class: ex-asyncawait
534
535 ## With async and await
536
537 ```perl
538 use Future::AsyncAwait;
539
540 async sub do_two_things
541 {
542 my $first = await do_first_thing();
543
544 my $second = await do_second_thing($first);
545
546 return [$first, $second];
547 }
548 ```
549
550 ???
551 There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
552 ---
553
554 ## Events in the world
555
556
557
558 ---
559 class: center, middle
560 name: conclusion
561
562 ## Conclusion:
563
564 ### Perl is fun.
565
566 ---
567 class: center, middle
568 name: last
569
570 ### Thanks.
571
572 </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>
573 <!-- vim: set ts=4 sts=4 sw=4 tw=120 et ft=markdown nowrap: -->
This page took 0.061429 seconds and 4 git commands to generate.