The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Async::Selector - level-triggered resource observer like select(2)

VERSION

0.02

SYNOPSIS

    use Async::Selector;
    
    my $selector = Async::Selector->new();
    
    ## Register resource
    my $resource = "some text.";  ## 10 bytes
    $selector->register(
        resource_A => sub {
            my $threshold = shift;
            return length($resource) >= $threshold ? $resource : undef;
        }
    );
    
    
    ## Select the resource with a callback.
    $selector->select(
        sub {
            my ($id, %resource) = @_;
            print "$resource{resource_A}\n";
            return 1;
        },
        resource_A => 20,  ## Tell me when the resource gets more than 20 bytes!
    );
    
    
    ## Append data to the resource
    $resource .= "data";  ## 14 bytes
    $selector->trigger('resource_A'); ## Nothing happens
    
    $resource .= "more data";  ## 23 bytes
    $selector->trigger('resource_A'); ## The callback prints 'some text.datamore data'

DESCRIPTION

Async::Selector is an object that observes registered resources and executes callbacks when some of the resources are available. Thus it is an implementation of the Observer pattern like Event::Notify, but the important difference is that Async::Selector is level-triggered like select(2) system call.

Basic usage of Async::Selector is as follows:

  1. Register as many resources as you like by register() method.

    A resource has its name and resource provider. A resource provier is a subroutine reference that returns some data (or undef if it's not available).

  2. Select as many resources as you like by select() method.

    When any of the selected resources gets available, a callback function is executed with the available resource data.

    Note that if some of the selected resources is available when calling select() method, it executes the callback function immediately. That's because Async::Selector is level-triggered.

  3. Notify the Async::Selector object by trigger() method that some of the registered resources have changed.

    The Async::Selector object then checks if any of the selected resources gets available. In this case the callback function given by select() method is executed.

CLASS METHODS

$selector = Async::Selector->new();

Creates an Async::Selector object. It takes no parameters.

OBJECT METHODS

$selector->register($name => $provider->($condition_input), ...);

Registers resources with the object. A resource is described as a pair of resource name and resource provider. You can register as many resources as you like.

The resource name ($name) is an arbitrary string. It is used to select the resource in select() method. If $name is already registered with $selector, the resource provider is updated with $provider and the old one is discarded.

The resource provider ($provider) is a subroutine reference. Its return value is supposed to be a scalar data of the resource if it's available, or undef if it's NOT available.

$provider subroutine takes a scalar argument ($condition_input), which is given in arguments of select() method. $provider can decide whether to provide the resource according to $condition_input.

register() method returns $selector object itself.

$selector->unregister($name, ...);

Unregister resources from $selector object.

$name is the name of the resource you want to unregister. You can unregister as many resources as you like.

unregister() returns $selector object itself.

$selection_id = $selector->select($callback->($selection_id, %resources), $name => $condition_input, ...);

Selects resources. A resource selection is described as a pair of resource name and condition input for the resource. You can select as many resources as you like.

$callback is a subroutine reference that is executed when any of the selected resources gets available. Its first argument $selection_id is the ID for this selection. It is the same value as the ID returned from select() method. The other argument (%resources) is a hash whose key is the resource name and value is the resource data. Note that some values in %resources can be undef, meaning that those resources are not available. Note also that $callback is executed before select() method returns if some of the selected resources is already available.

$callback is supposed to return a boolean value. If the return value is true, the selection is automatically canceled after the execution of $callback. If the return value is false, the selection remains.

$name is the resource name that you want to select. It is the name given in register() method.

$condition_input describes the condition the resource has to meet to be considered as "available". $condition_input is an arbitrary scalar, and it's interpretation is up to the resource provider.

select() method returns an ID for the selection ($selection_id), which can be used to cancel the selection in cancel() method. If $callback is executed before select() returns and $callback returns true, select() returns undef because the selection is already removed.

If no resource selection ($name => $condition_input pair) is specified, select() method silently ignores it. As a result, it returns undef and the $callback is never executed.

$selection_id = $selector->select_lt(...);

select_lt() method is an alias for select() method.

$selection_id = $selector->select_et(...);

This method is just like select() method but it emulates edge-triggered selection.

To emulate edge-triggered behavior, select_et() won't execute the $callback at the time of selection. The $callback is executed only when some of the selected resources gets available via trigger() method.

$selector->cancel($selection_id, ...);

Cancel selections so that their callback functions won't be executed.

$selection_id is the selection ID you want to cancel. It is returned by select() method. You can specify as many $selection_ids as you like.

cancel() method returns $selector object itself.

$selector->trigger($name, ...);

Notify $selector that the resources specified by $names may be changed.

$name is the name of the resource that have been changed. You can specify as many $names as you like.

trigger() method returns $selector object itself.

@resouce_names = $selector->resources();

Returns the list of registered resource names.

@selection_ids = $selector->selections();

Returns the list of currently active selection IDs.

EXAMPLES

Multiple resources, multiple selections

    my $selector = Async::Selector->new();
    my $a = 5;
    my $b = 6;
    my $c = 7;
    $selector->register(
        a => sub { my $t = shift; return $a >= $t ? $a : undef },
        b => sub { my $t = shift; return $b >= $t ? $b : undef },
        c => sub { my $t = shift; return $c >= $t ? $c : undef },
    );
    $selector->select(
        sub {
            my ($id, %res) = @_;
            print "Select 1: a is $res{a}\n";
            return 1;
        },
        a => 10
    );
    $selector->select(
        sub {
            my ($id, %res) = @_;
            foreach my $key (sort keys %res) {
                next if not defined($res{$key});
                print "Select 2: $key is $res{$key}\n";
            }
            return 1;
        },
        a => 12, b => 15, c => 15,
    );

    ($a, $b, $c) = (11, 14, 14);
    $selector->trigger(qw(a b c));  ## -> Select 1: a is 11
    print "---------\n";
    ($a, $b, $c) = (12, 14, 20);
    $selector->trigger(qw(a b c));  ## -> Select 2: a is 12
                                    ## -> Select 2: c is 20

Auto-cancel and non-cancel selections

    my $selector = Async::Selector->new();
    my $A = "";
    my $B = "";
    $selector->register(
        A => sub { my $in = shift; return length($A) >= $in ? $A : undef },
        B => sub { my $in = shift; return length($B) >= $in ? $B : undef },
    );

    my $sel_a = $selector->select(
        sub {
            my ($id, %res) = @_;
            print "A: $res{A}\n";
            return 1; ## auto-cancel
        },
        A => 5
    );
    my $sel_b = $selector->select(
        sub {
            my ($id, %res) = @_;
            print "B: $res{B}\n";
            return 0; ## non-cancel
        },
        B => 5
    );

    ## Trigger the resources.
    ## Execution order of selection callbacks is not guaranteed.
    ($A, $B) = ('aaaaa', 'bbbbb');
    $selector->trigger('A', 'B');   ## -> B: bbbbb
                                    ## -> A: aaaaa
    print "--------\n";
    ## $sel_a is automatically canceled.
    ($A, $B) = ('AAAAA', 'BBBBB');
    $selector->trigger('A', 'B');   ## -> B: BBBBB
    print "--------\n";

    $B = "CCCCCCC";
    $selector->trigger('A', 'B');        ## -> B: CCCCCCC
    print "--------\n";

    $selector->cancel($sel_b);
    $selector->trigger('A', 'B');        ## Nothing happens.

SEE ALSO

Event::Notify, Notification::Center

AUTHOR

Toshio Ito, <debug.ito at gmail.com>

BUGS

Please report any bugs or feature requests to bug-async-selector at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Async-Selector. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Async::Selector

You can also look for information at:

LICENSE AND COPYRIGHT

Copyright 2012 Toshio Ito.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.