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 Of course, you don't actually need to know anything about which syscalls are used and how a reactor actually works to do
449 event-driven programming. (and if any of this is going over your head, that's fine.)
451 But I'm covering it because I think it's good for you.
454 class: ex-basicreactor2
456 ## The basic reactor implementation
460 our $io_handles = [...];
463 my $next_timer = find_next_timer($timers);
465 * poll($io_handles, $next_timer-
>time_from_now);
467 handle_ready_io_handles($io_handles);
468 handle_expired_timers($timers);
473 ## Reactor examples on CPAN
476 - [`POE::Loop::IO_Poll`](https://metacpan.org/source/POE::Loop::IO_Poll)
477 - [`POE::Loop::Select`](https://metacpan.org/source/POE::Loop::Select)
478 - [`AnyEvent::Loop`](https://metacpan.org/source/AnyEvent::Loop)
479 - [`IO::Async::Loop::Poll`](https://metacpan.org/source/IO::Async::Loop::Poll)
480 - [`IO::Async::Loop::Select`](https://metacpan.org/source/IO::Async::Loop::Select)
481 - [`Mojo::Reactor::Poll`](https://metacpan.org/source/Mojo::Reactor::Poll)
485 These links, which will be available to you with the slides, link directly to the source code of these modules on
486 metacpan so you can take a look at how they work.
492 - [`Cocoa::EventLoop`](https://metacpan.org/pod/Cocoa::EventLoop)
493 - [`EV`](https://metacpan.org/pod/EV)
494 - [`Event::Lib`](https://metacpan.org/pod/Event::Lib)
495 - [`Event`](https://metacpan.org/pod/Event)
496 - [`FLTK`](https://metacpan.org/pod/FLTK)
499 - [`Glib`](https://metacpan.org/pod/Glib), [`Gtk`](https://metacpan.org/pod/Gtk), [`Gtk2`](https://metacpan.org/pod/Gtk2)
500 - [`Tk`](https://metacpan.org/pod/Tk)
501 - [`UV`](https://metacpan.org/pod/UV)
502 - [`Wx`](https://metacpan.org/pod/Wx)
507 I'm not going to go over any of these.
509 - You can use any one of these directly.
510 - Some of that are better than others (obvious).
511 - Which one you choose may depend one what you're building.
512 - If you're building a GUI, your choice is made for you.
513 - High-concurrency network application, `EV` is a good choice.
515 But actually you may not need to pick one...
518 class: center, middle
520 ## Reactor "front ends"
523 By my count there are four main front ends.
526 ## Reactor "front ends"
529 - [**`POE`**](https://metacpan.org/pod/POE) - Portable multitasking and networking framework for any event loop (or Perl, Objects, and Events)
530 - [**`IO::Async`**](https://metacpan.org/pod/IO::Async) - Asynchronous event-driven programming
531 - [**`Mojo::IOLoop`**](https://metacpan.org/pod/Mojo::IOLoop) - Minimalistic event loop
532 - [**`AnyEvent`**](https://metacpan.org/pod/AnyEvent) - The DBI of event loop programming
536 The benefit of using one of these rather than the reactors themselves is that your code will automatically work with any
537 of a number of supported reactors.
546 use Time::HiRes qw(time);
548 POE::Session-
>create(
551 $_[KERNEL]-
>delay(tick =
> 5);
554 tick =
> \&handle_timer_event,
563 - The oldest, released
1998
564 - Parallel processing was it's primary use.
573 use Time::HiRes qw(time);
575 POE::Session-
>create(
578 $_[KERNEL]-
>delay(tick =
> 5);
581 tick =
> \&handle_timer_event,
589 Using `POE` will implicitly load all the modules you'll need to actually do anything.
590 - In this case, `POE::Session` and `POE::Kernel`.
599 use Time::HiRes qw(time);
601 POE::Session-
>create(
604 $_[KERNEL]-
>delay(tick =
> 5);
607 tick =
> \&handle_timer_event,
617 In POE, the kernel is the thing that manages processes AKA sessions.
626 use Time::HiRes qw(time);
628 *POE::Session-
>create(
631 * $_[KERNEL]-
>delay(tick =
> 5);
634 * tick =
> \&handle_timer_event,
643 Sessions can be created to do stuff.
652 use IO::Async::Timer::Countdown;
654 my $loop = IO::Async::Loop-
>new;
656 my $timer = IO::Async::Timer::Countdown-
>new(
657 delay =
> 5, # seconds
658 on_expire =
> \&handle_timer_event,
674 use IO::Async::Timer::Countdown;
676 *my $loop = IO::Async::Loop-
>new;
678 my $timer = IO::Async::Timer::Countdown-
>new(
679 delay =
> 5, # seconds
680 on_expire =
> \&handle_timer_event,
691 - IO::Async doesn't seem to have a concept of a "default" loop.
692 - The user has control over starting the loop, as usual, but it does mean that modules can't really register
702 use IO::Async::Timer::Countdown;
704 my $loop = IO::Async::Loop-
>new;
706 my $timer = IO::Async::Timer::Countdown-
>new(
707 delay =
> 5, # seconds
708 on_expire =
> \&handle_timer_event,
718 Add your handler to the loop, and of course run the loop.
727 use IO::Async::Timer::Countdown;
729 my $loop = IO::Async::Loop-
>new;
731 my $timer = IO::Async::Timer::Countdown-
>new(
732 delay =
> 5, # seconds
733 on_expire =
> \&handle_timer_event,
743 Remember to actually start your timer! The timer facilities in other loops seem to do this for you, but this does give you more control.
744 - For example, you can call `reset` on the timer to set it back it it's initial delay value.
754 Mojo::IOLoop-
>timer(
5 =
> \&handle_timer_event);
756 Mojo::IOLoop-
>start if ! Mojo::IOLoop-
>is_running;
767 *Mojo::IOLoop-
>timer(
5 =
> \&handle_timer_event);
769 Mojo::IOLoop-
>start if ! Mojo::IOLoop-
>is_running;
773 Create the timer... easy.
783 Mojo::IOLoop-
>timer(
5 =
> \&handle_timer_event);
785 *Mojo::IOLoop-
>start if ! Mojo::IOLoop-
>is_running;
789 And start the loop. Very easy.
791 - `Mojo::IOLoop` provides methods for creating network clients and servers without much code.
792 - Doesn't seem to have support for demultiplexing signals, but if this is important you can probably set that up
793 directly with the `EV` reactor back end.
804 my $timer = AE::timer(
5,
0 =
> \&handle_timer_event);
818 my $timer = AE::timer(
5,
0 =
> \&handle_timer_event);
832 *my $loop = Glib::MainLoop-
>new;
834 my $timer = AE::timer(
5,
0 =
> \&handle_timer_event);
848 my $loop = Glib::MainLoop-
>new;
850 *my $timer = AE::timer(
5,
0 =
> \&handle_timer_event);
858 By the way, the second argument to `AE::timer` represents the interval if you want a periodic timer.
860 - Remember to save the return value of setting up your timer and other handles.
861 - That thing is actually called a "watcher", and the destruction of watcher objects is how event handlers get
864 `AnyEvent` technically has two APIs. This is the "simplified" API.
875 my $loop = Glib::MainLoop-
>new;
877 *my $timer = AnyEvent-
>timer(
879 * cb =
> \&handle_timer_event,
886 This is what the "complicated" API looks like.
890 class: center, middle
892 ![Thorns](img/thorn.jpg)
894 ## Watch out for the thorns...
897 There are some special considerations you need to take when writing event-driven code.
900 class: center, middle
902 ## Exceptions for error handling
905 class: center, middle
907 ### Problem: No exception handler up the call stack
912 ## Rule: Don't die/throw in event handlers.
915 ### Error callback pattern
918 do_something_asynchronously(
919 callback =
> sub { ... },
920 on_error =
> sub { ... },
927 ## Rule: Don't die/throw in event handlers.
932 my $promise = do_something_asynchronously();
934 $promise-
>on_done(sub { ... });
935 $promise-
>on_fail(sub { ... });
942 class: center, middle
950 - Sent to your program when it writes to a pipe that was closed.
953 - Default signal handler terminates the program.
958 ## Solution: Ignore `SIGPIPE`
961 $SIG{PIPE} = 'IGNORE';
965 Some event loops do this for you.
969 Look for `EPIPE` from syscalls (like [`write`](http://man.he.net/man2/write)) instead.
971 (You *are* checking return codes from your system calls... right?)
975 class: center, middle
977 ## Debugging event-driven code
982 ## Debugging event-driven code
985 - Print debug info to `STDERR`.
989 export PERL_FUTURE_DEBUG=
1
991 export ANYEVENT_DEBUG=
8
993 export MOJO_IOLOOP_DEBUG=
1
998 ## Debugging event-driven code
1001 - Print debug info to `STDERR`.
1002 - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
1006 ## Debugging event-driven code
1009 - Print debug info to `STDERR`.
1010 - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
1011 - Use [`perl5db.pl`](https://metacpan.org/pod/perl5db.pl) and other `Devel::` debuggers.
1015 Traditional debuggers are still useful for event-driven code.
1016 - Be sure to carefully avoid memory leaks -- they are more devastating in long-lived programs which event-driven
1017 programs tend to be.
1020 class: center, middle
1023 ### Proxy objects for future values
1026 Proxy objects for values that have not yet been retrieved or computed.
1028 - In some cases, promises can provide you an alternative, often more useful interface to callbacks.
1033 ## Basic usage (using [`Future`](https://metacpan.org/pod/Future))
1036 my $future = fetch_remote_file($url);
1038 $future-
>on_done(sub($filename, $contents) {
1039 print "Fetched $filename\n";
1044 The future is an object that stands in for the thing we actually want until the thing we want is available.
1048 $future-
>on_fail(sub($error) {
1054 If the operation that provides the `Future` isn't able to fulfill its promise to provide us what we want, the `Future`
1055 may be set to a "fail" state.
1057 - For any operation that can fail (and that includes almost everything), it's a good idea to handle errors.
1065 my $future = fetch_remote_file($url)
1066 -
>then(sub($filename, $contents) {
1067 my $another_future = write_local_file("/tmp/$filename",
1069 return $another_future;
1072 $future-
>on_done(sub($filepath) {
1073 print "Saved file to $filepath\n";
1076 $future-
>on_fail(sub($error) {
1090 sub fetch_remote_file($url) {
1091 my $future = Future-
>new;
1093 http_get $url =
> sub($data, $headers) {
1094 if ($headers-
>{Status} =~ /^[
23]/) {
1095 my ($filename) = get_filename($headers);
1096 $future-
>done($filename, $data);
1099 $future-
>fail("Failed to fetch file: $headers-
>{Reason}");
1108 class: center, middle
1110 Check out [Paul Evan's blog](http://leonerds-code.blogspot.com/
2013/
12/futures-advent-day-
1.html) for more things you can do with `Future`s.
1112 And, of course, [read the pod](https://metacpan.org/pod/Future).
1115 Paul did an advent calendar with short posts detailing the things you can do with `Future`.
1117 - It's really well done.
1120 class: center, middle
1122 ## There's also [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
1125 If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
1128 class: center, middle
1130 ### Yes, Perl can do it, too!
1133 class: ex-asyncawait
1135 ## Without async and await
1141 return do_first_thing()-
>then(sub {
1144 return do_second_thing($first)-
>then(sub {
1147 return Future-
>done([$first, $second]);
1154 class: ex-asyncawait
1156 ## With async and await
1159 use Future::AsyncAwait;
1161 async sub do_two_things
1163 my $first = await do_first_thing();
1165 my $second = await do_second_thing($first);
1167 return [$first, $second];
1172 There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
1175 class: center, middle
1177 ## Finally, there's also [`Future::Utils`](https://metacpan.org/pod/Future::Utils).
1195 Any exceptions throw in the code block are caught and become a failed `Future`.
1203 use Future::Utils qw(repeat);
1205 my $eventual_future = repeat {
1206 my $trial_future = ...
1207 return $trial_future;
1208 } while =
> sub($last_trial_future) { return should_keep_going($last_trial_future) };
1212 class: center, middle
1217 class: center, middle
1219 ### Proactor pattern
1222 We've gone over the Reactor pattern quite a bit, and for good reason.
1224 It's the predominant implementation of event-driven code in userspace apps.
1226 But you should also know about the Proactor pattern.
1228 It's basically the same as the reactor pattern except the event handlers perform asynchronous operations.
1229 - Can be done using special kernel facilities
1230 - or by keeping a thread pool.
1232 One thing to keep in mind about the reactor pattern is that any sort of blocking that occurs by any event handlers will
1233 slow everything down quite a bit. The proactor pattern can help avoid problems.
1236 class: center, middle
1238 ### Event-driven
<strike>programming
</strike> architecture
1241 Making your apps reactive and able to generate and respond to events is the tip of a very large iceburg.
1243 - Pubsub systems can be used to distribute events at a massive scale.
1244 - Message queues are components that can be plugged in to provide guaranteed delivery of events.
1246 Once your programs are event-driven, they're ready to be plugged into a whole world of other services.
1249 class: center, middle
1251 ### Lots of interconnected devices
1254 This concept has a trendy name, but I can't say it because I've swarn off buzzwords.
1256 Event-driven programming and architecture is used as a foundation for building APIs and applications that scale, if
1257 that's important to you.
1259 You can avoid coupling: When one of your devices has an event, it just needs to notify your world of devices and let the
1260 devices decide what to do.
1262 For example, if your car has connected sensors, it can notify your devices when you leave the car. Then your phone can
1263 receive that event and notify you that you left your kid in the car.
1265 So there are a lot of cool applications for this stuff.
1268 class: center, middle
1276 class: center, middle
1281 </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>
1282 <!-- vim: set ts=4 sts=4 sw=4 tw=120 et ft=markdown nowrap: -->