Last active
January 28, 2020 10:16
-
-
Save brianmed/45784c1c49c12780722ee8f57f083fd1 to your computer and use it in GitHub Desktop.
Hopefully a fun read about asynchronous, non-blocking operations in Mojolicious with concurrent events via Mojo::IOLoop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use Mojo::Base -strict; | |
use Mojo::IOLoop; | |
use Mojo::IOLoop::Delay; | |
say("Starting"); | |
Mojo::IOLoop::Delay->new->steps( | |
sub { | |
my $delay = shift; | |
Mojo::IOLoop->timer(3 => $delay->begin); | |
say("Second step in 3 seconds."); | |
}, | |
sub { | |
my ($delay, @args) = @_; | |
say("Now in second step."); | |
}, | |
)->wait; | |
say("Stopping"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use Mojo::Base -strict; | |
use Mojo::IOLoop; | |
use Mojo::IOLoop::Delay; | |
say("Starting"); | |
my $id = Mojo::IOLoop->recurring(0.5 => sub { | |
my $loop = shift; | |
say("recurring: " . scalar(localtime(time))); | |
}); | |
Mojo::IOLoop::Delay->new->steps( | |
sub { | |
my $delay = shift; | |
Mojo::IOLoop->timer(6 => $delay->begin); | |
say("Second step in 6 seconds."); | |
}, | |
sub { | |
my ($delay, @args) = @_; | |
Mojo::IOLoop->timer(1 => $delay->begin); | |
Mojo::IOLoop->timer(3 => $delay->begin); | |
say("Third step in 3 seconds."); | |
}, | |
sub { | |
my ($delay, @args) = @_; | |
say("Now in second step."); | |
}, | |
)->wait; | |
Mojo::IOLoop->remove($id); | |
say("Stopping"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is an attempt at explaining an asynchronous event loop in perl via | |
Mojo::IOLoop. | |
First, lets define asynchronous: | |
1. not occurring at the same time [2] | |
And, then a definition of an event loop: | |
An event loop is basically a loop that continually tests for external events | |
and executes the appropriate callbacks to handle them, it is often the main | |
loop in a program. Non-blocking tests for readability/writability of file | |
descriptors and timers are commonly used events for highly scalable network | |
servers, because they allow a single process to handle thousands of client | |
connections concurrently. [1] | |
So, we have a set of events from multiple sources that will transpire over a | |
given time frame and we desire processing them in a manner that most | |
efficiently utilizes resources. For example, if there are several inbound | |
network requests (e.g. browser requests) then the application requests via the | |
OS that each socket operation be put into the event loop's notification queue | |
where they are processed as time allows. | |
This event loop queuing process is called a non-blocking operation. | |
A non-blocking operation on the other hand lets the calling subroutine | |
continue execution even though the subroutine is not yet finished. Instead | |
of waiting, the calling subroutine passes along a callback to be executed | |
once the subroutine is finished, this is called continuation-passing | |
style. [3] | |
This event inspection and event processing is asynchronous, non-blocking and | |
allows for the facade of processing events at the exact same time, which is | |
the definition we will use for concurrent. | |
So, given these defintions and understanding of event loops and queues, how do | |
we utilze these in perl? The answer is, in part, Mojo::IOLoop. | |
Mojo::IOLoop is a very minimalistic event loop based on Mojo::Reactor, it | |
has been reduced to the absolute minimal feature set required to build | |
solid and scalable non-blocking TCP clients and servers. [4] | |
Let's look at a code example utilizing Mojo::IOLoop::Delay a very svelte | |
wrapper that will... manage callbacks and control the flow of events [5]. | |
A first example with a timer: | |
==== | |
1 use Mojo::Base -strict; | |
2 use Mojo::IOLoop; | |
3 use Mojo::IOLoop::Delay; | |
4 | |
5 say("Starting"); | |
6 | |
7 Mojo::IOLoop::Delay->new->steps( | |
8 sub { | |
9 my $delay = shift; | |
10 | |
11 Mojo::IOLoop->timer(3 => $delay->begin); | |
12 | |
13 say("Second step in 3 seconds."); | |
14 }, | |
15 sub { | |
16 my ($delay, @args) = @_; | |
17 | |
18 say("Now in second step."); | |
19 }, | |
20 )->wait; | |
21 | |
22 say("Stopping"); | |
==== | |
What should be noted is that the steps in the delay will be executed at a | |
later time; however, they will be executed in the order that they are defined. | |
Basically, sub at line 8 and 15 are added into the event loop's queue. | |
The next example will introduce concurrent events via a recurring timer and | |
Mojo::IOLoop::Delay. | |
==== | |
use Mojo::Base -strict; | |
use Mojo::IOLoop; | |
use Mojo::IOLoop::Delay; | |
say("Starting"); | |
my $id = Mojo::IOLoop->recurring(0.7 => sub { | |
my $loop = shift; | |
say("recurring: " . scalar(localtime(time))); | |
}); | |
Mojo::IOLoop::Delay->new->steps( | |
sub { | |
my $delay = shift; | |
Mojo::IOLoop->timer(6 => $delay->begin); | |
say("Second step in 6 seconds."); | |
}, | |
sub { | |
my ($delay, @args) = @_; | |
Mojo::IOLoop->timer(1 => $delay->begin); | |
Mojo::IOLoop->timer(3 => $delay->begin); | |
say("Third step in 3 seconds."); | |
}, | |
sub { | |
my ($delay, @args) = @_; | |
say("Now in second step."); | |
}, | |
)->wait; | |
Mojo::IOLoop->remove($id); | |
say("Stopping"); | |
==== | |
In the above example, our event loop has a recurring timer and a set of | |
serialized subs which contain timers that determine when the next step will | |
take place. Of particular interest is that the recurring timer is happening | |
concurrently while the Mojo::IOLoop::Delay's steps are in progress. | |
Truly, a cool feat. | |
[1]: http://mojolicious.org/perldoc/Mojolicious/Guides/FAQ#What-is-an-event-loop | |
[2]: http://www.dictionary.com/browse/asynchronous | |
[3]: http://mojolicious.org/perldoc/Mojolicious/Guides/FAQ#What-is-the-difference-between-blocking-and-non-blocking-operations | |
[4]: http://mojolicious.org/perldoc/Mojo/IOLoop | |
[5]: http://mojolicious.org/perldoc/Mojo/IOLoop/Delay |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment