Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Event-driven programming with Perl

Event-driven programming with Perl

My talk about doing event-driven programming with perl in OSDC.tw 2012.

#osdctw2012

4d8c65b603554811079a7ea9b7aab9c0?s=128

Kang-min Liu

April 14, 2012
Tweet

Transcript

  1. ࣄ݅᱓ಈఔࣜઃܭ With Perl Kang-min Liu / gugod@gugod.org 12年4月14日星期六

  2. In computer programming, event- driven programming or event-based programming is

    a programming paradigm in which the flow of the program is determined by events—e.g., sensor outputs or user actions (mouse clicks, key presses) or messages from other programs or threads. ref: wikipedia 12年4月14日星期六
  3. In computer programming, event- driven programming or event-based programming is

    a programming paradigm in which the flow of the program is determined by events—e.g., sensor outputs or user actions (mouse clicks, key presses) or messages from other programs or threads. ref: wikipedia 12年4月14日星期六
  4. main() 12年4月14日星期六

  5. onclick() ondoubleclick() onmouseover() oncontextmenu() onselect() onload() 12年4月14日星期六

  6. onclick() ondoubleclick() onmouseover() oncontextmenu() onselect() onload() main() 12年4月14日星期六

  7. onclick() ondoubleclick() onmouseover() oncontextmenu() onselect() onload() main() “click” 1.ఁଌ 2.೿ૹ

    ↺ 12年4月14日星期六
  8. onclick() ondoubleclick() onmouseover() oncontextmenu() onselect() onload() main() Event Loop ↺

    12年4月14日星期六
  9. onclick() ondoubleclick() onmouseover() oncontextmenu() onselect() onload() main() Event Handlers ↺

    12年4月14日星期六
  10. Even-driven ~ Asynchronous 12年4月14日星期六

  11. Even-driven ~ Asynchronous do_this(sub { then_do_that(); }); 12年4月14日星期六

  12. Event Loop In Perl https://metacpan.org/search?q=event+loop 403 results 12年4月14日星期六

  13. Event Loop In Perl • Event • Event::Lib(libevent) • IO::Async

    • EV (libev) • Glib • Gtk • Tk • Prima • Net::IRC • Mojo::IOLoop • POE 12年4月14日星期六
  14. use Event::Lib; use POSIX qw/SIGINT/; $|=1; # autoflush STDOUT sub

    timer { my $event = shift; print localtime . "\r"; $event->add(1); } sub signal { my $event = shift; print "BYE BYE\n"; exit; } my $timer = timer_new(\&timer); my $signal = signal_new(SIGINT, \&signal); $timer->add(1); # triggered every second $signal->add; event_mainloop; 12年4月14日星期六
  15. use Event::Lib; use POSIX qw/SIGINT/; $|=1; # autoflush STDOUT sub

    timer { my $event = shift; print localtime . "\r"; $event->add(1); } sub signal { my $event = shift; print "BYE BYE\n"; exit; } my $timer = timer_new(\&timer); my $signal = signal_new(SIGINT, \&signal); $timer->add(1); # triggered every second $signal->add; event_mainloop; 12年4月14日星期六
  16. use Event::Lib; use POSIX qw/SIGINT/; $|=1; # autoflush STDOUT sub

    timer { my $event = shift; print localtime . "\r"; $event->add(1); } sub signal { my $event = shift; print "BYE BYE\n"; exit; } my $timer = timer_new(\&timer); my $signal = signal_new(SIGINT, \&signal); $timer->add(1); # triggered every second $signal->add; event_mainloop; Event 12年4月14日星期六
  17. use Event::Lib; use POSIX qw/SIGINT/; $|=1; # autoflush STDOUT sub

    timer { my $event = shift; print localtime . "\r"; $event->add(1); } sub signal { my $event = shift; print "BYE BYE\n"; exit; } my $timer = timer_new(\&timer); my $signal = signal_new(SIGINT, \&signal); $timer->add(1); # triggered every second $signal->add; event_mainloop; Handlers 12年4月14日星期六
  18. use Event::Lib; use POSIX qw/SIGINT/; $|=1; # autoflush STDOUT sub

    timer { my $event = shift; print localtime . "\r"; $event->add(1); } sub signal { my $event = shift; print "BYE BYE\n"; exit; } my $timer = timer_new(\&timer); my $signal = signal_new(SIGINT, \&signal); $timer->add(1); # triggered every second $signal->add; event_mainloop; ਐೖࣄ݅᫮ᅲ 12年4月14日星期六
  19. use Event::Lib; use POSIX qw/SIGINT/; $|=1; # autoflush STDOUT sub

    timer { my $event = shift; print localtime . "\r"; $event->add(1); } sub signal { my $event = shift; print "BYE BYE\n"; exit; } my $timer = timer_new(\&timer); my $signal = signal_new(SIGINT, \&signal); $timer->add(1); # triggered every second $signal->add; event_mainloop; 12年4月14日星期六
  20. use POE; $|=1; sub handler_tick { print localtime . "\r";

    $_[KERNEL]->alarm( 'tick' => time() + 1 ); } sub handler_start { $_[KERNEL]->sig('INT', 'handle_sigint'); $_[KERNEL]->alarm( 'tick', time() + 1 ); } sub handler_stop { print "\nBYE BYE\n"; } sub handle_sigint { $_[KERNEL]->stop; } POE::Session->create( inline_states => { _start => \&handler_start, _stop => \&handler_stop, tick => \&handler_tick, } ); POE::Kernel->run(); 12年4月14日星期六
  21. use POE; $|=1; sub handler_tick { print localtime . "\r";

    $_[KERNEL]->alarm( 'tick' => time() + 1 ); } sub handler_start { $_[KERNEL]->sig('INT', 'handle_sigint'); $_[KERNEL]->alarm( 'tick', time() + 1 ); } sub handler_stop { print "\nBYE BYE\n"; } sub handle_sigint { $_[KERNEL]->stop; } POE::Session->create( inline_states => { _start => \&handler_start, _stop => \&handler_stop, tick => \&handler_tick, } ); POE::Kernel->run(); 12年4月14日星期六
  22. use POE; $|=1; sub handler_tick { print localtime . "\r";

    $_[KERNEL]->alarm( 'tick' => time() + 1 ); } sub handler_start { $_[KERNEL]->sig('INT', 'handle_sigint'); $_[KERNEL]->alarm( 'tick', time() + 1 ); } sub handler_stop { print "\nBYE BYE\n"; } sub handle_sigint { $_[KERNEL]->stop; } POE::Session->create( inline_states => { _start => \&handler_start, _stop => \&handler_stop, tick => \&handler_tick, } ); POE::Kernel->run(); Event 12年4月14日星期六
  23. use POE; $|=1; sub handler_tick { print localtime . "\r";

    $_[KERNEL]->alarm( 'tick' => time() + 1 ); } sub handler_start { $_[KERNEL]->sig('INT', 'handle_sigint'); $_[KERNEL]->alarm( 'tick', time() + 1 ); } sub handler_stop { print "\nBYE BYE\n"; } sub handle_sigint { $_[KERNEL]->stop; } POE::Session->create( inline_states => { _start => \&handler_start, _stop => \&handler_stop, tick => \&handler_tick, } ); POE::Kernel->run(); Event Handlers 12年4月14日星期六
  24. use POE; $|=1; sub handler_tick { print localtime . "\r";

    $_[KERNEL]->alarm( 'tick' => time() + 1 ); } sub handler_start { $_[KERNEL]->sig('INT', 'handle_sigint'); $_[KERNEL]->alarm( 'tick', time() + 1 ); } sub handler_stop { print "\nBYE BYE\n"; } sub handle_sigint { $_[KERNEL]->stop; } POE::Session->create( inline_states => { _start => \&handler_start, _stop => \&handler_stop, tick => \&handler_tick, } ); POE::Kernel->run(); ਐೖࣄ݅᫮ᅲ 12年4月14日星期六
  25. AnyEvent 12年4月14日星期六

  26. AnyEvent • Marc Lehmann @schmorp • http://home.schmorp.de • libev /

    rxvt-unicode / deliantra / JSON::XS 12年4月14日星期六
  27. AnyEvent • Library for Event-based or asynchronous programming • ݱ༗

    Event loop తந৅૚ 12年4月14日星期六
  28. AnyEvent Event • AnyEvent ຊ਎ᔒ༗ Event model • ࢖༻ऀجຊ্୞႔ཧඇಉ㑊ᬓा •

    AnyEvent ࠶ሡඇಉ㑊ᬓ์ਐݱ੒ event loop ڈࣥߦ 12年4月14日星期六
  29. AnyEvent Watcher • io • timer • signal • child

    • idle • condvar 12年4月14日星期六
  30. my $t = AnyEvent->timer( after => 0, interval => 1,

    cb => sub { say localtime; } ); 12年4月14日星期六
  31. my $t = AnyEvent->timer( after => 0, interval => 1,

    cb => sub { say localtime; } ); Watcher 12年4月14日星期六
  32. my $t = AnyEvent->timer( after => 0, interval => 1,

    cb => sub { say localtime; } ); Watcher setInterval 12年4月14日星期六
  33. my $t = AnyEvent->timer( after => 0, interval => 1,

    cb => sub { say localtime; } ); ... undef $t; 12年4月14日星期六
  34. my $t = AnyEvent->timer( after => 0, interval => 1,

    cb => sub { say localtime; } ); ... undef $t; ෆཁ࠶㑌ඵҹ࣌ؒྃ 12年4月14日星期六
  35. my $t = AnyEvent->timer( after => 0, interval => 1,

    cb => sub { say localtime; } ); ... undef $t; ෆཁ࠶㑌ඵҹ࣌ؒྃ clearInterval 12年4月14日星期六
  36. my $w = AnyEvent->signal( signal => "TERM", cb => sub

    { ... } ); 12年4月14日星期六
  37. $SIG{TERM} = sub { ... }; 12年4月14日星期六

  38. $SIG{TERM} = sub { do_this; }; $SIG{TERM} = sub {

    do_that; }; 12年4月14日星期六
  39. $SIG{TERM} = sub { do_this; }; $SIG{TERM} = sub {

    do_that; }; ☹ 12年4月14日星期六
  40. my $w = AnyEvent->signal( signal => "TERM", cb => sub

    { do_this } ); my $q = AnyEvent->signal( signal => "TERM", cb => sub { do_that } ); 12年4月14日星期六
  41. my $w = AnyEvent->signal( signal => "TERM", cb => sub

    { do_this } ); my $q = AnyEvent->signal( signal => "TERM", cb => sub { do_that } ); OK! 12年4月14日星期六
  42. my $w = AnyEvent->io( fh => $fh, poll => "r",

    cb => sub { ... } ); 12年4月14日星期六
  43. my $pid = fork; my $child_watcher; if ($pid) { $child_watcher

    = AnyEvent->child ( pid => $pid, cb => sub { my ($pid, $status) = @_; ... } ); } else { # child code } 12年4月14日星期六
  44. my $w = AnyEvent->idle( cb => sub { ... }

    ); 12年4月14日星期六
  45. use AnyEvent; $|=1; my $should_exit = AnyEvent->condvar; my $t =

    AnyEvent->timer( after => 0, interval => 1, cb => sub { print localtime."\r"; } ); my $s = AnyEvent->signal( signal => "INT", cb => sub { print "\na\BYE BYE\n"; $should_exit->send; } ); $should_exit->recv; 12年4月14日星期六
  46. use AnyEvent; $|=1; my $should_exit = AnyEvent->condvar; my $t =

    AnyEvent->timer( after => 0, interval => 1, cb => sub { print localtime."\r"; } ); my $s = AnyEvent->signal( signal => "INT", cb => sub { print "\na\BYE BYE\n"; $should_exit->send; } ); $should_exit->recv; 12年4月14日星期六
  47. use AnyEvent; $|=1; my $should_exit = AnyEvent->condvar; my $t =

    AnyEvent->timer( after => 0, interval => 1, cb => sub { print localtime."\r"; } ); my $s = AnyEvent->signal( signal => "INT", cb => sub { print "\na\BYE BYE\n"; $should_exit->send; } ); $should_exit->recv; Watcher 12年4月14日星期六
  48. use AnyEvent; $|=1; my $should_exit = AnyEvent->condvar; my $t =

    AnyEvent->timer( after => 0, interval => 1, cb => sub { print localtime."\r"; } ); my $s = AnyEvent->signal( signal => "INT", cb => sub { print "\na\BYE BYE\n"; $should_exit->send; } ); $should_exit->recv; Handler 12年4月14日星期六
  49. use AnyEvent; $|=1; my $should_exit = AnyEvent->condvar; my $t =

    AnyEvent->timer( after => 0, interval => 1, cb => sub { print localtime."\r"; } ); my $s = AnyEvent->signal( signal => "INT", cb => sub { print "\na\BYE BYE\n"; $should_exit->send; } ); $should_exit->recv; ਐೖࣄ݅᫮ᅲ 12年4月14日星期六
  50. AnyEvent Condvar • Condition Variable • ୅දʮ๭๭ᑍ݅੒ཱʯతᏓᏐ • mini event

    loop • Data warm hole 12年4月14日星期六
  51. AnyEvent Condvar ୊໨ɿఏࣔ࢖༻ऀ༌ೖ໊ࣈɼવޙҹग़ ိɻ 12年4月14日星期六

  52. AnyEvent Condvar $|=1; print "Enter your name> "; my $name

    = <STDIN>; print "Hello, $name\n"; 12年4月14日星期六
  53. AnyEvent Condvar $|=1; print "Enter your name> "; my $name

    = <STDIN>; print "Hello, $name\n"; IO block 12年4月14日星期六
  54. use AnyEvent; $|=1; my $name_ready = AE::cv; my $angel =

    AE::io \*STDIN, 0, sub { my $x = <STDIN>; $name_ready->send($x); }; print "Enter your name> "; my $name = $name_ready->recv; print "\nHello, $name\n"; 12年4月14日星期六
  55. use AnyEvent; $|=1; my $name_ready = AE::cv; my $angel =

    AE::io \*STDIN, 0, sub { my $x = <STDIN>; $name_ready->send($x); }; print "Enter your name> "; my $name = $name_ready->recv; print "\nHello, $name\n"; block 12年4月14日星期六
  56. use AnyEvent; $|=1; my $name_ready = AE::cv; my $angel =

    AE::io \*STDIN, 0, sub { my $x = <STDIN>; $name_ready->send($x); }; print "Enter your name> "; my $name = $name_ready->recv; print "\nHello, $name\n"; block 12年4月14日星期六
  57. AnyEvent Condvar ୊໨ɿ ఏࣔ࢖༻ऀ༌ೖ໊ࣈɼવޙҹग़ိɻ ೗ՌҰ௚ᔒ༗༌ೖɼ㑌ޒඵ৊ҹ㘤ଉ࠵Ұ Լɻ 12年4月14日星期六

  58. use AnyEvent; $|=1; my $name_ready = AE::cv; my $angel =

    AE::io \*STDIN, 0, sub { my $x = <STDIN>; $name_ready->send($x); }; my $daemon = AE::timer 5, 5, sub { print "\n\nHurry up!!\nEnter your name> "; }; print "Enter your name> "; my $name = $name_ready->recv; print "\nHello, $name\n"; block 12年4月14日星期六
  59. use AnyEvent; $|=1; my $exit = AE::cv { print "gg\n"

    }; my $name_ready = AE::cv { my ($cv) = @_; print "\nHello, " . $cv->recv; $exit->send; }; my $angel = AE::io \*STDIN, 0, sub { my $x = <STDIN>; $name_ready->send($x); }; my $daemon = AE::timer 5, 5, sub { print "\n\nHurry up!!\nEnter your name> "; }; print "Enter your name> "; $exit->recv; 12年4月14日星期六
  60. use AnyEvent; $|=1; my $exit = AE::cv { print "gg\n"

    }; my $name_ready = AE::cv { my ($cv) = @_; print "\nHello, " . $cv->recv; $exit->send; }; my $angel = AE::io \*STDIN, 0, sub { my $x = <STDIN>; $name_ready->send($x); }; my $daemon = AE::timer 5, 5, sub { print "\n\nHurry up!!\nEnter your name> "; }; print "Enter your name> "; $exit->recv; 12年4月14日星期六
  61. use AnyEvent; $|=1; my $exit = AE::cv { print "gg\n"

    }; my $name_ready = AE::cv { my ($cv) = @_; print "\nHello, " . $cv->recv; $exit->send; }; my $angel = AE::io \*STDIN, 0, sub { my $x = <STDIN>; $name_ready->send($x); }; my $daemon = AE::timer 5, 5, sub { print "\n\nHurry up!!\nEnter your name> "; }; print "Enter your name> "; $exit->recv; 12年4月14日星期六
  62. AnyEvent Convdar ໛ٖ State Machine / Workflow Start Working End

    12年4月14日星期六
  63. use v5.14; use AnyEvent; my ($start, $working, $end); $start =

    AE::cv { say "START"; $working->send; }; $working = AE::cv { say "WORKING for 5s"; my $w; $w = AE::timer 5, 0, sub { say "DONE working."; $end->send; undef $w; } }; $end = AE::cv { say "END"; }; $start->send; $end->recv; 12年4月14日星期六
  64. use v5.14; use AnyEvent; my ($start, $working, $end); $start =

    AE::cv { say "START"; $working->send; }; $working = AE::cv { say "WORKING for 5s"; my $w; $w = AE::timer 5, 0, sub { say "DONE working."; $end->send; undef $w; } }; $end = AE::cv { say "END"; }; $start->send; $end->recv; 12年4月14日星期六
  65. use v5.14; use AnyEvent; my ($start, $working, $end); $start =

    AE::cv { say "START"; $working->send; }; $working = AE::cv { say "WORKING for 5s"; my $w; $w = AE::timer 5, 0, sub { say "DONE working."; $end->send; undef $w; } }; $end = AE::cv { say "END"; }; $start->send; $end->recv; State State Handlers 12年4月14日星期六
  66. use v5.14; use AnyEvent; my ($start, $working, $end); $start =

    AE::cv { say "START"; $working->send; }; $working = AE::cv { say "WORKING for 5s"; my $w; $w = AE::timer 5, 0, sub { say "DONE working."; $end->send; undef $w; } }; $end = AE::cv { say "END"; }; $start->send; $end->recv; State Transition 12年4月14日星期六
  67. use v5.14; use AnyEvent; my ($start, $working, $end); $start =

    AE::cv { say "START"; $working->send; }; $working = AE::cv { say "WORKING for 5s"; my $w; $w = AE::timer 5, 0, sub { say "DONE working."; $end->send; undef $w; } }; $end = AE::cv { say "END"; }; $start->send; $end->recv; ਐೖ ࣄ݅᫮ᅲ 12年4月14日星期六
  68. anyEvent::CondVar • $cv ~ mini event loop • $cv->send →

    $cv->recv • $cv->send → $cv callback • $cv->begin, $cv->end 12年4月14日星期六
  69. AnyEvent::* 12年4月14日星期六

  70. AnyEvent::HTTP http_get "http://gugod.org", sub { my ($data, $headers) = @_;

    ... }; ... 12年4月14日星期六
  71. AnyEvent::HTTP sub parallel_get { my @urls = @_; my $all_done

    = AE::cv; for my $url (@urls) { $all_done->begin; my $t = time; http_get $url, sub { my ($data, $headers) = @_; ... $all_done->end; }; } $all_done->recv; ... } 12年4月14日星期六
  72. AnyEvent::DBI use AnyEvent::DBI; my $cv = AnyEvent->condvar; my $dbh =

    new AnyEvent::DBI "DBI:SQLite:dbname=test.db", "", ""; $dbh->exec("select * from orders where owner=?", "gugod", sub { my ($dbh, $rows, $rv) = @_; $#_ or die "failure: $@"; print "@$_\n" for @$rows; $cv->send; }); # asynchronously do sth. else here $cv->recv; 12年4月14日星期六
  73. AnyEvent::Socket tcp_connect "gameserver.deliantra.net", 13327, sub { my ($fh) = @_

    or die "gameserver.deliantra.net connect failed: $!"; # enjoy your filehandle ... }; 12年4月14日星期六
  74. AnyEvent::Socket tcp_server undef, 8888, sub { my ($fh, $host, $port)

    = @_; syswrite $fh, "The internet is full, $host: $port. Go away!\015\012"; }; 12年4月14日星期六
  75. AnyEvent::IRC my $c = AnyEvent->condvar; my $con = new AnyEvent::IRC::Connection;

    $con->connect ("irc.server.net", 6667); $con->reg_cb ( 'connect' => sub { my ($con) = @_; $con->send_msg (NICK => 'testbot'); $con->send_msg (USER => 'testbot', '*', '0', 'testbot'); }, 'irc_001' => sub { my ($con) = @_; print "$_[1]->{prefix} says I'm in the IRC: $_[1]->{params}->[-1]!\n"; $c->broadcast; } ); $c->wait; 12年4月14日星期六
  76. AnyEvent::XMPP • ޭೳ׬੔త XMPP (Jabber) ௨㘤໛૊ • ࢧԉଟॏாᥒ 12年4月14日星期六

  77. my $cl = AnyEvent::XMPP::Client->new(); $cl->set_accounts({ 'bob@gmail.com' => [ 'PASSWORD', 'talk.google.com',

    5223, { domain => 'gmail.com', old_style_ssl => 1 } ], 'alice@gmail.com' => [ 'PASSWORD', 'talk.google.com', 5223, { domain => 'gmail.com', old_style_ssl => 1 } ] }); $cl->reg_cb( message => sub { my ($client, $account, $message) = @_; $client->send_message("Nihao", $message->from, $account->jid); }, contact_request_subscribe => sub { my ($client, $account, $roster, $contact, $message) = @_; $contact->send_subscribed; }, disconnect => sub { my $client = shift; $client->update_connections; } ); $cl->start; 12年4月14日星期六
  78. AnyEvent::MP • Or “AEMP” • ଟߦఔؒ㘤ଉၚᬇ (Message Passing) • IPC,

    ୠՄލओػ • 㘤ଉ༗Ճີʢshared secretʣ • Erlang ෩ 12年4月14日星期六
  79. Seeder Sender Receiver 12年4月14日星期六

  80. Seeder Sender Receiver ొه ొه 12年4月14日星期六

  81. Seeder Sender Receiver ੥໰๭๭๭ ి࿩زᥒʁ 12年4月14日星期六

  82. Seeder Sender Receiver ੥໰๭๭๭ ి࿩زᥒʁ 39Keh7acE 12年4月14日星期六

  83. Seeder Sender Receiver “OHAI” 12年4月14日星期六

  84. Seeder Sender Receiver Receiver Receiver Receiver 12年4月14日星期六

  85. Seeder Sender Receiver ੥໰๭๭๭ ి࿩زᥒʁ Receiver Receiver Receiver 12年4月14日星期六

  86. Seeder Sender Receiver ੥໰๭๭๭ ి࿩زᥒʁ 42each88cE, u93keute7, u9ehknoh, e77e8u78e Receiver

    Receiver Receiver 12年4月14日星期六
  87. Seeder Sender Receiver Receiver Receiver Receiver “OHAI” 12年4月14日星期六

  88. use AnyEvent; use AnyEvent::MP; use AnyEvent::MP::Global; configure nodeid => "eg_receiver",

    binds => ["*:4040"]; my $port = port; grp_reg eg_receivers => $port; rcv $port, test => sub { my ($data, $reply_port) = @_; print "Received data: " . $data . "\n"; }; AnyEvent->condvar->recv; aemp receiver 12年4月14日星期六
  89. aemp sender use AnyEvent; use AnyEvent::MP; use AnyEvent::MP::Global; configure nodeid

    => "eg_sender", seeds => ["*:4040"]; my $find_timer = AnyEvent->timer (after => 0, interval => 1, cb => sub { my $ports = grp_get "eg_receivers" or return; snd $_, test => time for @$ports; }); AnyEvent->condvar->recv; 12年4月14日星期六
  90. AnyEvent::Worker my $worker = AnyEvent::Worker->new( sub { my @args =

    @_; ... } ); $worker->do("foo", "bar"); 12年4月14日星期六
  91. AnyEvent::Worker # fork 4 worker processes # put them in

    to a round-robin queue my $workers = Data::RoundRobin->new( map { AnyEvent::Worker->new(sub { ... }) } 1..4 ); $workers->next->do("foo", "bar"); # worker 1 $workers->next->do("this", "that"); # worker 2 $workers->next->do("ok", "go"); # workes 3 ... 12年4月14日星期六
  92. • German • Redis/Hiredis • BitTorrent • CouchDB • Cron

    • TermKey • DNS • JSONRPC • Monitor • MSN • RabbitMQ • SMTP • Twitter • ZeroMQ • Memcached • Twiggy AnyEvent::* 12年4月14日星期六
  93. More or Less • AnyEvent ੋᙛԼओྲྀ • AnyEvent::Intro • condvar

    = Fascinating • AnyEvent::* ༗ڐଟሞ • github.com/CPAN-API/metacpan-web 12年4月14日星期六
  94. Q? 12年4月14日星期六

  95. Enjoy Event Hacking! 12年4月14日星期六

  96. ↺ END 12年4月14日星期六