]> Dogcows Code - chaz/talk-event-driven-programming-in-perl/blobdiff - slides.html
add final slides before presentation
[chaz/talk-event-driven-programming-in-perl] / slides.html
index 6d589b4552d0e1edcb157ecabcd8f1e42d7d6b5f..745bf127db87dedbe3ae370155dfe02d9c935c1b 100644 (file)
@@ -70,6 +70,16 @@ 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
@@ -378,7 +388,11 @@ class: syscalls
 - [`clock_gettime`](http://man.he.net/man2/clock_gettime) - What time is it now?
 
 ---
+class: center, middle
+
+## Reactor pattern
 
+---
 ## Reactor pattern
 
 .big[
@@ -395,9 +409,26 @@ class: center, middle
 ![Reactor](img/reactor.svg)
 
 ---
-class: ex-basicreactor
+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
+## The basic reactor implementation
 
 ```perl
 our $timers     = [...];
@@ -413,10 +444,16 @@ while (1) {
 }
 ```
 
+???
+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-basicreactor
+class: ex-basicreactor2
 
-## The basic reactor
+## The basic reactor implementation
 
 ```perl
 our $timers     = [...];
@@ -433,7 +470,6 @@ while (1) {
 ```
 
 ---
-
 ## Reactor examples on CPAN
 
 .big[
@@ -445,6 +481,410 @@ while (1) {
 - [`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
@@ -495,10 +935,191 @@ $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
 
-## Use [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
+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.
@@ -549,11 +1170,99 @@ async sub do_two_things
 
 ???
 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 <strike>programming</strike> 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.
 
-## Events in the world
+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
This page took 0.034983 seconds and 4 git commands to generate.