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);
467 ## Reactor examples on CPAN
470 - [`POE::Loop::IO_Poll`](https://metacpan.org/source/POE::Loop::IO_Poll)
471 - [`POE::Loop::Select`](https://metacpan.org/source/POE::Loop::Select)
472 - [`AnyEvent::Loop`](https://metacpan.org/source/AnyEvent::Loop)
473 - [`IO::Async::Loop::Poll`](https://metacpan.org/source/IO::Async::Loop::Poll)
474 - [`IO::Async::Loop::Select`](https://metacpan.org/source/IO::Async::Loop::Select)
475 - [`Mojo::Reactor::Poll`](https://metacpan.org/source/Mojo::Reactor::Poll)
479 These links, which will be available to you with the slides, link directly to the source code of these modules on
480 metacpan so you can take a look at how they work.
486 - [`Cocoa::EventLoop`](https://metacpan.org/pod/Cocoa::EventLoop)
487 - [`EV`](https://metacpan.org/pod/EV)
488 - [`Event::Lib`](https://metacpan.org/pod/Event::Lib)
489 - [`Event`](https://metacpan.org/pod/Event)
490 - [`FLTK`](https://metacpan.org/pod/FLTK)
493 - [`Glib`](https://metacpan.org/pod/Glib), [`Gtk`](https://metacpan.org/pod/Gtk), [`Gtk2`](https://metacpan.org/pod/Gtk2)
494 - [`Tk`](https://metacpan.org/pod/Tk)
495 - [`UV`](https://metacpan.org/pod/UV)
496 - [`Wx`](https://metacpan.org/pod/Wx)
501 I'm not going to go over any of these.
503 - You can use any one of these directly.
504 - Some of that are better than others (obvious).
505 - Which one you choose may depend one what you're building.
506 - If you're building a GUI, your choice is made for you.
507 - High-concurrency network application, `EV` is a good choice.
509 But actually you may not need to pick one...
512 class: center, middle
514 ## Reactor "front ends"
517 By my count there are four main front ends.
520 ## Reactor "front ends"
523 - [**`POE`**](https://metacpan.org/pod/POE) - Portable multitasking and networking framework for any event loop (or Perl, Objects, and Events)
524 - [**`IO::Async`**](https://metacpan.org/pod/IO::Async) - Asynchronous event-driven programming
525 - [**`Mojo::IOLoop`**](https://metacpan.org/pod/Mojo::IOLoop) - Minimalistic event loop
526 - [**`AnyEvent`**](https://metacpan.org/pod/AnyEvent) - The DBI of event loop programming
530 The benefit of using one of these rather than the reactors themselves is that your code will automatically work with any
531 of a number of supported reactors.
534 - The oldest, released
1998
535 - Parallel processing was it's primary use.
536 - Everything center by the "wheel" (loop).
537 - You add "handles" to the object.
538 - Each type of handle has certain events it knows how to handle.
542 class: center, middle
544 ![Thorns](img/thorn.jpg)
546 ## Watch out for the thorns...
549 There are some special considerations you need to take when writing event-driven code.
552 class: center, middle
554 ## Exceptions for error handling
557 class: center, middle
559 ### Problem: No exception handler up the call stack
564 ## Rule: Don't die/throw in event handlers.
567 ### Error callback pattern
570 do_something_asynchronously(
571 callback =
> sub { ... },
572 on_error =
> sub { ... },
579 ## Rule: Don't die/throw in event handlers.
584 my $promise = do_something_asynchronously();
586 $promise-
>on_done(sub { ... });
587 $promise-
>on_fail(sub { ... });
594 class: center, middle
602 - Sent to your program when it writes to a pipe that was closed.
605 - Default signal handler terminates the program.
610 ## Solution: Ignore `SIGPIPE`
613 $SIG{PIPE} = 'IGNORE';
617 Some event loops do this for you.
621 Look for `EPIPE` from syscalls (like [`write`](http://man.he.net/man2/write)) instead.
623 (You *are* checking return codes from your system calls... right?)
627 class: center, middle
629 ## Debugging event-driven code
634 ## Debugging event-driven code
637 - Print debug info to `STDERR`.
641 export PERL_FUTURE_DEBUG=
1
643 export ANYEVENT_DEBUG=
8
645 export MOJO_IOLOOP_DEBUG=
1
650 ## Debugging event-driven code
653 - Print debug info to `STDERR`.
654 - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
658 ## Debugging event-driven code
661 - Print debug info to `STDERR`.
662 - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
663 - Use [`perl5db.pl`](https://metacpan.org/pod/perl5db.pl) and other `Devel::` debuggers.
667 Traditional debuggers are still useful for event-driven code.
668 - Be sure to carefully avoid memory leaks -- they are more devastating in long-lived programs which event-driven
672 class: center, middle
675 ### Proxy objects for future values
678 Proxy objects for values that have not yet been retrieved or computed.
680 - In some cases, promises can provide you an alternative, often more useful interface to callbacks.
685 ## Basic usage (using [`Future`](https://metacpan.org/pod/Future))
688 my $future = fetch_remote_file($url);
690 $future-
>on_done(sub($filename, $contents) {
691 print "Fetched $filename\n";
696 The future is an object that stands in for the thing we actually want until the thing we want is available.
700 $future-
>on_fail(sub($error) {
706 If the operation that provides the `Future` isn't able to fulfill its promise to provide us what we want, the `Future`
707 may be set to a "fail" state.
709 - For any operation that can fail (and that includes almost everything), it's a good idea to handle errors.
717 my $future = fetch_remote_file($url)
718 -
>then(sub($filename, $contents) {
719 my $another_future = write_local_file("/tmp/$filename",
721 return $another_future;
724 $future-
>on_done(sub($filepath) {
725 print "Saved file to $filepath\n";
728 $future-
>on_fail(sub($error) {
742 sub fetch_remote_file($url) {
743 my $future = Future-
>new;
745 http_get $url =
> sub($data, $headers) {
746 if ($headers-
>{Status} =~ /^[
23]/) {
747 my ($filename) = get_filename($headers);
748 $future-
>done($filename, $data);
751 $future-
>fail("Failed to fetch file: $headers-
>{Reason}");
760 class: center, middle
762 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.
764 And, of course, [read the pod](https://metacpan.org/pod/Future).
767 Paul did an advent calendar with short posts detailing the things you can do with `Future`.
769 - It's really well done.
772 class: center, middle
774 ## There's also [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
777 If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
780 class: center, middle
782 ### Yes, Perl can do it, too!
787 ## Without async and await
793 return do_first_thing()-
>then(sub {
796 return do_second_thing($first)-
>then(sub {
799 return Future-
>done([$first, $second]);
808 ## With async and await
811 use Future::AsyncAwait;
813 async sub do_two_things
815 my $first = await do_first_thing();
817 my $second = await do_second_thing($first);
819 return [$first, $second];
824 There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
827 class: center, middle
829 ## Finally, there's also [`Future::Utils`](https://metacpan.org/pod/Future::Utils).
847 Any exceptions throw in the code block are caught and become a failed `Future`.
855 use Future::Utils qw(repeat);
857 my $eventual_future = repeat {
858 my $trial_future = ...
859 return $trial_future;
860 } while =
> sub($last_trial_future) { return should_keep_going($last_trial_future) };
864 ## Events in the world
867 class: center, middle
875 class: center, middle
880 </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>
881 <!-- vim: set ts=4 sts=4 sw=4 tw=120 et ft=markdown nowrap: -->