1
package Padre::Role::Task;
7
Padre::Role::Task - A role for objects that commission tasks
11
This is a role that should be inherited from by objects in Padre's
12
permanent model that want to commision tasks to be run and have the
13
results fed back to them, if the answer is still relevant.
17
Objects in Padre that commission tasks to run in the background can continue
18
processing and changing state during the queue'ing and/or execution of their
21
If the object state changes in such a way as to make the results of a background
22
task irrelevant, a mechanism is needed to ensure these background tasks are
23
aborted where possible, or their results thrown away when not.
25
B<Padre::Role::Task> provides the concept of "task revisions" to support this
28
A task revision is an incrementing number for each owner that remains the same
29
as long as the results from any arbitrary launched task remains relevant for
30
the current state of the object.
32
When an object transitions a state boundary it will increment it's revision,
33
whether there are any running tasks or not.
35
When a task has completed the task manager will look up the owner (if it has
36
one) and check to see if the current revision of the owner object is the same
37
as when the task was scheduled. If so the Task Manager will call the
38
C<on_finish> handler passing it the task. If not, the completed task will be
41
=head2 Sending messages to your tasks
43
The L<Padre::Task> API supports bidirection communication between tasks and
46
However, when you commission a task via C<task_request> the task object is not
47
returned, leaving you without access to the task and thus without a method by
48
which to send messages to the child.
50
This is intentional, as there is no guarentee that your task will be launched
51
immediately and so sending messages immediately may be unsafe. The task may
52
need to be delayed until a new background worker can be spawned, or for longer
53
if the maximum background worker limit has been reached.
55
The solution is provided by the C<on_message> handler, which is passed the
56
parent task object as its first paramater.
58
Tasks which expect to be sent messages from their owner should send the owner
59
a greeting message as soon as they have started. Not only does this let the
60
parent know that work has commenced on their task, but it provides the task
61
object to the owner once there is certainty that any parent messages can be
62
dispatched to the child successfully.
64
In the following example, we assume a long running "service" style task that
65
will need to be interacted with over time.
72
task => 'My::Service',
73
on_message => 'my_message',
74
on_finish => 'my_finish',
82
# In this example our task sends an empty message to indicate "started"
84
$self->{my_service} = $task;
88
# Handle other messages...
99
use Padre::Current ();
102
our $VERSION = '0.90';
103
our $COMPATIBLE = '0.69';
105
# Use a shared sequence for object revisioning greatly
106
# simplifies the indexing process.
114
######################################################################
121
Padre::Role::Task->task_owner( 1234 );
123
The C<task_owner> static method is a convenience method which takes an owner
124
id and will look up the owner object.
126
Returns the object if it still exists and has not changed it's task revision.
128
Returns C<undef> of the owner object no longer exists, or has changed its task
129
revision since the original owner id was issued.
134
TRACE( $_[0] ) if DEBUG;
142
The C<task_manager> method is a convenience for quick access to the Padre's
143
L<Padre::TaskManager> instance.
148
TRACE( $_[0] ) if DEBUG;
149
return $_[0]->can('current')
150
? $_[0]->current->ide->task_manager
151
: Padre::Current->ide->task_manager;
158
The C<task_revision> accessor returns the current task revision for an object.
163
TRACE( $_[0] ) if DEBUG;
166
# Set a revision if this is the first time
167
unless ( defined $self->{task_revision} ) {
168
$self->{task_revision} = ++$SEQUENCE;
171
# Optimisation hack: Only populate the index when
172
# the revision is queried from the view.
173
unless ( exists $INDEX{ $self->{task_revision} } ) {
174
$INDEX{ $self->{task_revision} } = $self;
175
Scalar::Util::weaken( $INDEX{ $self->{task_revision} } );
178
TRACE("Owner revision is $self->{task_revision}") if DEBUG;
179
return $self->{task_revision};
186
The C<task_reset> method is called when the state of an owner object
187
significantly changes, and outstanding tasks should be deleted or ignored.
189
It will change the task revision of the owner and request the task manager to
190
send a standard C<cancel> message to any currently executing background tasks,
191
allowing them to terminate elegantly (if they handle
196
TRACE( $_[0] ) if DEBUG;
198
if ( $self->{task_revision} ) {
199
delete $INDEX{ $self->{task_revision} };
200
$self->task_manager->cancel( $self->{task_revision} );
202
$self->{task_revision} = ++$SEQUENCE;
210
task => 'Padre::Task::SomeTask',
211
on_message => 'message_handler_method',
212
on_finish => 'finish_handler_method',
217
The C<task_request> method is used to spawn a new background task for the
218
owner, loading the class and registering for callback messages in the process.
220
The C<task> parameter indicates the class of the task to be executed, which
221
must inherit from L<Padre::Task>. The class itself will be automatically loaded
224
The optional C<on_message> parameter should be the name of a method
225
(which must exist if provided) that will receive owner-targetted messages from
226
the background process.
228
The method will be passed the task object (as it exists after the C<prepare>
229
phase in the parent thread) as its first parameter, followed by any values
230
passed by the background task.
232
If no C<on_message> parameter is provided the default method null
233
C<task_message> will be called.
235
The optional C<on_finish> parameter should be the name of a method
236
(which must exist if provided) that will receive the task object back from
237
the background worker once the task has completed, complete with any state
238
saved in the task during its background execution.
240
It is passed a single parameter, which is the L<Padre::Task> object.
242
If no C<on_finish> parameter is provided the default method null
243
C<task_finish> will be called.
245
Any other parameters are passed through the constructor method of the task.
250
TRACE( $_[0] ) if DEBUG;
254
# Check and load the task
255
# Support a convenience shortcut where a false value
256
# for task means don't run a task at all.
257
my $name = delete $param{task} or return;
258
my $driver = Params::Util::_DRIVER( $name, 'Padre::Task' );
259
die "Invalid task class '$name'" unless $driver;
261
# Create and start the task with ourself as the owner
262
TRACE("Creating and scheduling task $driver") if DEBUG;
263
$driver->new( owner => $self, %param )->schedule;
270
The C<task_finish> method is the default handler method for completed tasks,
271
and will be called for any C<task_request> where no specific C<on_finish>
272
handler was provided.
274
If your object issues only one task, or if you would prefer a single common
275
finish handler for all your different tasks, you should override this method
276
instead of explicitly defining an C<on_finish> handler for every task.
278
The default implementation ensures that every task has an appropriate finish
279
handler by throwing an exception with a message indicating the owner and task
280
class for which no finish handler could be found.
285
my $class = ref( $_[0] ) || $_[0];
286
my $task = ref( $_[1] ) || $_[1];
287
die "Unhandled task_finish for $class (recieved $task)";
294
The C<task_message> method is the default handler method for completed tasks,
295
and will be called for any C<task_request> where no specific C<on_message>
296
handler was provided.
298
If your object issues only one task, or if you would prefer a single common
299
message handler for all your different tasks, you should override this method
300
instead of explicitly defining an C<on_finish> handler for every task.
302
If none of your tasks will send messages back to their owner, you do not need
303
to define this method.
305
The default implementation ensures that every task has an appropriate finish
306
handler by throwing an exception with a message indicating the owner and task
307
class for which no finish handler could be found.
312
my $class = ref( $_[0] ) || $_[0];
313
my $task = ref( $_[1] ) || $_[1];
314
die "Unhandled task_message for $class (recieved $task message $_[2]->[0])";
321
=head1 COPYRIGHT & LICENSE
323
Copyright 2008-2011 The Padre development team as listed in Padre.pm.
325
This program is free software; you can redistribute
326
it and/or modify it under the same terms as Perl itself.
328
The full text of the license can be found in the
329
LICENSE file included with this module.
333
# Copyright 2008-2011 The Padre development team as listed in Padre.pm.
335
# This program is free software; you can redistribute it and/or
336
# modify it under the same terms as Perl 5 itself.