Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

230 lines
5.8 KiB

  1. package Delegate;
  2. use lib $ENV{RAZZLETOOLPATH} . "\\sp";
  3. use lib $ENV{RAZZLETOOLPATH} . "\\PostBuildScripts";
  4. use lib $ENV{RAZZLETOOLPATH};
  5. use strict;
  6. use Carp;
  7. use IO::File;
  8. use Win32::Process;
  9. use Logmsg;
  10. #
  11. # Constructor
  12. # RETRY_TIMES - retry times for the child failed
  13. # MAX_PROCS - the maximum amount of the concurrent process
  14. # DELAY_TIME - the delay time before retry after it failed
  15. # JOBQ - the job queue for the children
  16. # PROCS - the amount of the running children process
  17. #
  18. sub new {
  19. my $class = shift;
  20. my $instance = {
  21. RETRY_TIMES => $_[0],
  22. MAX_PROCS => $_[1],
  23. DELAY_TIME => $_[2],
  24. JOBQ => undef,
  25. PROCS => 0
  26. };
  27. $instance->{'MAX_PROCS'} = 2 if (!defined $instance->{'MAX_PROCS'});
  28. $instance->{'MAX_PROCS'} = $ENV{'SYMCD_PROCS'} if (defined $ENV{'SYMCD_PROCS'} );
  29. $instance->{'MAX_PROCS'} = $ENV{'NUMBER_OF_PROCESSORS'} if ($ENV{'NUMBER_OF_PROCESSORS'} > $instance->{'MAX_PROCS'});
  30. $instance->{'DELAY_TIME'} = 5 if (!defined $instance->{'DELAY_TIME'});
  31. return bless $instance, $class;
  32. }
  33. #
  34. # Destructor - it close children we delegated
  35. #
  36. sub DESTROY {
  37. my ($self) = shift;
  38. my ($alias, $myjob, $status);
  39. # If the server terminate some how, we should know which
  40. # cab need to re-create.
  41. for $alias (keys %{$self->{'JOBQ'}} ) {
  42. $myjob = $self->{'JOBQ'}->{$alias};
  43. $status = $self->GetStatus($alias);
  44. if ($status eq 'RUNNING') {
  45. logmsg("$0 stopped ... killing $myjob->{'CMD'}");
  46. $myjob->{'PROCESSOBJ'}->Kill(-1);
  47. } else {
  48. logmsg("$0 stopped ... killing $alias (process status: $status)");
  49. }
  50. }
  51. }
  52. #
  53. # AddJob - register job (similar as += in C#)
  54. #
  55. # $obj->AddJob($alias, $cmdline, $IsComplete)
  56. # $alias - a nick name for this job (always uppercase)
  57. # $cmdline - the command for a child to process
  58. # $IsComplete - a verify function;
  59. # &{$IsComplete}($child_exit_code) should return TRUE
  60. # if the child finish the command correctly
  61. # return 0 - job alias exist
  62. # 1 - job registered
  63. #
  64. sub AddJob {
  65. my $self = shift;
  66. my ($alias, $cmdline, $IsComplete, $priority) = @_;
  67. return 0 if (exists $self->{'JOBQ'}->{$alias});
  68. %{$self->{'JOBQ'}->{$alias}} = (
  69. 'STATUS' => 'INITIAL',
  70. 'PROCESSOBJ' => undef,
  71. 'CMD' => $cmdline,
  72. 'RETRY' => $self->{'RETRY_TIMES'},
  73. 'RETURNVALUE' => 0,
  74. 'DELAYSTART' => undef,
  75. 'PRIORITY' => $priority,
  76. 'IsComplete' => $IsComplete
  77. );
  78. return 1;
  79. }
  80. #
  81. # Start - launch children
  82. #
  83. # $obj->Start()
  84. #
  85. sub Start {
  86. my ($self) = shift;
  87. my ($alias);
  88. for $alias (sort {$self->sort_by_priority($a,$b)} keys %{$self->{'JOBQ'}}) {
  89. $self->Launch($alias);
  90. }
  91. }
  92. #
  93. # Launch job in JobQ
  94. #
  95. # $self->Launch($alias) - please don't call it directly.
  96. #
  97. # return 0 - if we don't launch $alias (maybe because it is running or other reasons)
  98. # 1 - if we launch it
  99. #
  100. sub Launch {
  101. my ($self) = shift;
  102. my ($alias) = @_;
  103. my $status = $self->GetStatus($alias);
  104. # return if is running or finished
  105. return 0 if ($status eq 'RUNNING');
  106. # return if too many children are running
  107. return 0 if ($self->{'PROCS'} >= $self->{'MAX_PROCS'});
  108. # if failed,
  109. if ($status eq 'FAILED') {
  110. if ($self->{'JOBQ'}->{$alias}->{'RETRY'} > 0) {
  111. # For saftey, wait 5 seconds for system status recovered
  112. return 0 if (time() <= $self->{'JOBQ'}->{$alias}->{'DELAYSTART'} + $self->{'DELAY_TIME'});
  113. $self->{'JOBQ'}->{$alias}->{'RETRY'}--;
  114. } else {
  115. logmsg('ERROR - ' . $self->{'JOBQ'}->{$alias}->{'CMD'} . ' failed');
  116. delete $self->{'JOBQ'}->{$alias};
  117. $self->{'PROCS'}--;
  118. return 0;
  119. }
  120. }
  121. # Okay, if gets here, we will launch the child
  122. $self->{'PROCS'}++;
  123. if ($status eq 'INITIAL') {
  124. logmsg("Launching $alias ... $self->{'PROCS'}");
  125. } else {
  126. logmsg("Retrying $alias ... $self->{'PROCS'}");
  127. }
  128. $self->{'JOBQ'}->{$alias}->{'STATUS'} = 'RUNNING';
  129. Win32::Process::Create(
  130. $self->{'JOBQ'}->{$alias}->{'PROCESSOBJ'},
  131. "$ENV{'WINDIR'}\\system32\\cmd.exe",
  132. "cmd /c $self->{'JOBQ'}->{$alias}->{'CMD'}",
  133. 0,
  134. CREATE_NO_WINDOW,
  135. # CREATE_NEW_CONSOLE,
  136. ".") or do {
  137. logmsg('ERROR - ' . Win32::FormatMessage(Win32::GetLastError()));
  138. $self->{'PROCS'}--;
  139. return 0;
  140. };
  141. # $self->{'PROCS'}++;
  142. return 1;
  143. }
  144. #
  145. # $self->CompleteAll() - maintain the JOBQ for each registered jobs
  146. #
  147. # return PROCESS currently running
  148. #
  149. sub CompleteAll {
  150. my ($self) = shift;
  151. my ($myjob, $alias);
  152. for $alias (sort {$self->sort_by_priority($a,$b)} keys %{$self->{'JOBQ'}}) {
  153. # if launch this job, we check later
  154. next if ($self->Launch($alias));
  155. next if ($self->GetStatus($alias) ne 'RUNNING');
  156. $myjob = $self->{'JOBQ'}->{$alias};
  157. $myjob->{'PROCESSOBJ'}->Wait(5000) or next; # next if is running and not finish yet
  158. $myjob->{'PROCESSOBJ'}->GetExitCode($myjob->{'RETURNVALUE'});
  159. # decrese process counter
  160. $self->{'PROCS'}--;
  161. # if user defined IsComplete($ret)
  162. if ((defined $myjob->{'IsComplete'}) &&
  163. (ref($myjob->{'IsComplete'}) eq 'CODE')) {
  164. if (!&{$myjob->{'IsComplete'}}($myjob->{'RETURNVALUE'})) {
  165. $myjob->{'STATUS'} = 'FAILED';
  166. $myjob->{'DELAYSTART'} = time();
  167. logmsg("Job $alias failed... $self->{'PROCS'}");
  168. next;
  169. }
  170. # IsComplete = TRUE
  171. }
  172. # Default is also SUCCESS if the job finished
  173. delete $self->{'JOBQ'}->{$alias};
  174. logmsg("Job $alias complete... $self->{'PROCS'}");
  175. }
  176. return $self->{'PROCS'};
  177. }
  178. #
  179. # $self->AllJobDone - return TRUE if no job in jobq
  180. #
  181. sub AllJobDone {
  182. my ($self) = shift;
  183. return (0 == scalar(keys %{$self->{'JOBQ'}}));
  184. }
  185. #
  186. # $self->GetStatus - return status of the job
  187. #
  188. sub GetStatus {
  189. return $_[0]->{'JOBQ'}->{$_[1]}->{'STATUS'};
  190. }
  191. #
  192. # sort by priority
  193. #
  194. sub sort_by_priority
  195. { my $self = shift;
  196. return $self->{'JOBQ'}->{$_[0]}->{'PRIORITY'} <=> $self->{'JOBQ'}->{$_[1]}->{'PRIORITY'};
  197. }
  198. 1;