WithSubtask.pm
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:8k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore - Convenience abstract base class for classes with subtasks.
  3. #  Copyright (c) 2006,2007 OpenKore Developers
  4. #
  5. #  This software is open source, licensed under the GNU General Public
  6. #  License, version 2.
  7. #  Basically, this means that you're allowed to modify and distribute
  8. #  this software. However, if you distribute modified versions, you MUST
  9. #  also distribute the source code.
  10. #  See http://www.gnu.org/licenses/gpl.html for the full license.
  11. #########################################################################
  12. ##
  13. # MODULE DESCRIPTION: Convenience abstract base class for classes with subtasks.
  14. #
  15. # This is an convenience abstract class for tasks which have at most one active subtask
  16. # at any time. It provides convenience methods for making the usage of subtasks
  17. # easy.
  18. #
  19. # Task::WithSubtask has the following features:
  20. # `l
  21. # - Allows you to easily switch context to a subtask, allowing to subtask to
  22. #   temporarily have complete control.
  23. # - interrupt(), resume() and stop() calls are automatically propagated to subtasks.
  24. # - Allows you to define custom behavior when a subtask has completed or stopped.
  25. # `l`
  26. #
  27. # When you override iterate(), don't forget to check the return value of the
  28. # super method. See $Task_WithSubtask->iterate() for more information.
  29. package Task::WithSubtask;
  30. use strict;
  31. use Carp::Assert;
  32. use Modules 'register';
  33. use Task;
  34. use base qw(Task);
  35. ##
  36. # Task::WithSubtask->new(options...)
  37. #
  38. # Create a new Task::WithSubtask object. All options for Task->new() are allowed.
  39. # Two more options are allowed: <tt>autostop</tt> and <tt>autofail</tt> (both
  40. # are booleans).
  41. #
  42. # <tt>autostop</tt> which will influence the effect of the stop() method:
  43. # `l
  44. # - If autostop is set to 1: If a subtask is currently running, then the subtask's
  45. #   stop() method will be called, and the current task's status will be set to
  46. #   Task::STOPPED after the subtask has stopped.<br>
  47. #   If a subtask is not currently running, then the current task's status is
  48. #   immediately set to Task::STOPPED.
  49. # - If autostop is set to 0: If a subtask is currently running, then the subtask's
  50. #   stop() method will be called. Nothing else will happen in the current task:
  51. #   this is useful if you need to implement custom stop code.
  52. # `l`
  53. # The default value is true.
  54. #
  55. # <tt>autofail</tt> specifies whether this task should automatically fail if a subtask
  56. # has failed.
  57. # `l
  58. # - If autofail is on, and the subtask fails, then this task's status will
  59. #   be marked as DONE, and the error code and error message of the subtask will be
  60. #   passed to this task.
  61. # - If autofail is off, nothing will happen if a subtask fails. You are then responsible
  62. #   for handling the failure yourself by placing appropriate code in the method
  63. #   subtaskDone().
  64. # `l`
  65. # The default value is true.
  66. sub new {
  67. my $class = shift;
  68. my %args = @_;
  69. my $self = $class->SUPER::new(@_);
  70. $self->{ST_autostop} = defined($args{autostop}) ? $args{autostop} : 1;
  71. $self->{ST_autofail} = defined($args{autofail}) ? $args{autofail} : 1;
  72. $self->{ST_manageMutexes} = $args{manageMutexes};
  73. return $self;
  74. }
  75. sub DESTROY {
  76. my ($self) = @_;
  77. $self->SUPER::DESTROY();
  78. delete $self->{ST_mutexesChangedEvent} if ($self);
  79. }
  80. # Overrided method.
  81. sub interrupt {
  82. my ($self) = @_;
  83. $self->SUPER::interrupt();
  84. $self->{ST_subtask}->interrupt() if ($self->{ST_subtask});
  85. }
  86. # Overrided method.
  87. sub resume {
  88. my ($self) = @_;
  89. $self->SUPER::resume();
  90. $self->{ST_subtask}->resume() if ($self->{ST_subtask});
  91. }
  92. # Overrided method.
  93. sub stop {
  94. my ($self) = @_;
  95. if ($self->{ST_subtask}) {
  96. my $task = $self->{ST_subtask};
  97. $task->stop();
  98. if ($task->getStatus() == Task::STOPPED) {
  99. $self->SUPER::stop() if ($self->{ST_autostop});
  100. delete $self->{ST_subtask};
  101. $self->subtaskStopped($task);
  102. }
  103. } elsif ($self->{ST_autostop}) {
  104. $self->SUPER::stop();
  105. }
  106. }
  107. ##
  108. # boolean $Task_WithSubtask->iterate()
  109. #
  110. # This is like $Task->iterate(), but return 0 when a subtask is running, and 1
  111. # when a subtask is not running. If you override this method then you must check
  112. # the super call's return value. If the return value is 0 then you should do
  113. # nothing in the overrided iterate() method.
  114. sub iterate {
  115. my ($self) = @_;
  116. $self->SUPER::iterate();
  117. if ($self->{ST_subtask}) {
  118. # Run subtask if there is one.
  119. my $task = $self->{ST_subtask};
  120. $task->iterate();
  121. # If the task is completed, then we return 1 (with one exception, see below).
  122. # This way the child class doesn't have to wait for the next
  123. # iteration before it can continue.
  124. if ($task->getStatus() == Task::DONE) {
  125. my $error;
  126. my $result = 1;
  127. _restoreMutexes($self);
  128. delete $self->{ST_subtask};
  129. if ($self->{ST_autofail} && ($error = $task->getError())) {
  130. $error = $self->translateSubtaskError($task, $error);
  131. $self->setError($error->{code}, $error->{message});
  132. # We already set the current task's status to DONE,
  133. # so we don't want the child class's iterate() method
  134. # to do anything.
  135. $result = 0;
  136. }
  137. $self->subtaskDone($task);
  138. return $result;
  139. } elsif ($task->getStatus() == Task::STOPPED) {
  140. $self->setStopped() if ($self->{ST_autostop});
  141. _restoreMutexes($self);
  142. delete $self->{ST_subtask};
  143. $self->subtaskStopped($task);
  144. return 1;
  145. } else {
  146. # Task is not completed.
  147. return 0;
  148. }
  149. } else {
  150. return 1;
  151. }
  152. }
  153. ##
  154. # Task $Task_WithSubtask->getSubtask()
  155. #
  156. # Return the currently set subtask, or undef if there is none.
  157. sub getSubtask {
  158. return $_[0]->{ST_subtask};
  159. }
  160. ##
  161. # void $Task_WithSubtask->setSubtask(Task subtask)
  162. # Requires: !defined($self->getSubtask()) && $subtask->getStatus() == Task::INACTIVE
  163. # Ensures: $self->getSubtask() == $subtask
  164. #
  165. # Set the currently active subtask. This subtask is immediately activated. In the next
  166. # iteration, the subtask will be run, and iterate() will return 0 to indicate that
  167. # we're currently running a subtask.
  168. #
  169. # When the subtask is done or stopped, getSubtask() will return undef.
  170. sub setSubtask {
  171. my ($self, $subtask) = @_;
  172. assert(!defined($self->getSubtask())) if DEBUG;
  173. assert($subtask->getStatus() == Task::INACTIVE) if DEBUG;
  174. $self->{ST_subtask} = $subtask;
  175. if ($subtask) {
  176. $subtask->activate();
  177. if ($self->{ST_manageMutexes}) {
  178. # Save the current mutexes (before we switched to the subtask).
  179. my $mutexes = $self->getMutexes();
  180. $self->{ST_oldmutexes} = [@{$mutexes}];
  181. # Watch for changes in the subtask's mutex list and assign the subtask's
  182. # current mutexes to current task.
  183. $self->{ST_mutexesChangedEvent} = $subtask->onMutexesChanged->add($self,
  184. &_onSubtaskMutexesChanged);
  185. _onSubtaskMutexesChanged($self, $subtask);
  186. }
  187. }
  188. }
  189. ##
  190. # void $Task_WithSubtask->subtaskDone(Task subtask)
  191. #
  192. # Called when a subtask has completed, either successfully or with an error.
  193. #
  194. # <b>Note:</b> if autofail is on, and the subtask has completed with an error,
  195. # then the following is true:
  196. # `l
  197. # - $self->getStatus() == Task::DONE
  198. # - The return value of $self->getError() is defined.
  199. # `l`
  200. sub subtaskDone {
  201. }
  202. ##
  203. # void $Task_WithSubtask->subtaskStopped(Task subtask)
  204. #
  205. # Called when a subtask is stopped by Task::WithSubtask.
  206. sub subtaskStopped {
  207. }
  208. ##
  209. # Hash* $Task_WithSubtask->translateSubtaskError(Task subtask, Hash* error)
  210. # subtask: The subtask that finished with an error.
  211. # error: The subtask's error hash.
  212. # Returns: A new error hash.
  213. # Ensures: defined(result)
  214. #
  215. # When 'autofail' is turned on, Task::WithSubtask will set the error code and
  216. # error message of this task to the same value as the error code/message of the
  217. # subtask. If that is not what you want, then you can override that behavior
  218. # by overriding this method.
  219. #
  220. # This method allows you to specify how a subtask's error should be translated
  221. # into an error for this task.
  222. sub translateSubtaskError {
  223. my ($self, $task, $error) = @_;
  224. return $error;
  225. }
  226. # If this method is called then it means automutex is on.
  227. sub _onSubtaskMutexesChanged {
  228. my ($self, $subtask) = @_;
  229. my $mutexes = $subtask->getMutexes();
  230. $self->setMutexes(@{$mutexes});
  231. }
  232. # Remove the callback on the subtask's OnMutexesChanged event,
  233. # and restore the mutex state to what it was before we switched
  234. # to the subtask.
  235. sub _restoreMutexes {
  236. my ($self) = @_;
  237. if ($self->{ST_manageMutexes}) {
  238. $self->{ST_subtask}->onMutexesChanged->remove($self->{ST_mutexesChangedEvent});
  239. delete $self->{ST_mutexesChangedEvent};
  240. $self->setMutexes(@{$self->{ST_oldmutexes}});
  241. delete $self->{ST_oldmutexes};
  242. }
  243. }
  244. 1;