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. --- 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? --- ## Reactor pattern .big[ - Queues events asynchronously. - Demultiplexes and dispatches synchronously. ] --- name: graph-reactor class: center, middle ## Reactor pattern ![Reactor](img/reactor.svg) --- class: ex-basicreactor ## The basic reactor ```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); } ``` --- class: ex-basicreactor ## The basic reactor ```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) ] --- class: center, middle ## Use [`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. --- ## Events in the world --- class: center, middle name: conclusion ## Conclusion: ### Perl is fun. --- class: center, middle name: last ### Thanks.