]> Dogcows Code - chaz/talk-event-driven-programming-in-perl/blob - slides.html
add final slides before presentation
[chaz/talk-event-driven-programming-in-perl] / slides.html
1 <!DOCTYPE html>
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">
3
4 class: center, middle
5 name: title
6
7 # Event-driven Programming in Perl
8
9 Charles McGarvey
10
11 ???
12 - Hi. I'm Charles McGarvey.
13 - Been a Perl programmer for a long time...
14
15 ---
16 class: center, middle
17 name: bluehost
18
19 ???
20 - My employer is hiring.
21 - It's a pretty cool employer...
22
23 ---
24 ## "Normal" [userspace] programs
25
26 .big[
27 1. Parse args.
28 2. Read input.
29 3. Do stuff.
30 4. Write results as output.
31 ]
32
33 ---
34 class: pizza
35 background-image: url(img/pizza.jpg)
36 background-size: 100%
37
38 --
39 ```bash
40 # orderpizza --crust-type=thin \
41 --toppings=cheese,pepperoni,mushrooms
42 ```
43
44 --
45 ```perl
46 # source code of the orderpizza program
47
48 my $order = get_order_from_args(@ARGV);
49
50 my $ingredients = gather_ingredients($order);
51
52 my $pizza = prepare_and_bake($order, $ingredentials);
53
54 print($pizza);
55 ```
56
57 ???
58 But some programs are long-lived.
59
60 ---
61 ## Event-driven programs
62
63 .big[
64 1. Events happen.
65 2. When events happen, some code runs and does something.
66 ]
67
68 ???
69 At it's core, event-driven programming is this simple.
70
71 But there are some complications and things to knows, which is why this talk exists.
72
73 ---
74 class: center, middle
75
76 ![Help desk](img/helpdesk.jpg)
77
78 .small[
79 Image by Soniachat8.
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.
81 ]
82
83 ---
84 name: graph-eventloop
85 class: center, middle
86
87 ## Event loop
88
89 ![Event loop](img/eventloop.svg)
90
91 ???
92 We'll refine this model as we go.
93
94 ---
95 class: ex-hwinterrupts
96
97 ## Hardware interrupts
98
99 ```bash
100 # cat /proc/interrupts
101 CPU0 CPU1
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
117
118 ...
119 ```
120
121 ???
122 When a HW interrupt happens, the appropriate code to run is locate in the interrupt service routine
123
124 ---
125 class: ex-pressenter
126
127 ## Your first event-driven program
128
129 .middle[
130 ```perl
131 print "Press <Enter> to continue.";
132 <STDIN>;
133
134 handle_enter_keypress_event();
135 ```
136 ]
137
138 ---
139 class: ex-pressenter
140
141 ## Your first event-driven program
142
143 .middle[
144 ```perl
145 print "Press <Enter> to continue.";
146 *<STDIN>;
147
148 handle_enter_keypress_event();
149 ```
150 ]
151
152 .big[
153 - Wait for an event
154 ]
155
156 ---
157 class: ex-pressenter
158
159 ## Your first event-driven program
160
161 .middle[
162 ```perl
163 print "Press <Enter> to continue.";
164 *<STDIN>;
165
166 handle_enter_keypress_event();
167 ```
168 ]
169
170 .big[
171 - Wait for an event
172 - Event source
173 ]
174
175 ---
176 class: ex-pressenter
177
178 ## Your first event-driven program
179
180 .middle[
181 ```perl
182 print "Press <Enter> to continue.";
183 <STDIN>;
184
185 *handle_enter_keypress_event();
186 ```
187 ]
188
189 .big[
190 - Wait for an event
191 - Event source
192 - Event handler
193 ]
194
195 ---
196 class: ex-signals
197
198 ## Signals
199
200 ```perl
201 use POSIX;
202
203 $SIG{USR1} = sub { handle_usr1_signal_event() };
204 $SIG{ALRM} = sub { handle_timer_event() };
205 $SIG{QUIT} = sub { exit() };
206
207 alarm(3);
208
209 while (1) { POSIX::pause() }
210 ```
211
212 ---
213 class: ex-signals
214
215 ## Signals
216
217 ```perl
218 use POSIX;
219
220 *$SIG{USR1} = sub { handle_usr1_signal_event() };
221 *$SIG{ALRM} = sub { handle_timer_event() };
222 *$SIG{QUIT} = sub { exit() };
223
224 alarm(3);
225
226 while (1) { POSIX::pause() }
227 ```
228
229 ???
230 Notice that this program is capable of handling more than just one event handler at a time.
231
232 ---
233 class: ex-signals
234
235 ## Signals
236
237 ```perl
238 use POSIX;
239
240 $SIG{USR1} = sub { handle_usr1_signal_event() };
241 $SIG{ALRM} = sub { handle_timer_event() };
242 $SIG{QUIT} = sub { exit() };
243
244 alarm(3);
245
246 *while (1) { POSIX::pause() }
247 ```
248
249 .big[
250 - Program yields time to the kernel
251 ]
252
253 ???
254 This example has a loop, but it's not really doing anything.
255
256 But actually it is doing something very important: It's yielding its processing time to the kernel.
257
258 And kernel informs the program of events.
259
260 You might be able to use `sleep` as well, but some implementations of libc
261 in the past have implemented `sleep` using `alarm`.
262
263 Don't know how common that is nowadays, but old habbits...
264
265 ---
266 class: ex-gui
267
268 ## Graphical user interface example
269
270 ```perl
271 use Gtk3 '-init';
272
273 my $window = Gtk3::Window->new;
274
275 $window->signal_connect('key-press-event' => \&handle_keypress_event);
276 $window->signal_connect('delete-event' => \&Gtk3::main_quit);
277
278 $window->show_all;
279
280 Gtk3::main();
281 ```
282
283 ???
284 A user interface happens to be a good place to use evented code because humans are spontaneous and unpredictable.
285
286 ---
287 class: ex-gui
288
289 ## Graphical user interface example
290
291 ```perl
292 use Gtk3 '-init';
293
294 my $window = Gtk3::Window->new;
295
296 *$window->signal_connect('key-press-event' => \&handle_keypress_event);
297 *$window->signal_connect('delete-event' => \&Gtk3::main_quit);
298
299 $window->show_all;
300
301 Gtk3::main();
302 ```
303
304 .big[
305 - Event handlers
306 ]
307
308 ---
309 class: ex-gui
310
311 ## Graphical user interface example
312
313 ```perl
314 use Gtk3 '-init';
315
316 my $window = Gtk3::Window->new;
317
318 $window->signal_connect('key-press-event' => \&handle_keypress_event);
319 $window->signal_connect('delete-event' => \&Gtk3::main_quit);
320
321 $window->show_all;
322
323 *Gtk3::main();
324 ```
325
326 .big[
327 - Event handlers
328 - Yield and wait for events
329 ]
330
331 ---
332 class: event-types
333
334 ## Types of events
335
336 - IO
337
338 ???
339 Data available on a socket, or a new connection.
340
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.
343
344 --
345 - Signals
346
347 ???
348 As far as my program is concerned, it can receive a signal or message from another program at any time.
349
350 --
351 - Timer
352
353 ???
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.
356
357 --
358 - User input
359
360 ???
361 Human beings of course are masters of spontaneity.
362
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.
366
367 --
368 - Anything that can happen spontaneously in the real world.
369
370 ???
371 Lots of other external systems (besides humans) can "generate" events.
372
373 Lot of this requires kernel facilities. Speaking of which, how are these types of things implemented?
374
375 ---
376 class: syscalls
377
378 ## How event-driven userspace code works
379
380 ### syscalls
381
382 - [`pause`](http://man.he.net/man2/pause) - Sleeps until signal
383
384 --
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
386
387 --
388 - [`clock_gettime`](http://man.he.net/man2/clock_gettime) - What time is it now?
389
390 ---
391 class: center, middle
392
393 ## Reactor pattern
394
395 ---
396 ## Reactor pattern
397
398 .big[
399 - Queues events asynchronously.
400 - Demultiplexes and dispatches synchronously.
401 ]
402
403 ---
404 name: graph-reactor
405 class: center, middle
406
407 ## Reactor pattern
408
409 ![Reactor](img/reactor.svg)
410
411 ---
412 class: ex-basicreactor1
413
414 ## Using a reactor
415
416 ```perl
417 my $reactor = My::Reactor->new;
418
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);
423 ...
424
425 $reactor->run_loop;
426 ```
427
428 ---
429 class: ex-basicreactor2
430
431 ## The basic reactor implementation
432
433 ```perl
434 our $timers = [...];
435 our $io_handles = [...];
436
437 while (1) {
438 my $next_timer = find_next_timer($timers);
439
440 poll($io_handles, $next_timer->time_from_now);
441
442 handle_ready_io_handles($io_handles);
443 handle_expired_timers($timers);
444 }
445 ```
446
447 ???
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.)
450
451 But I'm covering it because I think it's good for you.
452
453 ---
454 class: ex-basicreactor2
455
456 ## The basic reactor implementation
457
458 ```perl
459 our $timers = [...];
460 our $io_handles = [...];
461
462 while (1) {
463 my $next_timer = find_next_timer($timers);
464
465 * poll($io_handles, $next_timer->time_from_now);
466
467 handle_ready_io_handles($io_handles);
468 handle_expired_timers($timers);
469 }
470 ```
471
472 ---
473 ## Reactor examples on CPAN
474
475 .big[
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)
482 ]
483
484 ???
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.
487
488 ---
489 ## Reactors
490
491 .col.big[
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)
497 ]
498 .col.big[
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)
503 - probably more...
504 ]
505
506 ???
507 I'm not going to go over any of these.
508
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.
514
515 But actually you may not need to pick one...
516
517 ---
518 class: center, middle
519
520 ## Reactor "front ends"
521
522 ???
523 By my count there are four main front ends.
524
525 ---
526 ## Reactor "front ends"
527
528 .big[
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
533 ]
534
535 ???
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.
538
539 ---
540 class: ex-poe
541
542 ## `POE`
543
544 ```perl
545 use POE;
546 use Time::HiRes qw(time);
547
548 POE::Session->create(
549 inline_states => {
550 _start => sub {
551 $_[KERNEL]->delay(tick => 5);
552 },
553
554 tick => \&handle_timer_event,
555 },
556 );
557
558 POE::Kernel->run;
559 ```
560
561 ???
562 POE:
563 - The oldest, released 1998
564 - Parallel processing was it's primary use.
565
566 ---
567 class: ex-poe
568
569 ## `POE`
570
571 ```perl
572 *use POE;
573 use Time::HiRes qw(time);
574
575 POE::Session->create(
576 inline_states => {
577 _start => sub {
578 $_[KERNEL]->delay(tick => 5);
579 },
580
581 tick => \&handle_timer_event,
582 },
583 );
584
585 POE::Kernel->run;
586 ```
587
588 ???
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`.
591
592 ---
593 class: ex-poe
594
595 ## `POE`
596
597 ```perl
598 use POE;
599 use Time::HiRes qw(time);
600
601 POE::Session->create(
602 inline_states => {
603 _start => sub {
604 $_[KERNEL]->delay(tick => 5);
605 },
606
607 tick => \&handle_timer_event,
608 },
609 );
610
611 *POE::Kernel->run;
612 ```
613
614 ???
615 Run the reactor.
616
617 In POE, the kernel is the thing that manages processes AKA sessions.
618
619 ---
620 class: ex-poe
621
622 ## `POE`
623
624 ```perl
625 use POE;
626 use Time::HiRes qw(time);
627
628 *POE::Session->create(
629 * inline_states => {
630 * _start => sub {
631 * $_[KERNEL]->delay(tick => 5);
632 * },
633 *
634 * tick => \&handle_timer_event,
635 * },
636 *);
637
638 POE::Kernel->run;
639 ```
640
641 ???
642
643 Sessions can be created to do stuff.
644
645 ---
646 class: ex-ioasync
647
648 ## `IO::Async`
649
650 ```perl
651 use IO::Async::Loop;
652 use IO::Async::Timer::Countdown;
653
654 my $loop = IO::Async::Loop->new;
655
656 my $timer = IO::Async::Timer::Countdown->new(
657 delay => 5, # seconds
658 on_expire => \&handle_timer_event,
659 );
660 $timer->start;
661
662 $loop->add($timer);
663
664 $loop->run;
665 ```
666
667 ---
668 class: ex-ioasync
669
670 ## `IO::Async`
671
672 ```perl
673 use IO::Async::Loop;
674 use IO::Async::Timer::Countdown;
675
676 *my $loop = IO::Async::Loop->new;
677
678 my $timer = IO::Async::Timer::Countdown->new(
679 delay => 5, # seconds
680 on_expire => \&handle_timer_event,
681 );
682 $timer->start;
683
684 $loop->add($timer);
685
686 $loop->run;
687 ```
688
689 ???
690 Create the loop
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
693 themselves.
694
695 ---
696 class: ex-ioasync
697
698 ## `IO::Async`
699
700 ```perl
701 use IO::Async::Loop;
702 use IO::Async::Timer::Countdown;
703
704 my $loop = IO::Async::Loop->new;
705
706 my $timer = IO::Async::Timer::Countdown->new(
707 delay => 5, # seconds
708 on_expire => \&handle_timer_event,
709 );
710 $timer->start;
711
712 *$loop->add($timer);
713
714 *$loop->run;
715 ```
716
717 ???
718 Add your handler to the loop, and of course run the loop.
719
720 ---
721 class: ex-ioasync
722
723 ## `IO::Async`
724
725 ```perl
726 use IO::Async::Loop;
727 use IO::Async::Timer::Countdown;
728
729 my $loop = IO::Async::Loop->new;
730
731 my $timer = IO::Async::Timer::Countdown->new(
732 delay => 5, # seconds
733 on_expire => \&handle_timer_event,
734 );
735 *$timer->start;
736
737 $loop->add($timer);
738
739 $loop->run;
740 ```
741
742 ???
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.
745
746 ---
747 class: ex-mojoioloop
748
749 ## `Mojo::IOLoop`
750
751 ```perl
752 use Mojo::IOLoop;
753
754 Mojo::IOLoop->timer(5 => \&handle_timer_event);
755
756 Mojo::IOLoop->start if ! Mojo::IOLoop->is_running;
757 ```
758
759 ---
760 class: ex-mojoioloop
761
762 ## `Mojo::IOLoop`
763
764 ```perl
765 use Mojo::IOLoop;
766
767 *Mojo::IOLoop->timer(5 => \&handle_timer_event);
768
769 Mojo::IOLoop->start if ! Mojo::IOLoop->is_running;
770 ```
771
772 ???
773 Create the timer... easy.
774
775 ---
776 class: ex-mojoioloop
777
778 ## `Mojo::IOLoop`
779
780 ```perl
781 use Mojo::IOLoop;
782
783 Mojo::IOLoop->timer(5 => \&handle_timer_event);
784
785 *Mojo::IOLoop->start if ! Mojo::IOLoop->is_running;
786 ```
787
788 ???
789 And start the loop. Very easy.
790
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.
794
795 ---
796 class: ex-anyevent
797
798 ## `AnyEvent`
799
800 ```perl
801 use EV;
802 use AE;
803
804 my $timer = AE::timer(5, 0 => \&handle_timer_event);
805
806 EV::run();
807 ```
808
809 ---
810 class: ex-anyevent
811
812 ## `AnyEvent`
813
814 ```perl
815 *use EV;
816 use AE;
817
818 my $timer = AE::timer(5, 0 => \&handle_timer_event);
819
820 *EV::run();
821 ```
822
823 ---
824 class: ex-anyevent
825
826 ## `AnyEvent`
827
828 ```perl
829 *use Glib;
830 use AE;
831
832 *my $loop = Glib::MainLoop->new;
833
834 my $timer = AE::timer(5, 0 => \&handle_timer_event);
835
836 *$loop->run;
837 ```
838
839 ---
840 class: ex-anyevent
841
842 ## `AnyEvent`
843
844 ```perl
845 use Glib;
846 use AE;
847
848 my $loop = Glib::MainLoop->new;
849
850 *my $timer = AE::timer(5, 0 => \&handle_timer_event);
851
852 $loop->run;
853 ```
854
855 ???
856 Create your timer.
857
858 By the way, the second argument to `AE::timer` represents the interval if you want a periodic timer.
859
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
862 unregistered.
863
864 `AnyEvent` technically has two APIs. This is the "simplified" API.
865
866 ---
867 class: ex-anyevent
868
869 ## `AnyEvent`
870
871 ```perl
872 use Glib;
873 *use AnyEvent;
874
875 my $loop = Glib::MainLoop->new;
876
877 *my $timer = AnyEvent->timer(
878 * after => 5,
879 * cb => \&handle_timer_event,
880 *);
881
882 $loop->run;
883 ```
884
885 ???
886 This is what the "complicated" API looks like.
887
888 ---
889 name: not-all-roses
890 class: center, middle
891
892 ![Thorns](img/thorn.jpg)
893
894 ## Watch out for the thorns...
895
896 ???
897 There are some special considerations you need to take when writing event-driven code.
898
899 ---
900 class: center, middle
901
902 ## Exceptions for error handling
903
904 ---
905 class: center, middle
906
907 ### Problem: No exception handler up the call stack
908
909 ---
910 class: ex-exceptions
911
912 ## Rule: Don't die/throw in event handlers.
913
914 --
915 ### Error callback pattern
916
917 ```perl
918 do_something_asynchronously(
919 callback => sub { ... },
920 on_error => sub { ... },
921 );
922 ```
923
924 ---
925 class: ex-exceptions
926
927 ## Rule: Don't die/throw in event handlers.
928
929 ### Use promises
930
931 ```perl
932 my $promise = do_something_asynchronously();
933
934 $promise->on_done(sub { ... });
935 $promise->on_fail(sub { ... });
936 ```
937
938 ???
939 More on this later.
940
941 ---
942 class: center, middle
943
944 ## `SIGPIPE`
945
946 ---
947 class: sigpipe
948 ## `SIGPIPE`
949
950 - Sent to your program when it writes to a pipe that was closed.
951
952 --
953 - Default signal handler terminates the program.
954
955 ---
956 class: ex-sigpipe
957
958 ## Solution: Ignore `SIGPIPE`
959
960 ```perl
961 $SIG{PIPE} = 'IGNORE';
962 ```
963
964 ???
965 Some event loops do this for you.
966
967 --
968 .big[
969 Look for `EPIPE` from syscalls (like [`write`](http://man.he.net/man2/write)) instead.
970
971 (You *are* checking return codes from your system calls... right?)
972 ]
973
974 ---
975 class: center, middle
976
977 ## Debugging event-driven code
978
979 ---
980 class: ex-debugging
981
982 ## Debugging event-driven code
983
984 .big[
985 - Print debug info to `STDERR`.
986 ]
987
988 ```bash
989 export PERL_FUTURE_DEBUG=1
990 # and
991 export ANYEVENT_DEBUG=8
992 # or
993 export MOJO_IOLOOP_DEBUG=1
994 # etc.
995 ```
996
997 ---
998 ## Debugging event-driven code
999
1000 .big[
1001 - Print debug info to `STDERR`.
1002 - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
1003 ]
1004
1005 ---
1006 ## Debugging event-driven code
1007
1008 .big[
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.
1012 ]
1013
1014 ???
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.
1018
1019 ---
1020 class: center, middle
1021
1022 ## Promises:
1023 ### Proxy objects for future values
1024
1025 ???
1026 Proxy objects for values that have not yet been retrieved or computed.
1027
1028 - In some cases, promises can provide you an alternative, often more useful interface to callbacks.
1029
1030 ---
1031 class: ex-future
1032
1033 ## Basic usage (using [`Future`](https://metacpan.org/pod/Future))
1034
1035 ```perl
1036 my $future = fetch_remote_file($url);
1037
1038 $future->on_done(sub($filename, $contents) {
1039 print "Fetched $filename\n";
1040 });
1041 ```
1042
1043 ???
1044 The future is an object that stands in for the thing we actually want until the thing we want is available.
1045
1046 --
1047 ```perl
1048 $future->on_fail(sub($error) {
1049 warn $error;
1050 });
1051 ```
1052
1053 ???
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.
1056
1057 - For any operation that can fail (and that includes almost everything), it's a good idea to handle errors.
1058
1059 ---
1060 class: ex-future
1061
1062 ## Chaining futures
1063
1064 ```perl
1065 my $future = fetch_remote_file($url)
1066 ->then(sub($filename, $contents) {
1067 my $another_future = write_local_file("/tmp/$filename",
1068 $contents);
1069 return $another_future;
1070 });
1071
1072 $future->on_done(sub($filepath) {
1073 print "Saved file to $filepath\n";
1074 });
1075
1076 $future->on_fail(sub($error) {
1077 warn $error;
1078 });
1079 ```
1080
1081 ---
1082 class: ex-future2
1083
1084 ## Creating futures
1085
1086 ```perl
1087 use AnyEvent::HTTP;
1088 use Future;
1089
1090 sub fetch_remote_file($url) {
1091 my $future = Future->new;
1092
1093 http_get $url => sub($data, $headers) {
1094 if ($headers->{Status} =~ /^[23]/) {
1095 my ($filename) = get_filename($headers);
1096 $future->done($filename, $data);
1097 }
1098 else {
1099 $future->fail("Failed to fetch file: $headers->{Reason}");
1100 }
1101 };
1102
1103 return $future;
1104 }
1105 ```
1106
1107 ---
1108 class: center, middle
1109
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.
1111
1112 And, of course, [read the pod](https://metacpan.org/pod/Future).
1113
1114 ???
1115 Paul did an advent calendar with short posts detailing the things you can do with `Future`.
1116
1117 - It's really well done.
1118
1119 ---
1120 class: center, middle
1121
1122 ## There's also [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
1123
1124 ???
1125 If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
1126
1127 ---
1128 class: center, middle
1129
1130 ### Yes, Perl can do it, too!
1131
1132 ---
1133 class: ex-asyncawait
1134
1135 ## Without async and await
1136
1137 ```perl
1138 use Future;
1139
1140 sub do_two_things {
1141 return do_first_thing()->then(sub {
1142 my $first = shift;
1143
1144 return do_second_thing($first)->then(sub {
1145 my $second = shift;
1146
1147 return Future->done([$first, $second]);
1148 });
1149 });
1150 }
1151 ```
1152
1153 ---
1154 class: ex-asyncawait
1155
1156 ## With async and await
1157
1158 ```perl
1159 use Future::AsyncAwait;
1160
1161 async sub do_two_things
1162 {
1163 my $first = await do_first_thing();
1164
1165 my $second = await do_second_thing($first);
1166
1167 return [$first, $second];
1168 }
1169 ```
1170
1171 ???
1172 There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
1173
1174 ---
1175 class: center, middle
1176
1177 ## Finally, there's also [`Future::Utils`](https://metacpan.org/pod/Future::Utils).
1178
1179 ---
1180 class: ex-future2
1181
1182 ## Call
1183
1184 ```perl
1185 my $future = call {
1186 do_stuff();
1187
1188 ...
1189
1190 my $future = ...;
1191 return $future;
1192 };
1193 ```
1194
1195 Any exceptions throw in the code block are caught and become a failed `Future`.
1196
1197 ---
1198 class: ex-future2
1199
1200 ## Loops and stuff
1201
1202 ```perl
1203 use Future::Utils qw(repeat);
1204
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) };
1209 ```
1210
1211 ---
1212 class: center, middle
1213
1214 ## Final thoughts
1215
1216 ---
1217 class: center, middle
1218
1219 ### Proactor pattern
1220
1221 ???
1222 We've gone over the Reactor pattern quite a bit, and for good reason.
1223
1224 It's the predominant implementation of event-driven code in userspace apps.
1225
1226 But you should also know about the Proactor pattern.
1227
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.
1231
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.
1234
1235 ---
1236 class: center, middle
1237
1238 ### Event-driven <strike>programming</strike> architecture
1239
1240 ???
1241 Making your apps reactive and able to generate and respond to events is the tip of a very large iceburg.
1242
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.
1245
1246 Once your programs are event-driven, they're ready to be plugged into a whole world of other services.
1247
1248 ---
1249 class: center, middle
1250
1251 ### Lots of interconnected devices
1252
1253 ???
1254 This concept has a trendy name, but I can't say it because I've swarn off buzzwords.
1255
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.
1258
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.
1261
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.
1264
1265 So there are a lot of cool applications for this stuff.
1266
1267 ---
1268 class: center, middle
1269 name: conclusion
1270
1271 ## Conclusion:
1272
1273 ### Perl is fun.
1274
1275 ---
1276 class: center, middle
1277 name: last
1278
1279 ### Thanks.
1280
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: -->
This page took 0.085482 seconds and 4 git commands to generate.