class: center, middle name: title # Event-driven Programming in Perl Charles McGarvey ??? - Hi. I'm Charles McGarvey. - Been a Perl programmer for a long time... --- class: center, middle name: bluehost ??? - My employer is hiring. - It's a pretty cool employer... --- ## "Normal" [userspace] programs .big[ 1. Parse args. 2. Read input. 3. Do stuff. 4. Write results as output. ] --- class: pizza background-image: url(img/pizza.jpg) background-size: 100% -- ```bash # orderpizza --crust-type=thin \ --toppings=cheese,pepperoni,mushrooms ``` -- ```perl # source code of the orderpizza program my $order = get_order_from_args(@ARGV); my $ingredients = gather_ingredients($order); my $pizza = prepare_and_bake($order, $ingredentials); print($pizza); ``` ??? But some programs are long-lived. --- ## Event-driven programs .big[ 1. Events happen. 2. When events happen, some code runs and does something. ] ??? At it's core, event-driven programming is this simple. But there are some complications and things to knows, which is why this talk exists. --- class: center, middle ![Help desk](img/helpdesk.jpg) .small[ Image by Soniachat8. 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. ] --- name: graph-eventloop class: center, middle ## Event loop ![Event loop](img/eventloop.svg) ??? We'll refine this model as we go. --- class: ex-hwinterrupts ## Hardware interrupts ```bash # cat /proc/interrupts CPU0 CPU1 0: 51 0 IR-IO-APIC 2-edge timer 1: 685006 5 IR-IO-APIC 1-edge i8042 8: 0 0 IR-IO-APIC 8-edge rtc0 9: 1724419 6314 IR-IO-APIC 9-fasteoi acpi 12: 12300601 138 IR-IO-APIC 12-edge i8042 16: 0 0 IR-IO-APIC 16-fasteoi i801_smbus 120: 0 0 DMAR-MSI 0-edge dmar0 121: 0 0 DMAR-MSI 1-edge dmar1 122: 7009890 45112 IR-PCI-MSI 327680-edge xhci_hcd 123: 44 3 IR-PCI-MSI 360448-edge mei_me 124: 509 0 IR-PCI-MSI 1048576-edge rtsx_pci 125: 80130 0 IR-PCI-MSI 2621440-edge nvme0q0, nvme0q1 126: 121892439 2961357 IR-PCI-MSI 32768-edge i915 127: 49 100 IR-PCI-MSI 514048-edge snd_hda_intel:card0 128: 0 79412 IR-PCI-MSI 2621441-edge nvme0q2 ... ``` ??? When a HW interrupt happens, the appropriate code to run is locate in the interrupt service routine --- class: ex-pressenter ## Your first event-driven program .middle[ ```perl print "Press
to continue.";
; handle_enter_keypress_event(); ``` ] --- class: ex-pressenter ## Your first event-driven program .middle[ ```perl print "Press
to continue."; *
; handle_enter_keypress_event(); ``` ] .big[ - Wait for an event ] --- class: ex-pressenter ## Your first event-driven program .middle[ ```perl print "Press
to continue."; *
; handle_enter_keypress_event(); ``` ] .big[ - Wait for an event - Event source ] --- class: ex-pressenter ## Your first event-driven program .middle[ ```perl print "Press
to continue.";
; *handle_enter_keypress_event(); ``` ] .big[ - Wait for an event - Event source - Event handler ] --- class: ex-signals ## Signals ```perl use POSIX; $SIG{USR1} = sub { handle_usr1_signal_event() }; $SIG{ALRM} = sub { handle_timer_event() }; $SIG{QUIT} = sub { exit() }; alarm(3); while (1) { POSIX::pause() } ``` --- class: ex-signals ## Signals ```perl use POSIX; *$SIG{USR1} = sub { handle_usr1_signal_event() }; *$SIG{ALRM} = sub { handle_timer_event() }; *$SIG{QUIT} = sub { exit() }; alarm(3); while (1) { POSIX::pause() } ``` ??? Notice that this program is capable of handling more than just one event handler at a time. --- class: ex-signals ## Signals ```perl use POSIX; $SIG{USR1} = sub { handle_usr1_signal_event() }; $SIG{ALRM} = sub { handle_timer_event() }; $SIG{QUIT} = sub { exit() }; alarm(3); *while (1) { POSIX::pause() } ``` .big[ - Program yields time to the kernel ] ??? This example has a loop, but it's not really doing anything. But actually it is doing something very important: It's yielding its processing time to the kernel. And kernel informs the program of events. You might be able to use `sleep` as well, but some implementations of libc in the past have implemented `sleep` using `alarm`. Don't know how common that is nowadays, but old habbits... --- class: ex-gui ## Graphical user interface example ```perl use Gtk3 '-init'; my $window = Gtk3::Window->new; $window->signal_connect('key-press-event' => \&handle_keypress_event); $window->signal_connect('delete-event' => \&Gtk3::main_quit); $window->show_all; Gtk3::main(); ``` ??? A user interface happens to be a good place to use evented code because humans are spontaneous and unpredictable. --- class: ex-gui ## Graphical user interface example ```perl use Gtk3 '-init'; my $window = Gtk3::Window->new; *$window->signal_connect('key-press-event' => \&handle_keypress_event); *$window->signal_connect('delete-event' => \&Gtk3::main_quit); $window->show_all; Gtk3::main(); ``` .big[ - Event handlers ] --- class: ex-gui ## Graphical user interface example ```perl use Gtk3 '-init'; my $window = Gtk3::Window->new; $window->signal_connect('key-press-event' => \&handle_keypress_event); $window->signal_connect('delete-event' => \&Gtk3::main_quit); $window->show_all; *Gtk3::main(); ``` .big[ - Event handlers - Yield and wait for events ] --- class: event-types ## Types of events - IO ??? Data available on a socket, or a new connection. Server or client, data across a wire cannot typically be relied upon to arrive in a predictable fashion, so an event-driven architect makes a lot of sense for network applications. -- - Signals ??? As far as my program is concerned, it can receive a signal or message from another program at any time. -- - Timer ??? If I need something to happen to happen in five minutes or at a specific absolute time, using the idea of an alarm clock is tempting. I can set an alarm and pretend that the clock itself is a source of events. -- - User input ??? Human beings of course are masters of spontaneity. Are they going to press a button on the keyboard next, or move the mouse? If my program is connected to a microphone, maybe the human is going to start talking to the program. The program has to be ready for anything, so defining and accepting "events" for all the different ways that a human can interact with the program is a good way to go. -- - Anything that can happen spontaneously in the real world. ??? Lots of other external systems (besides humans) can "generate" events. Lot of this requires kernel facilities. Speaking of which, how are these types of things implemented? --- class: syscalls ## How event-driven userspace code works ### syscalls - [`pause`](http://man.he.net/man2/pause) - Sleeps until signal -- - [`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 -- - [`clock_gettime`](http://man.he.net/man2/clock_gettime) - What time is it now? --- class: center, middle ## Reactor pattern --- ## Reactor pattern .big[ - Queues events asynchronously. - Demultiplexes and dispatches synchronously. ] --- name: graph-reactor class: center, middle ## Reactor pattern ![Reactor](img/reactor.svg) --- class: ex-basicreactor1 ## Using a reactor ```perl my $reactor = My::Reactor->new; # Set up event handlers $reactor->slurp_file($filepath, \&handle_slurp_event); $reactor->timer(5, \&handle_timer_event); $reactor->listen($socket, \&handle_new_connect_event); ... $reactor->run_loop; ``` --- class: ex-basicreactor2 ## The basic reactor implementation ```perl our $timers = [...]; our $io_handles = [...]; while (1) { my $next_timer = find_next_timer($timers); poll($io_handles, $next_timer->time_from_now); handle_ready_io_handles($io_handles); handle_expired_timers($timers); } ``` ??? Of course, you don't actually need to know anything about which syscalls are used and how a reactor actually works to do event-driven programming. (and if any of this is going over your head, that's fine.) But I'm covering it because I think it's good for you. --- class: ex-basicreactor2 ## The basic reactor implementation ```perl our $timers = [...]; our $io_handles = [...]; while (1) { my $next_timer = find_next_timer($timers); * poll($io_handles, $next_timer->time_from_now); handle_ready_io_handles($io_handles); handle_expired_timers($timers); } ``` --- ## Reactor examples on CPAN .big[ - [`POE::Loop::IO_Poll`](https://metacpan.org/source/POE::Loop::IO_Poll) - [`POE::Loop::Select`](https://metacpan.org/source/POE::Loop::Select) - [`AnyEvent::Loop`](https://metacpan.org/source/AnyEvent::Loop) - [`IO::Async::Loop::Poll`](https://metacpan.org/source/IO::Async::Loop::Poll) - [`IO::Async::Loop::Select`](https://metacpan.org/source/IO::Async::Loop::Select) - [`Mojo::Reactor::Poll`](https://metacpan.org/source/Mojo::Reactor::Poll) ] ??? These links, which will be available to you with the slides, link directly to the source code of these modules on metacpan so you can take a look at how they work. --- ## Reactors .col.big[ - [`Cocoa::EventLoop`](https://metacpan.org/pod/Cocoa::EventLoop) - [`EV`](https://metacpan.org/pod/EV) - [`Event::Lib`](https://metacpan.org/pod/Event::Lib) - [`Event`](https://metacpan.org/pod/Event) - [`FLTK`](https://metacpan.org/pod/FLTK) ] .col.big[ - [`Glib`](https://metacpan.org/pod/Glib), [`Gtk`](https://metacpan.org/pod/Gtk), [`Gtk2`](https://metacpan.org/pod/Gtk2) - [`Tk`](https://metacpan.org/pod/Tk) - [`UV`](https://metacpan.org/pod/UV) - [`Wx`](https://metacpan.org/pod/Wx) - probably more... ] ??? I'm not going to go over any of these. - You can use any one of these directly. - Some of that are better than others (obvious). - Which one you choose may depend one what you're building. - If you're building a GUI, your choice is made for you. - High-concurrency network application, `EV` is a good choice. But actually you may not need to pick one... --- class: center, middle ## Reactor "front ends" ??? By my count there are four main front ends. --- ## Reactor "front ends" .big[ - [**`POE`**](https://metacpan.org/pod/POE) - Portable multitasking and networking framework for any event loop (or Perl, Objects, and Events) - [**`IO::Async`**](https://metacpan.org/pod/IO::Async) - Asynchronous event-driven programming - [**`Mojo::IOLoop`**](https://metacpan.org/pod/Mojo::IOLoop) - Minimalistic event loop - [**`AnyEvent`**](https://metacpan.org/pod/AnyEvent) - The DBI of event loop programming ] ??? The benefit of using one of these rather than the reactors themselves is that your code will automatically work with any of a number of supported reactors. --- class: ex-poe ## `POE` ```perl use POE; use Time::HiRes qw(time); POE::Session->create( inline_states => { _start => sub { $_[KERNEL]->delay(tick => 5); }, tick => \&handle_timer_event, }, ); POE::Kernel->run; ``` ??? POE: - The oldest, released 1998 - Parallel processing was it's primary use. --- class: ex-poe ## `POE` ```perl *use POE; use Time::HiRes qw(time); POE::Session->create( inline_states => { _start => sub { $_[KERNEL]->delay(tick => 5); }, tick => \&handle_timer_event, }, ); POE::Kernel->run; ``` ??? Using `POE` will implicitly load all the modules you'll need to actually do anything. - In this case, `POE::Session` and `POE::Kernel`. --- class: ex-poe ## `POE` ```perl use POE; use Time::HiRes qw(time); POE::Session->create( inline_states => { _start => sub { $_[KERNEL]->delay(tick => 5); }, tick => \&handle_timer_event, }, ); *POE::Kernel->run; ``` ??? Run the reactor. In POE, the kernel is the thing that manages processes AKA sessions. --- class: ex-poe ## `POE` ```perl use POE; use Time::HiRes qw(time); *POE::Session->create( * inline_states => { * _start => sub { * $_[KERNEL]->delay(tick => 5); * }, * * tick => \&handle_timer_event, * }, *); POE::Kernel->run; ``` ??? Sessions can be created to do stuff. --- class: ex-ioasync ## `IO::Async` ```perl use IO::Async::Loop; use IO::Async::Timer::Countdown; my $loop = IO::Async::Loop->new; my $timer = IO::Async::Timer::Countdown->new( delay => 5, # seconds on_expire => \&handle_timer_event, ); $timer->start; $loop->add($timer); $loop->run; ``` --- class: ex-ioasync ## `IO::Async` ```perl use IO::Async::Loop; use IO::Async::Timer::Countdown; *my $loop = IO::Async::Loop->new; my $timer = IO::Async::Timer::Countdown->new( delay => 5, # seconds on_expire => \&handle_timer_event, ); $timer->start; $loop->add($timer); $loop->run; ``` ??? Create the loop - IO::Async doesn't seem to have a concept of a "default" loop. - The user has control over starting the loop, as usual, but it does mean that modules can't really register themselves. --- class: ex-ioasync ## `IO::Async` ```perl use IO::Async::Loop; use IO::Async::Timer::Countdown; my $loop = IO::Async::Loop->new; my $timer = IO::Async::Timer::Countdown->new( delay => 5, # seconds on_expire => \&handle_timer_event, ); $timer->start; *$loop->add($timer); *$loop->run; ``` ??? Add your handler to the loop, and of course run the loop. --- class: ex-ioasync ## `IO::Async` ```perl use IO::Async::Loop; use IO::Async::Timer::Countdown; my $loop = IO::Async::Loop->new; my $timer = IO::Async::Timer::Countdown->new( delay => 5, # seconds on_expire => \&handle_timer_event, ); *$timer->start; $loop->add($timer); $loop->run; ``` ??? 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. - For example, you can call `reset` on the timer to set it back it it's initial delay value. --- class: ex-mojoioloop ## `Mojo::IOLoop` ```perl use Mojo::IOLoop; Mojo::IOLoop->timer(5 => \&handle_timer_event); Mojo::IOLoop->start if ! Mojo::IOLoop->is_running; ``` --- class: ex-mojoioloop ## `Mojo::IOLoop` ```perl use Mojo::IOLoop; *Mojo::IOLoop->timer(5 => \&handle_timer_event); Mojo::IOLoop->start if ! Mojo::IOLoop->is_running; ``` ??? Create the timer... easy. --- class: ex-mojoioloop ## `Mojo::IOLoop` ```perl use Mojo::IOLoop; Mojo::IOLoop->timer(5 => \&handle_timer_event); *Mojo::IOLoop->start if ! Mojo::IOLoop->is_running; ``` ??? And start the loop. Very easy. - `Mojo::IOLoop` provides methods for creating network clients and servers without much code. - Doesn't seem to have support for demultiplexing signals, but if this is important you can probably set that up directly with the `EV` reactor back end. --- class: ex-anyevent ## `AnyEvent` ```perl use EV; use AE; my $timer = AE::timer(5, 0 => \&handle_timer_event); EV::run(); ``` --- class: ex-anyevent ## `AnyEvent` ```perl *use EV; use AE; my $timer = AE::timer(5, 0 => \&handle_timer_event); *EV::run(); ``` --- class: ex-anyevent ## `AnyEvent` ```perl *use Glib; use AE; *my $loop = Glib::MainLoop->new; my $timer = AE::timer(5, 0 => \&handle_timer_event); *$loop->run; ``` --- class: ex-anyevent ## `AnyEvent` ```perl use Glib; use AE; my $loop = Glib::MainLoop->new; *my $timer = AE::timer(5, 0 => \&handle_timer_event); $loop->run; ``` ??? Create your timer. By the way, the second argument to `AE::timer` represents the interval if you want a periodic timer. - Remember to save the return value of setting up your timer and other handles. - That thing is actually called a "watcher", and the destruction of watcher objects is how event handlers get unregistered. `AnyEvent` technically has two APIs. This is the "simplified" API. --- class: ex-anyevent ## `AnyEvent` ```perl use Glib; *use AnyEvent; my $loop = Glib::MainLoop->new; *my $timer = AnyEvent->timer( * after => 5, * cb => \&handle_timer_event, *); $loop->run; ``` ??? This is what the "complicated" API looks like. --- name: not-all-roses class: center, middle ![Thorns](img/thorn.jpg) ## Watch out for the thorns... ??? There are some special considerations you need to take when writing event-driven code. --- class: center, middle ## Exceptions for error handling --- class: center, middle ### Problem: No exception handler up the call stack --- class: ex-exceptions ## Rule: Don't die/throw in event handlers. -- ### Error callback pattern ```perl do_something_asynchronously( callback => sub { ... }, on_error => sub { ... }, ); ``` --- class: ex-exceptions ## Rule: Don't die/throw in event handlers. ### Use promises ```perl my $promise = do_something_asynchronously(); $promise->on_done(sub { ... }); $promise->on_fail(sub { ... }); ``` ??? More on this later. --- class: center, middle ## `SIGPIPE` --- class: sigpipe ## `SIGPIPE` - Sent to your program when it writes to a pipe that was closed. -- - Default signal handler terminates the program. --- class: ex-sigpipe ## Solution: Ignore `SIGPIPE` ```perl $SIG{PIPE} = 'IGNORE'; ``` ??? Some event loops do this for you. -- .big[ Look for `EPIPE` from syscalls (like [`write`](http://man.he.net/man2/write)) instead. (You *are* checking return codes from your system calls... right?) ] --- class: center, middle ## Debugging event-driven code --- class: ex-debugging ## Debugging event-driven code .big[ - Print debug info to `STDERR`. ] ```bash export PERL_FUTURE_DEBUG=1 # and export ANYEVENT_DEBUG=8 # or export MOJO_IOLOOP_DEBUG=1 # etc. ``` --- ## Debugging event-driven code .big[ - Print debug info to `STDERR`. - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug). ] --- ## Debugging event-driven code .big[ - Print debug info to `STDERR`. - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug). - Use [`perl5db.pl`](https://metacpan.org/pod/perl5db.pl) and other `Devel::` debuggers. ] ??? Traditional debuggers are still useful for event-driven code. - Be sure to carefully avoid memory leaks -- they are more devastating in long-lived programs which event-driven programs tend to be. --- class: center, middle ## Promises: ### Proxy objects for future values ??? Proxy objects for values that have not yet been retrieved or computed. - In some cases, promises can provide you an alternative, often more useful interface to callbacks. --- class: ex-future ## Basic usage (using [`Future`](https://metacpan.org/pod/Future)) ```perl my $future = fetch_remote_file($url); $future->on_done(sub($filename, $contents) { print "Fetched $filename\n"; }); ``` ??? The future is an object that stands in for the thing we actually want until the thing we want is available. -- ```perl $future->on_fail(sub($error) { warn $error; }); ``` ??? If the operation that provides the `Future` isn't able to fulfill its promise to provide us what we want, the `Future` may be set to a "fail" state. - For any operation that can fail (and that includes almost everything), it's a good idea to handle errors. --- class: ex-future ## Chaining futures ```perl my $future = fetch_remote_file($url) ->then(sub($filename, $contents) { my $another_future = write_local_file("/tmp/$filename", $contents); return $another_future; }); $future->on_done(sub($filepath) { print "Saved file to $filepath\n"; }); $future->on_fail(sub($error) { warn $error; }); ``` --- class: ex-future2 ## Creating futures ```perl use AnyEvent::HTTP; use Future; sub fetch_remote_file($url) { my $future = Future->new; http_get $url => sub($data, $headers) { if ($headers->{Status} =~ /^[23]/) { my ($filename) = get_filename($headers); $future->done($filename, $data); } else { $future->fail("Failed to fetch file: $headers->{Reason}"); } }; return $future; } ``` --- class: center, middle 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. And, of course, [read the pod](https://metacpan.org/pod/Future). ??? Paul did an advent calendar with short posts detailing the things you can do with `Future`. - It's really well done. --- class: center, middle ## There's also [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait). ??? If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code. --- class: center, middle ### Yes, Perl can do it, too! --- class: ex-asyncawait ## Without async and await ```perl use Future; sub do_two_things { return do_first_thing()->then(sub { my $first = shift; return do_second_thing($first)->then(sub { my $second = shift; return Future->done([$first, $second]); }); }); } ``` --- class: ex-asyncawait ## With async and await ```perl use Future::AsyncAwait; async sub do_two_things { my $first = await do_first_thing(); my $second = await do_second_thing($first); return [$first, $second]; } ``` ??? There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior. --- class: center, middle ## Finally, there's also [`Future::Utils`](https://metacpan.org/pod/Future::Utils). --- class: ex-future2 ## Call ```perl my $future = call { do_stuff(); ... my $future = ...; return $future; }; ``` Any exceptions throw in the code block are caught and become a failed `Future`. --- class: ex-future2 ## Loops and stuff ```perl use Future::Utils qw(repeat); my $eventual_future = repeat { my $trial_future = ... return $trial_future; } while => sub($last_trial_future) { return should_keep_going($last_trial_future) }; ``` --- class: center, middle ## Final thoughts --- class: center, middle ### Proactor pattern ??? We've gone over the Reactor pattern quite a bit, and for good reason. It's the predominant implementation of event-driven code in userspace apps. But you should also know about the Proactor pattern. It's basically the same as the reactor pattern except the event handlers perform asynchronous operations. - Can be done using special kernel facilities - or by keeping a thread pool. One thing to keep in mind about the reactor pattern is that any sort of blocking that occurs by any event handlers will slow everything down quite a bit. The proactor pattern can help avoid problems. --- class: center, middle ### Event-driven
programming
architecture ??? Making your apps reactive and able to generate and respond to events is the tip of a very large iceburg. - Pubsub systems can be used to distribute events at a massive scale. - Message queues are components that can be plugged in to provide guaranteed delivery of events. Once your programs are event-driven, they're ready to be plugged into a whole world of other services. --- class: center, middle ### Lots of interconnected devices ??? This concept has a trendy name, but I can't say it because I've swarn off buzzwords. Event-driven programming and architecture is used as a foundation for building APIs and applications that scale, if that's important to you. You can avoid coupling: When one of your devices has an event, it just needs to notify your world of devices and let the devices decide what to do. For example, if your car has connected sensors, it can notify your devices when you leave the car. Then your phone can receive that event and notify you that you left your kid in the car. So there are a lot of cool applications for this stuff. --- class: center, middle name: conclusion ## Conclusion: ### Perl is fun. --- class: center, middle name: last ### Thanks.