2013/12/05

Futures advent day 5

Day 5 - Causing a Future to fail

Yesterday we looked at how to handle a Future that has failed. We can cause a Future to fail by invoking its fail method instead of done.

sub GET_checked
{
  my ( $url ) = @_;

  GET( $url )->then( sub {
    my ( $r ) = @_;
    if( $r->code !=~ m/^[23]../ ) {
      return Future->new->fail( $r->code." ".$r->message );
    }
    else {
      return Future->new->done( $r );
    }
  });
}

Here we have created a checked version of our hypothetical HTTP GET function, which returns a Future that will only be successful if the HTTP response was in the 2xx or 3xx ranges. If it gets an error (4xx or 5xx) then the Future will fail.

Another way to cause a future to fail is to simply throw a regular perl exception from a then or else code block. Each call to a code reference passed to these methods is wrapped in a eval {} block and causes the future to fail if the code throws an exception. This makes it easier to handle because now the chained future will fail, rather than causing the code that marked the preceding future as complete to propagate the exception it threw.

my $f = GET_checked("http://my-site-here.com/products.xml")
  ->then( sub {
    my ( $response ) = @_;
    if( $response->content_type ne "text/xml" ) {
      die "Expected Content-type: text/xml";
    }
    return Future->new->done( $response );
  });

This code is equivalent to code which uses Future->new->fail - the caller will not directly die with that exception, but instead the returned future will fail.

<< First | < Prev | Next >

2013/12/04

Futures advent day 4

Day 4 - Coping with failure

So far every Future example we have seen has resulted in an eventual success. But, like regular perl functions can either return a result or die() an exception, so too can Futures either complete with a result or a failure. Whereas the on_done method attaches code to handle a successful result we can use on_fail similarly to attach code to handle a failure.

my $f = GET("http://my-site-here.com/");

$f->on_done( sub {
  my ( $response ) = @_;
  print "Got a response\n";
});

$f->on_fail( sub {
  my ( $failure ) = @_;
  print STDERR "It failed: $failure\n";
});

If the get method receives a failure on the Future instead of a success, it throws the message from that failure as a perl exception, causing the method to die instead of return. This leads to many cases of Future code being able to look and act very similarly to regular perl function calls, with values being returned, or exceptions being thrown and caught.

<< First | < Prev | Next >

2013/12/03

Futures advent day 3

Day 3 - Chaining Futures to perform a sequence of actions

A more powerful ability than on_done, and one which in practice turns out to be used much more often, is provided by the then method. This method itself returns a Future, and expects code that will return a Future when it is invoked. In this way it provides an ability to perform a second action which returns a Future after the first one completes, and returns a Future that represents the complete combination of the first and the second.

my $f = GET("http://my-site-here.com/first")
  ->then( sub {
    my ( $first_response ) = @_;
    my $path = path_from_response($first_response);
    GET("http://my-site-here.com/$path);
  });

my $second_response = $f->get;

In this second example after the first page has been returned we then fetch a second page by somehow using the result of the first page's response to give a path name for the second. The returned Future in $f will be complete after this second response has been received. The call to get will then wait for this to happen.

Of course, we are not limited to simply two actions - because the then method returns another Future, we can simply call then on that as well to chain as many steps of a process as are necessary to complete it. This leads to a neat sequence of code, quite unlike the ever-indenting nature of passing callback functions.

<< First | < Prev | Next >

2013/12/02

Futures advent day 2

Day 2 - Doing something when a Future completes

Because a Future object represents an operation that is currently in progress or has finished it provides the ideal place to attach logic to ask for further activity to happen when the original has completed. The most immediate way is to pass a piece of code in a sub reference to the on_done method.

my $f = GET("http://my-site-here.com/");

$f->on_done( sub {
  my ( $response ) = @_;
  print "Got a response\n";
});

In this case, as for many posts yet to come, we are presuming some hypothetical GET function that returns a Future wrapping an HTTP operation in the obvious manner.

If the returned Future is already complete (perhaps because it is a synchronous client that always completes immediately, or because it was served internally from a cache) then the on_done method invokes the code immediately. If not, the code is stored inside the Future object itself for when it eventually gets its result.

<< First | < Prev | Next >

2013/12/01

Futures advent day 1

It is traditional around this time of year for Perl blogs to publish an advent calendar - a series of 24 short little posts around a common theme.

People have suggested I might write one about Futures, so here goes...

Day 1 - Futures can return values synchronously

You don't in fact have to use a Future for anything asynchronous. A simple synchronous-returning function can use them too.

use Future;

sub sum
{
   my @numbers = @_;
   my $total = 0;
   $total += $_ for @numbers;
   return Future->new->done( $total );
}

say "The total is " . sum( 10, 20, 30 )->get;

It may not be immediately obvious currently why you want to do this, but I hope to motivate why over the following 23 posts...

<< First | < Prev | Next >

2013/10/23

Parallel Name Resolving using IO::Async

Perl has a variety of modules and frameworks that allow multiple, parallel operations at once. Some are specific to one kind of task, and some more generic. One of the larger general-purpose ones is IO::Async.

IO::Async provides an abstraction around the system name resolver, getaddrinfo(), allowing it to be called asynchronously to resolve a number of names at once, and returning results later as they arrive.

To do this, start with the resolver object itself. This can be obtained from the underlying IO::Async::Loop object. We do not actually need to keep a reference to the Loop, as the resolver will keep that itself.

use IO::Async::Loop;

my $resolver = IO::Async::Loop->new->resolver;

Next, call the getaddrinfo method on it, passing in the details of the name lookup required, and collect the result list. We need to pass a hint to the method so that it returns just one kind of socket address rather than iterating all the possible kinds. Since we only care about the IP address and not the service port number it doesn't matter too much what hint we pass, but one of the simpler ones is to ask for the stream socket type (i.e. TCP ports).

my @results = $resolver->getaddrinfo(
   host     => "www.google.com",
   socktype => 'stream',
)->get;

This method is intended for creating packed socket address structures for passing directly to connect() or bind(), so to obtain a human-readable string it will need converting back to a printable numeric form by using Socket::getnameinfo(). We need to pass in the NI_NUMERICHOST flag in order to have it return a plain numeric IP address instead of reverse resolving that address back into a name. The numeric address string itself will come in the second positional result from getnameinfo(), so we will have to use a list slice operator to return just that.

use Socket qw( getnameinfo NI_NUMERICHOST );

my @addrs = map { ( getnameinfo $_->{addr}, NI_NUMERICHOST )[1] }
            @results;

print "$_\n" for @addrs;

This yields the list of IP addresses for this one hostname:

2a00:1450:4009:809::1014
173.194.34.112
173.194.34.115
173.194.34.113
173.194.34.116
173.194.34.114

The reason for the get method here is that, like (almost) all of the IO::Async methods that perform a single asynchronous operation, the getaddrinfo method returns a Future. A Future is an object representing an outstanding operation that may not yet be complete. In this first simple example we simply wanted to wait for that operation to complete, so we forced it by calling the get method on it. This method waits for the Future to be complete then returns its result.

Of course, the entire reason for our using IO::Async was to perform multiple operations at the same time, and wait concurrently for them all to complete. So rather than calling get on each individual getaddrinfo future, we can combine them all together into a single future that needs them all to complete before it itself is considered completed.

my @hosts = qw( www.google.com www.facebook.com www.iana.org );

my @futures = map {
   my $host = $_;
   $resolver->getaddrinfo(
      host     => $host,
      socktype => 'stream',
   )
} @hosts;

my @results = Future->needs_all( @futures )->get;

my @addrs = map { ( getnameinfo $_->{addr}, NI_NUMERICHOST )[1] }
                @results;

print "$_\n" for @addrs;

This now yields:

2a00:1450:4009:809::1011
173.194.41.180
173.194.41.177
173.194.41.179
173.194.41.176
173.194.41.178
2a03:2880:f00a:401:face:b00c:0:1
31.13.72.65
2620:0:2d0:200::8
192.0.32.8

Oh dear. Unfortunately, the needs_all future has simply concatenated all of the individual results together, so we have lost track of which host has which addresses. To solve this, we can make each individual host future return not a list of its results, but a two-element list containing its hostname and an ARRAY ref of the IP addresses it resolved to. That way, when we fetch the results of the overall needs_all future we will have an even-sized name-value list, perfect for assigning into a hash.

To do this we can have each host future be a two-stage operation, consisting of first the getaddrinfo call, and then altering its result using a code block passed to the transform method.

my @futures = map {
   my $host = $_;
   $resolver->getaddrinfo(
      host     => $host,
      socktype => 'stream',
   )->transform(
      done => sub {
         my @results = @_;
         my @addrs = map { (getnameinfo $_->{addr}, NI_NUMERICHOST)[1] }
                         @results;
         return ( $host, \@addrs );
      }
   );
} @hosts;

my %addrs = Future->needs_all( @futures )->get;

use Data::Dump 'pp';
print STDERR pp(\%addrs);

Now we retain the mapping from hostnames to the list of IP addresses they resolved to:

{
  "www.facebook.com" => ["2a03:2880:f00a:201:face:b00c:0:1", "31.13.72.1"],
  "www.google.com"   => [
                          "2a00:1450:4009:809::1010",
                          "173.194.41.177",
                          "173.194.41.178",
                          "173.194.41.180",
                          "173.194.41.179",
                          "173.194.41.176",
                        ],
  "www.iana.org"     => ["2620:0:2d0:200::8", "192.0.32.8"],
}

Now, this post is fairly obviously written in response to Parallel DNS lookups using AnyEvent but from the perspective of IO::Async instead. Asides from the choice of event system, two important differences should be observed:

  • Through the use of futures, this example manages both the flow of control and data along with it. It does not need to declare variables that get captured by callback functions to cause data to flow separately from the way it uses an object to handle the flow of control. Each future object yields its result, and the individual futures can form linear flows by using transform or other methods to return different results, or needs_all or other methods to combine individual futures into larger ones.
  • Nowhere in the above did I mention DNS. This is intentional. IO::Async's getaddrinfo resolver really is an asynchronous wrapper around the underlying Socket function of the same name. Because of this it uses the system's standard name resolver as provided by libc, ensuring it will yield the same identical results as any other program using the system resolver, regardless of whether libc is configured to use DNS, files, LDAP, or any other resolution method. It also automatically handles IPv6 if the underlying system does; returning a mixture of IPv4 and IPv6 addresses in the host's preferred order. The caller does not need to be aware of the subtle distinctions of RFC 3484 sorting order, for example.

2013/09/30

Perl - Tickit - 0.40

Latest Tickit version (0.40) is now up on CPAN. Recent changes include:

  • Mouse drag-and-drop events (0.32)

    Windows now create more interesting events to represent mouse drag-and-drop operations. Starting, moving, and ending a mouse-drag all create events that can help widgets render more interesting behaviours.

  • Added Tickit::RenderBuffer (0.33)

    RenderBuffer is an in-memory buffer to store content that will eventually be rendered to the terminal. In effect it stores a double-buffer of content, allowing widgets to draw in whatever order is most convenient for them, before efficiently flushing it in a top-to-bottom manner.

    Being implemented in C/XS instead of Perl allows it to operate more efficiently than the previous-generation direct rendering to Windows. As it stores the content before rendering it also allows better handling of Unicode line-drawing characters; allowing for characters to be merged together out of multiple line segments, creating the ability for complex line-drawing shapes to be easily rendered.

  • Added timer support to core event loop (0.34)

    Widgets can now react to timed events, allowing for animation effects and other behaviours.

  • Focus management (0.34 and 0.35)

    Container widgets now provide management of the focus-chain order of their children, allowing the whole widget tree to maintain the "next" and "previous" direction of focusing. The base code handles the <Tab> and <Shift-Tab> key events to cycle input focus around the widget tree automatically.

  • Use RenderBuffer in favour of direct Window rendering (0.40)

    Now that RenderBuffer is in the core distribution, all widgets should be using it rather the previous render method to render directly on the window. This is a stepping-stone change to allow for further improvements.

  • New sizing model (0.40)

    New methods are provided by the base Tickit::Widget class that cache the requested size of the widget, and only inform the parent container when this size actually changes. This allows for more efficient reshaping and redrawing operations when widgets change their size requirements, without needing to recalculate the whole widget tree.

These final two changes help support a couple of interesting planned improvements:

  • Widget minimal/maximal size handling

    Because the base widget class now handles size information more directly, it can implement bounds on minimal and maximal widget size. These will be derived from Tickit::Style. It may also be possible to consider padding and margin controls in the base widget class and thus automatically apply to every widget.

  • Whole-tree rendering via RenderBuffer

    By adding area masks to RenderBuffer it should then support being used as a single buffer object to render the entire window hierarchy. Because the masking code will be implemented in C code, it will much more efficient than the current pure-perl Window-based solution involving visibility testing per character cell. This will allow for much more better redrawing performance.

    This will also allow RenderBuffer to move out of the Tickit.xs file and into libtickit itself, where it can be useful to non-Perl code (such as native C programs or other language bindings).