Amanda-2.6.1

Amanda::MainLoop


NAME

Amanda::MainLoop - Perl interface to the Glib MainLoop


SYNOPSIS

    use Amanda::MainLoop;
    
    my $to = Amanda::MainLoop::timeout_source(2000);
    $to->set_callback(sub { 
        print "Time's Up!\n";
        $to->remove();              # dont' re-queue this timeout
        Amanda::MainLoop::quit();   # return from Amanda::MainLoop::run
    });
    Amanda::MainLoop::run();

Note that all functions in this module are individually available for export, e.g.,

    use Amanda::MainLoop qw(run quit);


OVERVIEW

The main event loop of an application is a tight loop which waits for events, and calls functions to respond to those events. This design allows an IO-bound application to multitask within a single thread, by responding to IO events as they occur instead of blocking on particular IO operations.

The Amanda security API, transfer API, and other components rely on the event loop to allow them to respond to their own events in a timely fashion.

The overall structure of an application, then, is to initialize its state, register callbacks for some events, and begin looping. When some application-defined state is reached, the loop is terminated and the application cleans up and exits.

LOOPING

The actual event loop takes place within a call to Amanda::MainLoop::run(). This function executes until a call to Amanda::MainLoop::quit() occurs, at which point run() returns. You can check whether the loop is running with Amanda::MainLoop::is_running().

In some cases, a sub should only run after the loop has started. The most common case is when a callback may call Amanda::MainLoop::quit immediately. In this circumstance, use call_later, which takes a subref and an arbitrary number of arguments for that sub:

    my $cb = sub {
        my ($letters, $digits) = @_;
        # ..
        Amanda::MainLoop::quit();
    };
    Amanda::MainLoop::call_later($cb, "abc", 123);
    # ..
    Amanda::MainLoop::run();

Similarly, a tight loop of callbacks with no blocking operations can lead to excessive stack consumption. In this case, call_later() is useful, too. It queues the callback to run at the next cycle of the MainLoop:

    sub might_delay {
        my ($cb) = @_;
        if (can_do_it_now()) {
            my $result = do_it();
            Amanda::MainLoop::call_later($cb, $result)
        } else {
            # ..
        }
    }

EVENT SOURCES

An event source is responsible for producing events. A source may produce multiple events over its lifetime.

The method $src-set_callback(\&cb)> sets the function that will be called for a given source, and ``attaches'' the source to the main loop so that it will begin generating events. The arguments to the callback depend on the event source, but the first argument is always the source itself. Unless specified, no other arguments are provided.

Event sources persist until they are removed with $src-remove()>, even if the source itself is no longer accessible from Perl. Although Glib supports it, there is no provision for ``automatically'' removing an event source. As an example:

  sub start_timer { 
    my ($loops) = @_;
    Amanda::MainLoop::timeout_source(200)->set_callback(sub {
      my ($src) = @_;
      print "timer\n";
      if (--$loops <= 0) {
        $src->remove();
        Amanda::MainLoop::quit();
      }
    });
  }
  start_timer(10);
  Amanda::MainLoop::run();

Timeout

  my $src = Amanda::MainLoop::timeout_source(10000);

A timeout source will create events at the specified interval, specified in milliseconds (thousandths of a second). The events will continue until the source is destroyed.

Idle

  my $src = Amanda::MainLoop::idle_source(2);

An idle source will create events continuously except when a higher-priority source is emitting events. Priorities are generally small positive integers, with larger integers denoting lower priorities. The events will continue until the source is destroyed.

Child Watch

  my $src = Amanda::MainLoop::child_watch_source($pid);

A child watch source will issue an event when the process with the given PID dies. To avoid race conditions, it will issue an event even if the process dies before the source is created. The callback is called with three arguments: the event source, the PID, and the child's exit status.

Note that this source is totally incompatible with any thing that would cause perl to change the SIGCHLD handler. If SIGCHLD is changed, under some circumstances the module will recognize this circumstance, add a warning to the debug log, and continue operating. However, it is impossible to catch all possible situations.

File Descriptor

  my $src = Amanda::MainLoop::fd_source($fd, G_IO_IN);

This source will issuen an event whenever one of the given conditions is true for the given file descriptor. The conditions are from Glib's GIOCondition, and are $G_IO_IN, G_IO_OUT, $G_IO_PRI, $G_IO_ERR, $G_IO_HUP, and $G_IO_NVAL. These constants are available with the import tag :GIOCondition.

Generally, when reading from a file descriptor, use $G_IO_IN|$G_IO_HUP to ensure that an EOF triggers an event as well. Writing to a file descriptor can simply use $G_IO_OUT.


RELATION TO GLIB

Glib's main event loop is described in the Glib manual: http://library.gnome.org/devel/glib/stable/glib-The-Main-Event-Loop.html. Note that Amanda depends only on the functionality available in Glib-2.2.0, so many functions described in that document are not available in Amanda. This module provides a much-simplified interface to the glib library, and is not intended as a generic wrapper for it. Specifically:

Amanda's perl-accessible main loop only runs a single GMainContext, and always runs in the main thread.
Besides idle sources, event priorities are not accessible from Perl.


PROGRAMMING HINTS

Most often, callbacks are short, and can be specified as anonymous subs:

    $src->set_callback(sub {
        my ($src) = @_;
        # ...
    });

There is currently no means in place to specify extra arguments for a callback when it is set. If the callback needs access to other data, it should use a Perl closure in the form of lexically scoped variables and a (possibly anonymous) sub:

    {
        my $total_results = 0;
        $src->set_callback(sub {
            my ($src, $result) = @_;
            $total_results += $result;
        });
    }

Event sources are often set up in groups, e.g., a long-term operation and a timeout. When this is the case, be careful that all sources are removed when the operation is complete. The easiest way to accomplish this is to include all sources in a lexical scope and remove them at the appropriate times:

    {
        my $op_src = long_operation_src();
        my $timeout_src = Amanda::MainLoop::timeout_source($timeout);
        sub finish {
            $op_src->remove();
            $timeout_src->remove();
        }
        $op_src->set_callback(sub {
            print "Operation complete\n";
            finish();
        });
        $timeout_src->set_callback(sub {
            print "Operation timed out\n";
            finish();
        });
    }

Both of these limitations may be lifted in future revisions of the Amanda::MainLoop manpage.


ABOUT THIS PAGE

This page was automatically generated Fri Feb 5 19:41:21 2010 from the Amanda source tree, and documents the most recent development version of Amanda. For documentation specific to the version of Amanda on your system, use the 'perldoc' command.


Amanda-2.6.1