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">
7 # Event-driven Programming in Perl
12 - Hi. I'm Charles McGarvey.
13 - Been a Perl programmer for a long time...
20 - My employer is hiring.
21 - It's a pretty cool employer...
24 ## "Normal" [userspace] programs
30 4. Write results as output.
35 background-image: url(img/pizza.jpg)
40 # orderpizza --crust-type=thin \
41 --toppings=cheese,pepperoni,mushrooms
46 # source code of the orderpizza program
48 my $order = get_order_from_args(@ARGV);
50 my $ingredients = gather_ingredients($order);
52 my $pizza = prepare_and_bake($order, $ingredentials);
58 But some programs are long-lived.
61 ## Event-driven programs
65 2. When events happen, some code runs and does something.
69 At it's core, event-driven programming is this simple.
71 But there are some complications and things to knows, which is why this talk exists.
76 ![Help desk](img/helpdesk.jpg)
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.
89 ![Event loop](img/eventloop.svg)
92 We'll refine this model as we go.
95 class: ex-hwinterrupts
97 ## Hardware interrupts
100 # cat /proc/interrupts
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
122 When a HW interrupt happens, the appropriate code to run is locate in the interrupt service routine
127 ## Your first event-driven program
131 print "Press
<Enter> to continue.";
134 handle_enter_keypress_event();
141 ## Your first event-driven program
145 print "Press
<Enter> to continue.";
148 handle_enter_keypress_event();
159 ## Your first event-driven program
163 print "Press
<Enter> to continue.";
166 handle_enter_keypress_event();
178 ## Your first event-driven program
182 print "Press
<Enter> to continue.";
185 *handle_enter_keypress_event();
203 $SIG{USR1} = sub { handle_usr1_signal_event() };
204 $SIG{ALRM} = sub { handle_timer_event() };
205 $SIG{QUIT} = sub { exit() };
209 while (
1) { POSIX::pause() }
220 *$SIG{USR1} = sub { handle_usr1_signal_event() };
221 *$SIG{ALRM} = sub { handle_timer_event() };
222 *$SIG{QUIT} = sub { exit() };
226 while (
1) { POSIX::pause() }
230 Notice that this program is capable of handling more than just one event handler at a time.
240 $SIG{USR1} = sub { handle_usr1_signal_event() };
241 $SIG{ALRM} = sub { handle_timer_event() };
242 $SIG{QUIT} = sub { exit() };
246 *while (
1) { POSIX::pause() }
250 - Program yields time to the kernel
254 This example has a loop, but it's not really doing anything.
256 But actually it is doing something very important: It's yielding its processing time to the kernel.
258 And kernel informs the program of events.
260 You might be able to use `sleep` as well, but some implementations of libc
261 in the past have implemented `sleep` using `alarm`.
263 Don't know how common that is nowadays, but old habbits...
268 ## Graphical user interface example
273 my $window = Gtk3::Window-
>new;
275 $window-
>signal_connect('key-press-event' =
> \&handle_keypress_event);
276 $window-
>signal_connect('delete-event' =
> \&Gtk3::main_quit);
284 A user interface happens to be a good place to use evented code because humans are spontaneous and unpredictable.
289 ## Graphical user interface example
294 my $window = Gtk3::Window-
>new;
296 *$window-
>signal_connect('key-press-event' =
> \&handle_keypress_event);
297 *$window-
>signal_connect('delete-event' =
> \&Gtk3::main_quit);
311 ## Graphical user interface example
316 my $window = Gtk3::Window-
>new;
318 $window-
>signal_connect('key-press-event' =
> \&handle_keypress_event);
319 $window-
>signal_connect('delete-event' =
> \&Gtk3::main_quit);
328 - Yield and wait for events
339 Data available on a socket, or a new connection.
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.
348 As far as my program is concerned, it can receive a signal or message from another program at any time.
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.
361 Human beings of course are masters of spontaneity.
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.
368 - Anything that can happen spontaneously in the real world.
371 Lots of other external systems (besides humans) can "generate" events.
373 Lot of this requires kernel facilities. Speaking of which, how are these types of things implemented?
378 ## How event-driven userspace code works
382 - [`pause`](http://man.he.net/man2/pause) - Sleeps until signal
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
388 - [`clock_gettime`](http://man.he.net/man2/clock_gettime) - What time is it now?
391 class: center, middle
399 - Queues events asynchronously.
400 - Demultiplexes and dispatches synchronously.
405 class: center, middle
409 ![Reactor](img/reactor.svg)
412 class: ex-basicreactor1
417 my $reactor = My::Reactor-
>new;
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);
429 class: ex-basicreactor2
431 ## The basic reactor implementation
435 our $io_handles = [...];
438 my $next_timer = find_next_timer($timers);
440 poll($io_handles, $next_timer-
>time_from_now);
442 handle_ready_io_handles($io_handles);
443 handle_expired_timers($timers);
448 class: ex-basicreactor2
450 ## The basic reactor implementation
454 our $io_handles = [...];
457 my $next_timer = find_next_timer($timers);
459 * poll($io_handles, $next_timer-
>time_from_now);
461 handle_ready_io_handles($io_handles);
462 handle_expired_timers($timers);
468 ## Reactor examples on CPAN
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)
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.
485 class: center, middle
487 ![Thorns](img/thorn.jpg)
489 ## Watch out for the thorns...
492 There are some special considerations you need to take when writing event-driven code.
495 class: center, middle
497 ## Exceptions for error handling
500 class: center, middle
502 ### Problem: No exception handler up the call stack
507 ## Rule: Don't die/throw in event handlers.
510 ### Error callback pattern
513 do_something_asynchronously(
514 callback =
> sub { ... },
515 on_error =
> sub { ... },
522 ## Rule: Don't die/throw in event handlers.
527 my $promise = do_something_asynchronously();
529 $promise-
>on_done(sub { ... });
530 $promise-
>on_fail(sub { ... });
534 class: center, middle
542 - Sent to your program when it writes to a pipe that was closed.
545 - Default signal handler terminates the program.
550 ## Solution: Ignore `SIGPIPE`
553 $SIG{PIPE} = 'IGNORE';
557 Some event loops do this for you.
561 Look for `EPIPE` from syscalls (like [`write`](http://man.he.net/man2/write)) instead.
563 (You *are* checking return codes from your system calls... right?)
567 class: center, middle
569 ## Use [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
572 If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
575 class: center, middle
577 ### Yes, Perl can do it, too!
582 ## Without async and await
588 return do_first_thing()-
>then(sub {
591 return do_second_thing($first)-
>then(sub {
594 return Future-
>done([$first, $second]);
603 ## With async and await
606 use Future::AsyncAwait;
608 async sub do_two_things
610 my $first = await do_first_thing();
612 my $second = await do_second_thing($first);
614 return [$first, $second];
619 There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
622 ## Events in the world
627 class: center, middle
635 class: center, middle
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: -->