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.
79 ![Event loop](img/eventloop.svg)
82 We'll refine this model as we go.
85 class: ex-hwinterrupts
87 ## Hardware interrupts
90 # cat /proc/interrupts
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
112 When a HW interrupt happens, the appropriate code to run is locate in the interrupt service routine
117 ## Your first event-driven program
121 print "Press
<Enter> to continue.";
124 handle_enter_keypress_event();
131 ## Your first event-driven program
135 print "Press
<Enter> to continue.";
138 handle_enter_keypress_event();
149 ## Your first event-driven program
153 print "Press
<Enter> to continue.";
156 handle_enter_keypress_event();
168 ## Your first event-driven program
172 print "Press
<Enter> to continue.";
175 *handle_enter_keypress_event();
193 $SIG{USR1} = sub { handle_usr1_signal_event() };
194 $SIG{ALRM} = sub { handle_timer_event() };
195 $SIG{QUIT} = sub { exit() };
199 while (
1) { POSIX::pause() }
210 *$SIG{USR1} = sub { handle_usr1_signal_event() };
211 *$SIG{ALRM} = sub { handle_timer_event() };
212 *$SIG{QUIT} = sub { exit() };
216 while (
1) { POSIX::pause() }
220 Notice that this program is capable of handling more than just one event handler at a time.
230 $SIG{USR1} = sub { handle_usr1_signal_event() };
231 $SIG{ALRM} = sub { handle_timer_event() };
232 $SIG{QUIT} = sub { exit() };
236 *while (
1) { POSIX::pause() }
240 - Program yields time to the kernel
244 This example has a loop, but it's not really doing anything.
246 But actually it is doing something very important: It's yielding its processing time to the kernel.
248 And kernel informs the program of events.
250 You might be able to use `sleep` as well, but some implementations of libc
251 in the past have implemented `sleep` using `alarm`.
253 Don't know how common that is nowadays, but old habbits...
258 ## Graphical user interface example
263 my $window = Gtk3::Window-
>new;
265 $window-
>signal_connect('key-press-event' =
> \&handle_keypress_event);
266 $window-
>signal_connect('delete-event' =
> \&Gtk3::main_quit);
274 A user interface happens to be a good place to use evented code because humans are spontaneous and unpredictable.
279 ## Graphical user interface example
284 my $window = Gtk3::Window-
>new;
286 *$window-
>signal_connect('key-press-event' =
> \&handle_keypress_event);
287 *$window-
>signal_connect('delete-event' =
> \&Gtk3::main_quit);
301 ## Graphical user interface example
306 my $window = Gtk3::Window-
>new;
308 $window-
>signal_connect('key-press-event' =
> \&handle_keypress_event);
309 $window-
>signal_connect('delete-event' =
> \&Gtk3::main_quit);
318 - Yield and wait for events
329 Data available on a socket, or a new connection.
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.
338 As far as my program is concerned, it can receive a signal or message from another program at any time.
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.
351 Human beings of course are masters of spontaneity.
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.
358 - Anything that can happen spontaneously in the real world.
361 Lots of other external systems (besides humans) can "generate" events.
363 Lot of this requires kernel facilities. Speaking of which, how are these types of things implemented?
368 ## How event-driven userspace code works
372 - [`pause`](http://man.he.net/man2/pause) - Sleeps until signal
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
378 - [`clock_gettime`](http://man.he.net/man2/clock_gettime) - What time is it now?
385 - Queues events asynchronously.
386 - Demultiplexes and dispatches synchronously.
391 class: center, middle
395 ![Reactor](img/reactor.svg)
398 class: ex-basicreactor
404 our $io_handles = [...];
407 my $next_timer = find_next_timer($timers);
409 poll($io_handles, $next_timer-
>time_from_now);
411 handle_ready_io_handles($io_handles);
412 handle_expired_timers($timers);
417 class: ex-basicreactor
423 our $io_handles = [...];
426 my $next_timer = find_next_timer($timers);
428 * poll($io_handles, $next_timer-
>time_from_now);
430 handle_ready_io_handles($io_handles);
431 handle_expired_timers($timers);
437 ## Reactor examples on CPAN
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)
450 class: center, middle
452 ![Thorns](img/thorn.jpg)
454 ## Watch out for the thorns...
457 There are some special considerations you need to take when writing event-driven code.
460 class: center, middle
462 ## Exceptions for error handling
465 class: center, middle
467 ### Problem: No exception handler up the call stack
472 ## Rule: Don't die/throw in event handlers.
475 ### Error callback pattern
478 do_something_asynchronously(
479 callback =
> sub { ... },
480 on_error =
> sub { ... },
487 ## Rule: Don't die/throw in event handlers.
492 my $promise = do_something_asynchronously();
494 $promise-
>on_done(sub { ... });
495 $promise-
>on_fail(sub { ... });
499 class: center, middle
501 ## Use [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
504 If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
507 class: center, middle
509 ### Yes, Perl can do it, too!
514 ## Without async and await
520 return do_first_thing()-
>then(sub {
523 return do_second_thing($first)-
>then(sub {
526 return Future-
>done([$first, $second]);
535 ## With async and await
538 use Future::AsyncAwait;
540 async sub do_two_things
542 my $first = await do_first_thing();
544 my $second = await do_second_thing($first);
546 return [$first, $second];
551 There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
554 ## Events in the world
559 class: center, middle
567 class: center, middle
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: -->