Provided by: padre_1.00+dfsg-1_all bug

NAME

       Padre::Task - Padre Task API 3.0

SYNOPSIS

         # Fire a task that will communicate back to an owner object
         My::Task->new(
             owner      => $padre_role_task_object,
             on_run     => 'owner_run_method',
             on_status  => 'owner_status_method',
             on_message => 'owner_message_method',
             on_finish  => 'owner_finish_method',
             my_param1  => 123,
             my_param2  => 'abc',
         )->schedule;

         package My::Task;

         sub new {
             my $class = shift;
             my $self  = $class->SUPER::new(@_);

             # Check params and validate the task

             return $self;
         }

         sub prepare {
             my $self = shift;

             # Run after scheduling immediately before serialised to a worker

             return 0 if $self->my_last_second_abort_check;
             return 1; # Continue and run
         }

         sub run {
             my $self = shift;

             # Called in child, do the work here

             return 1;
         }

         sub finish {
             my $self = shift;

             # Called in parent after successful completion

             return 1;
         }

         1;

DESCRIPTION

       The Padre Task API implements support for background and parallel execution of code in the
       Padre IDE, and is based on the CPAN Process API.

       A Task Class is a class that completely encapsulates a single unit of work, describing not
       only the work to be done, but also how the unit of work is created, how is serialised for
       transport, and any initialisation or cleanup work needs to be done.

       A Task is a single self-contained unit of work, and is implemented as a single instance of
       a particular Task Class.

   The lifecycle of a Task object
       From the perspective of a task author, the execution of a task will occur in four distinct
       phases.

       1. Construction

       The creation of a task is always done completely independantly of its execution. Typically
       this is done via the "new" method, or something that calls it.

       This separate construction step allows validation of parameters in advance, as well as
       allowing bulk task pre-generation and advanced task management functionality such as
       prioritisation, queueing, throttling and load-balancing of tasks.

       2. Preparation

       Once a task has been constructed, an arbitrarily long time may pass before the code is
       actually run (if it is ever run at all).

       If the actual execution of the task will result in certain work being done in the parent
       thread, this work cannot be done in the constructor. And once created as an object, no
       futher task code will be called until the task is ready for execution.

       To give the author a chance to allow for any problems that may occur as a result of this
       delay, the Task API provides a preparation phase for the task via the "prepare" method.

       This preparation code is run in the parent thread once the task has been prioritised, has
       a worker allocated to it, and has been encapsulated in its Padre::TaskHandle, but before
       the object is serialised for transport into the thread.

       A task can use this preparation phase to detach from non-serialisable resources in the
       object such as database handles, to copy any interesting parent state late rather than
       early, or decide on a last-second self-abort.

       Once the preparation phase is completed the task will be serialised, transported into
       assigned worker thread and then executed immediately.

       Because it will execute in the parent thead, the rest of the Padre instance is available
       for use if needed, but the preparation code should run quickly and must not block.

       3. Execution

       The main phase of the task is where the CPU-intensive or blocking code can be safely run.
       It is run inside a worker thread in the background, without impacting on the performance
       of the parent thread.

       However, the task execution phase must be entirely self-contained.

       The worker threads not only do not have access to the Padre IDE variable structure, but
       most Padre classes (including heavily used modules such as Padre::Current) will not be
       loaded at all in the worker thread.

       Any output that needs to be transported back to the parent should be stored in the object
       somewhere. When the cleanup phase is run, these values will be available automatically in
       the parent.

       4. Cleanup

       When the execution phase of the task is completed, the task object will be serialised for
       transport back up to the parent thread.

       On arrival, the instance of the task in the parent will be gutted and its contents
       replaced with the contents of the version arriving from the child thread.

       Once this is complete, the task object will fire a "finish" handler allowing it to take
       action in the parent thread based on the work done in the child.

       This can include having the task contact any "owner" object that had commissioned the task
       in the first place.

METHODS

   new
         My::Task->new(
             owner      => $padre_role_task_object,
             on_run     => 'owner_run_method',
             on_status  => 'owner_status_method',
             on_message => 'owner_message_method',
             on_finish  => 'owner_finish_method',
             my_param1  => 123,
             my_param2  => 'abc',
         );

       The "new" method creates a new "task", a self-contained object that represents a unit of
       work to be done in the background (although not required to be done in the background).

       In addition to defining a set of method for you to provide as the task implementer, the
       base class also provides implements a "task ownership" system in the base class that you
       may use for nearly no cost in terms of code.

       This task owner system will consume three parameters.

       The optional "owner" parameter should be an object that inherits from the role
       Padre::Role::Task. Message and finish events for this task will be forwarded on to
       handlers on the owner, if they are defined.

       The optional "on_run" parameter should be the name of a method that can be called on the
       owner object, to be called once the task has started running and control of the worker
       message queue has been handed over to the task.

       The optional "on_message" parameter should be the name of a method that can be called on
       the owner object, to be called when a message arrives from the child object during its
       execution.

       The required (if "owner" was provided) "on_finish" parameter should be the name of a
       method that can be called on the owner object, to be called when the task has completed
       and returns to the parent from the child object.

       When implementing your own task, you should always call the "SUPER::new" method first, to
       ensure that integration with the task owner system is done.

       You can then check any other parameters, capture additional information from the IDE, and
       validate that the task is correctly requested and should go ahead.

       The creation of a task object does NOT imply that it will be executed, merely that the
       require for work to be done is validly formed. A task object may never execute, or may
       only execute significantly later than it was created.

       Anything that the task needs to do once it is certain that the task will be run should be
       done in the "prepare" method (see below).

       Returns a new task object if the request is valid, or throws an exception if the request
       is invalid.

   on_run
       The "on_run" accessor returns the name of the owner's "run" notification handler method,
       if one was defined.

   on_status
       The "on_status" accessor returns the name of the owner's status handler method, if one was
       defined.

   on_message
       The "on_message" accessor returns the name of the owner's message handler method, if one
       was defined.

   on_finish
       The "on_finish" accessor returns the name of the owner's finish handler method, if one was
       defined.

   as_string
       The "as_string" method is used to serialise the task into a string for transmission
       between the parent and the child (in both directions).

       By default your task will be serialised using Storable's "nfreeze" method, which is
       suitable for transmission between threads or processes running the same instance of Perl
       with the same module search path.

       This should be sufficient in most situations.

   from_string
       The "from_string" method is used to deserialise the task from a string after transmission
       between the parent and the child (in both directions).

       By default your task will be deserialised using Storable's "thaw" method, which is
       suitable for transmission between threads or processes running the same instance of Perl
       with the same module search path.

       This should be sufficient in most situations.

   locks
       The "locks" method returns a list of locks that the task needs to reserve in order to
       execute safely.

       The meaning, usage, and available quantity of the required locks are tracked by the task
       manager. Enforcement of resource limits may be strict, or may only serve as hints to the
       scheduler.

       Returns a list of strings, or the null list if the task is light with trivial or no
       resource consumption.

   schedule
         $task->schedule;

       The "schedule" method is used to trigger the sending of the task to a worker for
       processing at whatever time the Task Manager deems it appropriate.

       This could be immediately, with the task sent before the call returns, or it may be
       delayed indefinately or never run at all.

       Returns true if the task was dispatched immediately.

       Returns false if the task was queued for later dispatch.

   prepare
       The optional "prepare" method will be called by the task manager on your task object while
       still in the parent thread, immediately before being serialised to pass to the worker
       thread.

       This method should be used to compensate for the potential time difference between when
       "new" is oridinally called and when the task will actually be run.

       For example, a GUI element may indicate the need to run a background task on the visible
       document but does not care that it is the literally "current" document at the time the
       task was spawned.

       By capturing the contents of the current document during "prepare" rather than "new" the
       task object is able to apply the task to the most up to date information at the time we
       are able to do the work, rather than at the time we know we need to do the work.

       The "prepare" method can take a relatively heavy parameter such as a reference to a Wx
       element, and flatten it to the widget ID or contents of the widget instead.

       The "prepare" method also gives your task object a chance to determine whether or not it
       is still necessary. In some situations the delay between "new" and "prepare" may be long
       enough that the task is no longer relevant, and so by the use of "prepare" you can
       indicate execution should be aborted.

       Returns true if the task is stil valid, and so the task should be executed.

       Returns false if the task is no longer valid, and the task should be aborted.

   run
       The "run" method is called on the object in the worker thread immediately after
       deserialisation. It is where the actual computations and work for the task occurs.

       In many situations the implementation of run is simple and procedural, doing work based on
       input parameters stored on the object, blocking if necessary, and storing the results of
       the computation on the object for transmission back to the parent thread.

       In more complex scenarios, you may wish to do a series of tasks or a recursive set of
       tasks in a loop with a check on the "cancelled" method periodically to allow the aborting
       of the task if requested by the parent.

       In even more advanced situations, you may embed and launch an entire event loop such as
       POE or AnyEvent inside the "run" method so that long running or complex functionality can
       be run in the background.

       Once inside of "run" your task is in complete control and the task manager cannot interupt
       the execution of your code short of killing the thread entirely. The standard "cancelled"
       method to check for a request from the parent to abort your task is cooperative and
       entirely voluntary.

       Returns true if the computation was completed successfully.

       Returns false if the computation was not completed successfully, and so the parent should
       not run any post-task logic.

   finish
       The "finish" method is called on the object in the parent thread once it has been passed
       back up to the parent, if "run" completed successfully.

       It is responsible for cleaning up the task and taking any actions based on the result of
       the computation.

       If your task is fire-and-forget or void and you don't care about when the task completes,
       you do not need to implement this method.

       The default implementation of "finish" implements redirection to the "on_finish" handler
       of the task owner object, if one has been defined.

   is_parent
       The "is_parent" method returns true if the task object is in the parent thread, or false
       if it is in the child thread.

   is_child
       The "is_child" method returns true if the task object is in the child thread, or false if
       it is in the parent thread.

   cancelled
         sub run {
             my $self = shift;

             # Abort a long task if we are no longer wanted
             foreach my $thing ( @{$self->{lots_of_stuff}} ) {
                 return if $self->cancelled;

                 # Do something expensive
             }

             return 1;
         }

       The "cancelled" method should be called in the child worker, and allows the task to be
       cooperatively aborted before it has completed.

       The abort mechanism is cooperative. Tasks that do not periodically check the "cancelled"
       method will continue until they are complete regardless of the desires of the task
       manager.

   tell_status
         # Indicate we are waiting, but only while we are waiting
         $task->tell_status('Waiting...');
         sleep 5
         $task->tell_status;

       The "tell_status" method allows a task to trickle informative status messages up to the
       parent thread. These messages serve a dual purpose.

       Firstly, the messages will (or at least may) be displayed to the user to indicate progress
       through a long asynchronous background task. For example, a filesystem search task might
       send a status message for each directory that it examines, so that the user can monitor
       the task speed and level of completion.

       Secondly, the regular flow of messages from the task indicates to the Padre::TaskManager
       that the task is running correctly, making progress through its assigned workload, and has
       probably not crashed or hung.

       While the task manager does not currently kill hanging threads, it will almost certainly
       do so in the future. And so it may even be worth sending a periodic null status message
       every few seconds just to assure the task manager that your long-running task is still
       alive.

SEE ALSO

       Padre, Process

COPYRIGHT

       Copyright 2008-2013 The Padre development team as listed in Padre.pm.

       This program is free software; you can redistribute it and/or modify it under the same
       terms as Perl 5 itself.

       The full text of the license can be found in the LICENSE file included with this module.