]> Dogcows Code - chaz/talk-event-driven-programming-in-perl/blob - slides.html
add more slides on Future
[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 class: ex-basicreactor2
449
450 ## The basic reactor implementation
451
452 ```perl
453 our $timers = [...];
454 our $io_handles = [...];
455
456 while (1) {
457 my $next_timer = find_next_timer($timers);
458
459 * poll($io_handles, $next_timer->time_from_now);
460
461 handle_ready_io_handles($io_handles);
462 handle_expired_timers($timers);
463 }
464 ```
465
466 ---
467 ## Reactor examples on CPAN
468
469 .big[
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)
476 ]
477
478 ???
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.
481
482 ---
483 ## Reactors
484
485 .col.big[
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)
491 ]
492 .col.big[
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)
497 - probably more...
498 ]
499
500 ???
501 I'm not going to go over any of these.
502
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.
508
509 But actually you may not need to pick one...
510
511 ---
512 class: center, middle
513
514 ## Reactor "front ends"
515
516 ???
517 By my count there are four main front ends.
518
519 ---
520 ## Reactor "front ends"
521
522 .big[
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
527 ]
528
529 ???
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.
532
533 POE:
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.
539
540 ---
541 name: not-all-roses
542 class: center, middle
543
544 ![Thorns](img/thorn.jpg)
545
546 ## Watch out for the thorns...
547
548 ???
549 There are some special considerations you need to take when writing event-driven code.
550
551 ---
552 class: center, middle
553
554 ## Exceptions for error handling
555
556 ---
557 class: center, middle
558
559 ### Problem: No exception handler up the call stack
560
561 ---
562 class: ex-exceptions
563
564 ## Rule: Don't die/throw in event handlers.
565
566 --
567 ### Error callback pattern
568
569 ```perl
570 do_something_asynchronously(
571 callback => sub { ... },
572 on_error => sub { ... },
573 );
574 ```
575
576 ---
577 class: ex-exceptions
578
579 ## Rule: Don't die/throw in event handlers.
580
581 ### Use promises
582
583 ```perl
584 my $promise = do_something_asynchronously();
585
586 $promise->on_done(sub { ... });
587 $promise->on_fail(sub { ... });
588 ```
589
590 ???
591 More on this later.
592
593 ---
594 class: center, middle
595
596 ## `SIGPIPE`
597
598 ---
599 class: sigpipe
600 ## `SIGPIPE`
601
602 - Sent to your program when it writes to a pipe that was closed.
603
604 --
605 - Default signal handler terminates the program.
606
607 ---
608 class: ex-sigpipe
609
610 ## Solution: Ignore `SIGPIPE`
611
612 ```perl
613 $SIG{PIPE} = 'IGNORE';
614 ```
615
616 ???
617 Some event loops do this for you.
618
619 --
620 .big[
621 Look for `EPIPE` from syscalls (like [`write`](http://man.he.net/man2/write)) instead.
622
623 (You *are* checking return codes from your system calls... right?)
624 ]
625
626 ---
627 class: center, middle
628
629 ## Debugging event-driven code
630
631 ---
632 class: ex-debugging
633
634 ## Debugging event-driven code
635
636 .big[
637 - Print debug info to `STDERR`.
638 ]
639
640 ```bash
641 export PERL_FUTURE_DEBUG=1
642 # and
643 export ANYEVENT_DEBUG=8
644 # or
645 export MOJO_IOLOOP_DEBUG=1
646 # etc.
647 ```
648
649 ---
650 ## Debugging event-driven code
651
652 .big[
653 - Print debug info to `STDERR`.
654 - Check out [`AnyEvent::Debug`](https://metacpan.org/pod/AnyEvent::Debug).
655 ]
656
657 ---
658 ## Debugging event-driven code
659
660 .big[
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.
664 ]
665
666 ???
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
669 programs tend to be.
670
671 ---
672 class: center, middle
673
674 ## Promises:
675 ### Proxy objects for future values
676
677 ???
678 Proxy objects for values that have not yet been retrieved or computed.
679
680 - In some cases, promises can provide you an alternative, often more useful interface to callbacks.
681
682 ---
683 class: ex-future
684
685 ## Basic usage (using [`Future`](https://metacpan.org/pod/Future))
686
687 ```perl
688 my $future = fetch_remote_file($url);
689
690 $future->on_done(sub($filename, $contents) {
691 print "Fetched $filename\n";
692 });
693 ```
694
695 ???
696 The future is an object that stands in for the thing we actually want until the thing we want is available.
697
698 --
699 ```perl
700 $future->on_fail(sub($error) {
701 warn $error;
702 });
703 ```
704
705 ???
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.
708
709 - For any operation that can fail (and that includes almost everything), it's a good idea to handle errors.
710
711 ---
712 class: ex-future
713
714 ## Chaining futures
715
716 ```perl
717 my $future = fetch_remote_file($url)
718 ->then(sub($filename, $contents) {
719 my $another_future = write_local_file("/tmp/$filename",
720 $contents);
721 return $another_future;
722 });
723
724 $future->on_done(sub($filepath) {
725 print "Saved file to $filepath\n";
726 });
727
728 $future->on_fail(sub($error) {
729 warn $error;
730 });
731 ```
732
733 ---
734 class: ex-future2
735
736 ## Creating futures
737
738 ```perl
739 use AnyEvent::HTTP;
740 use Future;
741
742 sub fetch_remote_file($url) {
743 my $future = Future->new;
744
745 http_get $url => sub($data, $headers) {
746 if ($headers->{Status} =~ /^[23]/) {
747 my ($filename) = get_filename($headers);
748 $future->done($filename, $data);
749 }
750 else {
751 $future->fail("Failed to fetch file: $headers->{Reason}");
752 }
753 };
754
755 return $future;
756 }
757 ```
758
759 ---
760 class: center, middle
761
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.
763
764 And, of course, [read the pod](https://metacpan.org/pod/Future).
765
766 ???
767 Paul did an advent calendar with short posts detailing the things you can do with `Future`.
768
769 - It's really well done.
770
771 ---
772 class: center, middle
773
774 ## There's also [`Future::AsyncAwait`](https://metacpan.org/pod/Future::AsyncAwait).
775
776 ???
777 If you have used JavaScript recently, you may have used its "async/await" feature to clean up your non-blocking code.
778
779 ---
780 class: center, middle
781
782 ### Yes, Perl can do it, too!
783
784 ---
785 class: ex-asyncawait
786
787 ## Without async and await
788
789 ```perl
790 use Future;
791
792 sub do_two_things {
793 return do_first_thing()->then(sub {
794 my $first = shift;
795
796 return do_second_thing($first)->then(sub {
797 my $second = shift;
798
799 return Future->done([$first, $second]);
800 });
801 });
802 }
803 ```
804
805 ---
806 class: ex-asyncawait
807
808 ## With async and await
809
810 ```perl
811 use Future::AsyncAwait;
812
813 async sub do_two_things
814 {
815 my $first = await do_first_thing();
816
817 my $second = await do_second_thing($first);
818
819 return [$first, $second];
820 }
821 ```
822
823 ???
824 There are caveats: Localized variable assignments don't work, nor anything that has implied local-like behavior.
825
826 ---
827 class: center, middle
828
829 ## Finally, there's also [`Future::Utils`](https://metacpan.org/pod/Future::Utils).
830
831 ---
832 class: ex-future2
833
834 ## Call
835
836 ```perl
837 my $future = call {
838 do_stuff();
839
840 ...
841
842 my $future = ...;
843 return $future;
844 };
845 ```
846
847 Any exceptions throw in the code block are caught and become a failed `Future`.
848
849 ---
850 class: ex-future2
851
852 ## Loops and stuff
853
854 ```perl
855 use Future::Utils qw(repeat);
856
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) };
861 ```
862
863 ---
864 ## Events in the world
865
866 ---
867 class: center, middle
868 name: conclusion
869
870 ## Conclusion:
871
872 ### Perl is fun.
873
874 ---
875 class: center, middle
876 name: last
877
878 ### Thanks.
879
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: -->
This page took 0.066005 seconds and 4 git commands to generate.