These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php namespace Comodojo\Extender\Runner; |
|||||||||||
2 | ||||||||||||
3 | use \Exception; |
|||||||||||
4 | use \Comodojo\Extender\Checks; |
|||||||||||
5 | use \Comodojo\Extender\Queue; |
|||||||||||
6 | use \Monolog\Logger; |
|||||||||||
7 | use \Comodojo\Exception\TaskException; |
|||||||||||
8 | ||||||||||||
9 | /** |
|||||||||||
10 | * Jobs runner |
|||||||||||
11 | * |
|||||||||||
12 | * @package Comodojo extender |
|||||||||||
13 | * @author Marco Giovinazzi <[email protected]> |
|||||||||||
14 | * @license GPL-3.0+ |
|||||||||||
15 | * |
|||||||||||
16 | * LICENSE: |
|||||||||||
17 | * |
|||||||||||
18 | * This program is free software: you can redistribute it and/or modify |
|||||||||||
19 | * it under the terms of the GNU Affero General Public License as |
|||||||||||
20 | * published by the Free Software Foundation, either version 3 of the |
|||||||||||
21 | * License, or (at your option) any later version. |
|||||||||||
22 | * |
|||||||||||
23 | * This program is distributed in the hope that it will be useful, |
|||||||||||
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||||||||||
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||||||||||
26 | * GNU Affero General Public License for more details. |
|||||||||||
27 | * |
|||||||||||
28 | * You should have received a copy of the GNU Affero General Public License |
|||||||||||
29 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||||||||||
30 | */ |
|||||||||||
31 | ||||||||||||
32 | class JobsRunner { |
|||||||||||
33 | ||||||||||||
34 | /** |
|||||||||||
35 | * Jobs database (a simple array!). |
|||||||||||
36 | * |
|||||||||||
37 | * @var array |
|||||||||||
38 | */ |
|||||||||||
39 | private $jobs = array(); |
|||||||||||
40 | ||||||||||||
41 | /** |
|||||||||||
42 | * Logger instance |
|||||||||||
43 | * |
|||||||||||
44 | * @var \Monolog\Logger |
|||||||||||
45 | */ |
|||||||||||
46 | private $logger = null; |
|||||||||||
47 | ||||||||||||
48 | /** |
|||||||||||
49 | * Multithread switch |
|||||||||||
50 | * |
|||||||||||
51 | * @var bool |
|||||||||||
52 | */ |
|||||||||||
53 | private $multithread = false; |
|||||||||||
54 | ||||||||||||
55 | /** |
|||||||||||
56 | * Array of completed processes |
|||||||||||
57 | * |
|||||||||||
58 | * @var array |
|||||||||||
59 | */ |
|||||||||||
60 | private $completed_processes = array(); |
|||||||||||
61 | ||||||||||||
62 | /** |
|||||||||||
63 | * Array of running processes |
|||||||||||
64 | * |
|||||||||||
65 | * @var array |
|||||||||||
66 | */ |
|||||||||||
67 | private $running_processes = array(); |
|||||||||||
68 | ||||||||||||
69 | /** |
|||||||||||
70 | * Array of forked processes |
|||||||||||
71 | * |
|||||||||||
72 | * @var array |
|||||||||||
73 | */ |
|||||||||||
74 | private $forked_processes = array(); |
|||||||||||
75 | ||||||||||||
76 | /** |
|||||||||||
77 | * Amount of data (bits) to read from interprocess sockets |
|||||||||||
78 | * |
|||||||||||
79 | * @var int |
|||||||||||
80 | */ |
|||||||||||
81 | private $max_result_bytes_in_multithread = null; |
|||||||||||
82 | ||||||||||||
83 | /** |
|||||||||||
84 | * Maximum child runtime (after this interval parent process will start to kill) |
|||||||||||
85 | * |
|||||||||||
86 | * @var array |
|||||||||||
87 | */ |
|||||||||||
88 | private $max_childs_runtime = null; |
|||||||||||
89 | ||||||||||||
90 | /** |
|||||||||||
91 | * Array of ipc inter-processes sockets |
|||||||||||
92 | * |
|||||||||||
93 | * @var array |
|||||||||||
94 | */ |
|||||||||||
95 | private $ipc_array = array(); |
|||||||||||
96 | ||||||||||||
97 | /** |
|||||||||||
98 | * Number of processes waiting in the queue |
|||||||||||
99 | * |
|||||||||||
100 | * @var int |
|||||||||||
101 | */ |
|||||||||||
102 | private $queued_processes = 0; |
|||||||||||
103 | ||||||||||||
104 | /** |
|||||||||||
105 | * Array of chunks from current jobs |
|||||||||||
106 | * |
|||||||||||
107 | * It is used to divide jobs in groups and generate the queue |
|||||||||||
108 | * |
|||||||||||
109 | * @var array |
|||||||||||
110 | */ |
|||||||||||
111 | private $queued_chunks = array(); |
|||||||||||
112 | ||||||||||||
113 | /** |
|||||||||||
114 | * Time between SIGTERM and SIGKILL when child is killed |
|||||||||||
115 | * |
|||||||||||
116 | * Just to give it a chance |
|||||||||||
117 | * |
|||||||||||
118 | * @var int |
|||||||||||
119 | */ |
|||||||||||
120 | static private $lagger_timeout = 5; |
|||||||||||
121 | ||||||||||||
122 | /** |
|||||||||||
123 | * Runner constructor |
|||||||||||
124 | * |
|||||||||||
125 | * @param \Comodojo\Extender\Debug $logger Logger instance |
|||||||||||
126 | * @param bool $multithread Enable/disable multithread mode |
|||||||||||
127 | * @param int $max_result_bytes_in_multithread Max result bytes |
|||||||||||
128 | * @param int $max_childs_runtime Max child runtime |
|||||||||||
129 | */ |
|||||||||||
130 | 12 | final public function __construct(Logger $logger, $multithread, $max_result_bytes_in_multithread, $max_childs_runtime) { |
||||||||||
131 | ||||||||||||
132 | 12 | $this->logger = $logger; |
||||||||||
133 | ||||||||||||
134 | 12 | $this->multithread = $multithread; |
||||||||||
135 | ||||||||||||
136 | 12 | $this->max_result_bytes_in_multithread = $max_result_bytes_in_multithread; |
||||||||||
137 | ||||||||||||
138 | 12 | $this->max_childs_runtime = $max_childs_runtime; |
||||||||||
0 ignored issues
–
show
|
||||||||||||
139 | ||||||||||||
140 | 12 | } |
||||||||||
141 | ||||||||||||
142 | /** |
|||||||||||
143 | * Add job to current queue |
|||||||||||
144 | * |
|||||||||||
145 | * @param \Comodojo\Extender\Job\Job $job An instance of \Comodojo\Extender\Job\Job |
|||||||||||
146 | * |
|||||||||||
147 | * @return string A job unique identifier |
|||||||||||
148 | */ |
|||||||||||
149 | final public function addJob(\Comodojo\Extender\Job\Job $job) { |
|||||||||||
150 | ||||||||||||
151 | $uid = self::getJobUid(); |
|||||||||||
152 | ||||||||||||
153 | try { |
|||||||||||
154 | ||||||||||||
155 | $class = $job->getClass(); |
|||||||||||
156 | ||||||||||||
157 | if ( class_exists($class) === false ) throw new Exception("Task cannot be loaded"); |
|||||||||||
158 | ||||||||||||
159 | $this->jobs[$uid] = array( |
|||||||||||
160 | "name" => $job->getName(), |
|||||||||||
161 | "id" => $job->getId(), |
|||||||||||
162 | "parameters"=> $job->getParameters(), |
|||||||||||
163 | "task" => $job->getTask(), |
|||||||||||
164 | "class" => $class |
|||||||||||
165 | ); |
|||||||||||
166 | ||||||||||||
167 | } catch (Exception $e) { |
|||||||||||
168 | ||||||||||||
169 | $this->logger->error('Error including job', array( |
|||||||||||
170 | "JOBUID"=> $uid, |
|||||||||||
171 | "ERROR" => $e->getMessage(), |
|||||||||||
172 | "ERRID" => $e->getCode() |
|||||||||||
173 | )); |
|||||||||||
174 | ||||||||||||
175 | return false; |
|||||||||||
176 | ||||||||||||
177 | } |
|||||||||||
178 | ||||||||||||
179 | return $uid; |
|||||||||||
180 | ||||||||||||
181 | } |
|||||||||||
182 | ||||||||||||
183 | /** |
|||||||||||
184 | * Free (reset) runner instance |
|||||||||||
185 | * |
|||||||||||
186 | */ |
|||||||||||
187 | final public function free() { |
|||||||||||
188 | ||||||||||||
189 | $this->jobs = array(); |
|||||||||||
190 | $this->completed_processes = array(); |
|||||||||||
191 | $this->running_processes = array(); |
|||||||||||
192 | $this->forked_processes = array(); |
|||||||||||
193 | $this->ipc_array = array(); |
|||||||||||
194 | ||||||||||||
195 | } |
|||||||||||
196 | ||||||||||||
197 | /** |
|||||||||||
198 | * Execute job(s) in current queue |
|||||||||||
199 | * |
|||||||||||
200 | * @return array An array of completed processes |
|||||||||||
201 | */ |
|||||||||||
202 | final public function run() { |
|||||||||||
203 | ||||||||||||
204 | // if jobs > concurrent jobs, create the queue |
|||||||||||
205 | ||||||||||||
206 | if ( $this->multithread AND defined("EXTENDER_MAX_CHILDS") AND sizeof($this->jobs) > EXTENDER_MAX_CHILDS AND EXTENDER_MAX_CHILDS != 0 ) { |
|||||||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Using logical operators such as
and instead of && is generally not recommended.
PHP has two types of connecting operators (logical operators, and boolean operators):
The difference between these is the order in which they are executed. In most cases,
you would want to use a boolean operator like Let’s take a look at a few examples: // Logical operators have lower precedence:
$f = false or true;
// is executed like this:
($f = false) or true;
// Boolean operators have higher precedence:
$f = false || true;
// is executed like this:
$f = (false || true);
Logical Operators are used for Control-FlowOne case where you explicitly want to use logical operators is for control-flow such as this: $x === 5
or die('$x must be 5.');
// Instead of
if ($x !== 5) {
die('$x must be 5.');
}
Since // The following is currently a parse error.
$x === 5
or throw new RuntimeException('$x must be 5.');
These limitations lead to logical operators rarely being of use in current PHP code.
Loading history...
|
||||||||||||
207 | ||||||||||||
208 | $this->queued_processes = sizeof($this->jobs); |
|||||||||||
209 | ||||||||||||
210 | // split jobs in chunks |
|||||||||||
211 | ||||||||||||
212 | $this->queued_chunks = array_chunk($this->jobs, EXTENDER_MAX_CHILDS, true); |
|||||||||||
213 | ||||||||||||
214 | // exec chunks, one at time |
|||||||||||
215 | ||||||||||||
216 | foreach ( $this->queued_chunks as $chunk ) { |
|||||||||||
217 | ||||||||||||
218 | $this->queued_processes = $this->queued_processes - sizeof($chunk); |
|||||||||||
0 ignored issues
–
show
It seems like
$this->queued_processes - sizeof($chunk) can also be of type double . However, the property $queued_processes is declared as type integer . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
Loading history...
|
||||||||||||
219 | ||||||||||||
220 | Queue::dump(sizeof($chunk), $this->queued_processes); |
|||||||||||
221 | ||||||||||||
222 | $this->forker($chunk); |
|||||||||||
223 | ||||||||||||
224 | View Code Duplication | if ( $this->multithread ) $this->logger->info("Extender forked ".sizeof($this->forked_processes)." process(es) in the running queue", $this->forked_processes); |
||||||||||
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
||||||||||||
225 | ||||||||||||
226 | $this->catcher(); |
|||||||||||
227 | ||||||||||||
228 | $this->forked_processes = array(); |
|||||||||||
229 | ||||||||||||
230 | } |
|||||||||||
231 | ||||||||||||
232 | } else { |
|||||||||||
233 | ||||||||||||
234 | Queue::dump(sizeof($this->jobs), 0); |
|||||||||||
235 | ||||||||||||
236 | $this->forker($this->jobs); |
|||||||||||
237 | ||||||||||||
238 | View Code Duplication | if ( $this->multithread ) $this->logger->info("Extender forked ".sizeof($this->forked_processes)." process(es) in the running queue", $this->forked_processes); |
||||||||||
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository.
Loading history...
|
||||||||||||
239 | ||||||||||||
240 | $this->catcher(); |
|||||||||||
241 | ||||||||||||
242 | } |
|||||||||||
243 | ||||||||||||
244 | // Dump the end queue status |
|||||||||||
245 | ||||||||||||
246 | Queue::dump(sizeof($this->running_processes), $this->queued_processes); |
|||||||||||
247 | ||||||||||||
248 | return $this->completed_processes; |
|||||||||||
249 | ||||||||||||
250 | } |
|||||||||||
251 | ||||||||||||
252 | /** |
|||||||||||
253 | * Terminate all running processes |
|||||||||||
254 | * |
|||||||||||
255 | * @param int Parent process pid |
|||||||||||
256 | * @param integer $parent_pid |
|||||||||||
257 | */ |
|||||||||||
258 | final public function killAll($parent_pid) { |
|||||||||||
259 | ||||||||||||
260 | foreach ( $this->running_processes as $pid => $process ) { |
|||||||||||
261 | ||||||||||||
262 | // if ( $pid !== $parent_pid) posix_kill($pid, SIGTERM); |
|||||||||||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
53% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them.
Loading history...
|
||||||||||||
263 | if ( $pid !== $parent_pid ) self::kill($pid); |
|||||||||||
264 | ||||||||||||
265 | } |
|||||||||||
266 | ||||||||||||
267 | } |
|||||||||||
268 | ||||||||||||
269 | /** |
|||||||||||
270 | * Fork or exec some jobs |
|||||||||||
271 | * |
|||||||||||
272 | * @param array $jobs A subset of $this->jobs to process in a round |
|||||||||||
273 | */ |
|||||||||||
274 | private function forker($jobs) { |
|||||||||||
275 | ||||||||||||
276 | foreach ( $jobs as $jobUid => $job ) { |
|||||||||||
277 | ||||||||||||
278 | if ( $this->multithread AND sizeof($jobs) > 1 ) { |
|||||||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Using logical operators such as
and instead of && is generally not recommended.
PHP has two types of connecting operators (logical operators, and boolean operators):
The difference between these is the order in which they are executed. In most cases,
you would want to use a boolean operator like Let’s take a look at a few examples: // Logical operators have lower precedence:
$f = false or true;
// is executed like this:
($f = false) or true;
// Boolean operators have higher precedence:
$f = false || true;
// is executed like this:
$f = (false || true);
Logical Operators are used for Control-FlowOne case where you explicitly want to use logical operators is for control-flow such as this: $x === 5
or die('$x must be 5.');
// Instead of
if ($x !== 5) {
die('$x must be 5.');
}
Since // The following is currently a parse error.
$x === 5
or throw new RuntimeException('$x must be 5.');
These limitations lead to logical operators rarely being of use in current PHP code.
Loading history...
|
||||||||||||
279 | ||||||||||||
280 | $status = $this->runMultithread($jobUid); |
|||||||||||
281 | ||||||||||||
282 | if ( !is_null($status["pid"]) ) { |
|||||||||||
283 | ||||||||||||
284 | $this->running_processes[$status["pid"]] = array($status["name"], $status["uid"], $status["timestamp"], $status["id"]); |
|||||||||||
285 | ||||||||||||
286 | array_push($this->forked_processes, $status["pid"]); |
|||||||||||
287 | ||||||||||||
288 | } |
|||||||||||
289 | ||||||||||||
290 | } else { |
|||||||||||
291 | ||||||||||||
292 | $status = $this->runSinglethread($jobUid); |
|||||||||||
293 | ||||||||||||
294 | array_push($this->completed_processes, $status); |
|||||||||||
295 | ||||||||||||
296 | } |
|||||||||||
297 | ||||||||||||
298 | } |
|||||||||||
299 | ||||||||||||
300 | } |
|||||||||||
301 | ||||||||||||
302 | /** |
|||||||||||
303 | * Catch results from completed jobs |
|||||||||||
304 | * |
|||||||||||
305 | */ |
|||||||||||
306 | private function catcher() { |
|||||||||||
307 | ||||||||||||
308 | $exec_time = microtime(true); |
|||||||||||
309 | ||||||||||||
310 | while ( !empty($this->running_processes) ) { |
|||||||||||
311 | ||||||||||||
312 | foreach ( $this->running_processes as $pid => $job ) { |
|||||||||||
313 | ||||||||||||
314 | //$job[0] is name |
|||||||||||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
50% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them.
Loading history...
|
||||||||||||
315 | //$job[1] is uid |
|||||||||||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
50% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them.
Loading history...
|
||||||||||||
316 | //$job[2] is start timestamp |
|||||||||||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
40% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them.
Loading history...
|
||||||||||||
317 | //$job[3] is job id |
|||||||||||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
40% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them.
Loading history...
|
||||||||||||
318 | ||||||||||||
319 | if ( !self::isRunning($pid) ) { |
|||||||||||
320 | ||||||||||||
321 | list($reader, $writer) = $this->ipc_array[$job[1]]; |
|||||||||||
322 | ||||||||||||
323 | socket_close($writer); |
|||||||||||
324 | ||||||||||||
325 | $parent_result = socket_read($reader, $this->max_result_bytes_in_multithread, PHP_BINARY_READ); |
|||||||||||
326 | ||||||||||||
327 | if ( $parent_result === false ) { |
|||||||||||
328 | ||||||||||||
329 | $this->logger->error("socket_read() failed. Reason: ".socket_strerror(socket_last_error($reader))); |
|||||||||||
330 | ||||||||||||
331 | array_push($this->completed_processes, Array( |
|||||||||||
332 | null, |
|||||||||||
333 | $job[0], //$job_name, |
|||||||||||
334 | false, |
|||||||||||
335 | $job[2], //$start_timestamp, |
|||||||||||
336 | null, |
|||||||||||
337 | "socket_read() failed. Reason: ".socket_strerror(socket_last_error($reader)), |
|||||||||||
338 | $job[3], |
|||||||||||
339 | null |
|||||||||||
340 | )); |
|||||||||||
341 | ||||||||||||
342 | $status = 'ERROR'; |
|||||||||||
343 | ||||||||||||
344 | } else { |
|||||||||||
345 | ||||||||||||
346 | $result = unserialize(rtrim($parent_result)); |
|||||||||||
347 | ||||||||||||
348 | socket_close($reader); |
|||||||||||
349 | ||||||||||||
350 | array_push($this->completed_processes, Array( |
|||||||||||
351 | $pid, |
|||||||||||
352 | $job[0], //$job_name, |
|||||||||||
353 | $result["success"], |
|||||||||||
354 | $job[2], //$start_timestamp, |
|||||||||||
355 | $result["timestamp"], |
|||||||||||
356 | $result["result"], |
|||||||||||
357 | $job[3], |
|||||||||||
358 | $result["worklogid"] |
|||||||||||
359 | )); |
|||||||||||
360 | ||||||||||||
361 | $status = $result["success"] ? 'success' : 'failure'; |
|||||||||||
362 | ||||||||||||
363 | } |
|||||||||||
364 | ||||||||||||
365 | $this->logger->notice("Job ".$job[0]."(".$job[3].") ends with ".$status); |
|||||||||||
366 | ||||||||||||
367 | unset($this->running_processes[$pid]); |
|||||||||||
368 | ||||||||||||
369 | $this->logger->info("Removed pid ".$pid." from the running queue, job terminated with ".$status); |
|||||||||||
370 | ||||||||||||
371 | } else { |
|||||||||||
372 | ||||||||||||
373 | $current_time = microtime(true); |
|||||||||||
374 | ||||||||||||
375 | if ( $current_time > $exec_time + $this->max_childs_runtime ) { |
|||||||||||
376 | ||||||||||||
377 | $this->logger->warning("Killing pid ".$pid." due to maximum exec time reached (>".$this->max_childs_runtime.")", array( |
|||||||||||
378 | "START_TIME" => $exec_time, |
|||||||||||
379 | "CURRENT_TIME" => $current_time, |
|||||||||||
380 | "MAX_RUNTIME" => $this->max_childs_runtime |
|||||||||||
381 | )); |
|||||||||||
382 | ||||||||||||
383 | $kill = self::kill($pid); |
|||||||||||
384 | ||||||||||||
385 | if ( $kill ) $this->logger->warning("Pid ".$pid." killed"); |
|||||||||||
386 | ||||||||||||
387 | else $this->logger->warning("Pid ".$pid." could not be killed"); |
|||||||||||
388 | ||||||||||||
389 | list($reader, $writer) = $this->ipc_array[$job[1]]; |
|||||||||||
390 | ||||||||||||
391 | socket_close($writer); |
|||||||||||
392 | socket_close($reader); |
|||||||||||
393 | ||||||||||||
394 | array_push($this->completed_processes, Array( |
|||||||||||
395 | $pid, |
|||||||||||
396 | $job[0], //$job_name, |
|||||||||||
397 | false, |
|||||||||||
398 | $job[2], //$start_timestamp, |
|||||||||||
399 | $current_time, |
|||||||||||
400 | "Job ".$job[0]." killed due to maximum exec time reached (>".$this->max_childs_runtime.")", |
|||||||||||
401 | $job[3], |
|||||||||||
402 | null |
|||||||||||
403 | )); |
|||||||||||
404 | ||||||||||||
405 | $this->logger->notice("Job ".$job[0]."(".$job[3].") ends with error"); |
|||||||||||
406 | ||||||||||||
407 | unset($this->running_processes[$pid]); |
|||||||||||
408 | ||||||||||||
409 | $this->logger->info("Removed pid ".$pid." from the running queue, job terminated with error"); |
|||||||||||
410 | ||||||||||||
411 | } |
|||||||||||
412 | ||||||||||||
413 | } |
|||||||||||
414 | ||||||||||||
415 | } |
|||||||||||
416 | ||||||||||||
417 | } |
|||||||||||
418 | ||||||||||||
419 | } |
|||||||||||
420 | ||||||||||||
421 | /** |
|||||||||||
422 | * Run job in singlethread mode |
|||||||||||
423 | * |
|||||||||||
424 | * @param string Job unique identifier |
|||||||||||
425 | * |
|||||||||||
426 | * @return array {[pid],[name],[success],[start],[end],[result],[id]} |
|||||||||||
427 | */ |
|||||||||||
428 | private function runSinglethread($jobUid) { |
|||||||||||
429 | ||||||||||||
430 | $job = $this->jobs[$jobUid]; |
|||||||||||
431 | ||||||||||||
432 | // get job start timestamp |
|||||||||||
433 | $start_timestamp = microtime(true); |
|||||||||||
434 | ||||||||||||
435 | $this->logger->notice("Starting job ".$job['name']."(".$job['id'].")"); |
|||||||||||
436 | ||||||||||||
437 | $name = $job['name']; |
|||||||||||
438 | ||||||||||||
439 | $id = $job['id']; |
|||||||||||
440 | ||||||||||||
441 | $parameters = $job['parameters']; |
|||||||||||
442 | ||||||||||||
443 | $task = $job['task']; |
|||||||||||
0 ignored issues
–
show
$task is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the
Loading history...
|
||||||||||||
444 | ||||||||||||
445 | $task_class = $job['class']; |
|||||||||||
446 | ||||||||||||
447 | try { |
|||||||||||
448 | ||||||||||||
449 | // create a task instance |
|||||||||||
450 | ||||||||||||
451 | $thetask = new $task_class($parameters, $this->logger, null, $name, $start_timestamp, false, $id); |
|||||||||||
452 | ||||||||||||
453 | // get the task pid (we are in singlethread mode) |
|||||||||||
454 | ||||||||||||
455 | $pid = $thetask->getPid(); |
|||||||||||
456 | ||||||||||||
457 | // run task |
|||||||||||
458 | ||||||||||||
459 | $result = $thetask->start(); |
|||||||||||
460 | ||||||||||||
461 | } catch (TaskException $te) { |
|||||||||||
462 | ||||||||||||
463 | $this->logger->notice("Job ".$job['name']."(".$job['id'].") ends with error"); |
|||||||||||
464 | ||||||||||||
465 | return array($pid, $name, false, $start_timestamp, $te->getEndTimestamp(), $te->getMessage(), $id, $te->getWorklogId()); |
|||||||||||
0 ignored issues
–
show
The variable
$pid does not seem to be defined for all execution paths leading up to this point.
If you define a variable conditionally, it can happen that it is not defined for all execution paths. Let’s take a look at an example: function myFunction($a) {
switch ($a) {
case 'foo':
$x = 1;
break;
case 'bar':
$x = 2;
break;
}
// $x is potentially undefined here.
echo $x;
}
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined. Available Fixes
Loading history...
|
||||||||||||
466 | ||||||||||||
467 | } catch (Exception $e) { |
|||||||||||
468 | ||||||||||||
469 | $this->logger->notice("Job ".$job['name']."(".$job['id'].") ends with error"); |
|||||||||||
470 | ||||||||||||
471 | return array($pid, $name, false, $start_timestamp, null, $e->getMessage(), $id, null); |
|||||||||||
472 | ||||||||||||
473 | } |
|||||||||||
474 | ||||||||||||
475 | $this->logger->notice("Job ".$job['name']."(".$job['id'].") ends with ".($result["success"] ? "success" : "failure")); |
|||||||||||
476 | ||||||||||||
477 | return array($pid, $name, $result["success"], $start_timestamp, $result["timestamp"], $result["result"], $id, $result["worklogid"]); |
|||||||||||
478 | ||||||||||||
479 | } |
|||||||||||
480 | ||||||||||||
481 | /** |
|||||||||||
482 | * Run job in singlethread mode |
|||||||||||
483 | * |
|||||||||||
484 | * @param string Job unique identifier |
|||||||||||
485 | * |
|||||||||||
486 | * @return array {[pid],[name],[success],[start],[end],[result],[id]} |
|||||||||||
487 | */ |
|||||||||||
488 | private function runMultithread($jobUid) { |
|||||||||||
489 | ||||||||||||
490 | $job = $this->jobs[$jobUid]; |
|||||||||||
491 | ||||||||||||
492 | // get job start timestamp |
|||||||||||
493 | $start_timestamp = microtime(true); |
|||||||||||
494 | ||||||||||||
495 | $this->logger->notice("Starting job ".$job['name']."(".$job['id'].")"); |
|||||||||||
496 | ||||||||||||
497 | $name = $job['name']; |
|||||||||||
498 | ||||||||||||
499 | $id = $job['id']; |
|||||||||||
500 | ||||||||||||
501 | $parameters = $job['parameters']; |
|||||||||||
502 | ||||||||||||
503 | $task = $job['task']; |
|||||||||||
0 ignored issues
–
show
$task is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the
Loading history...
|
||||||||||||
504 | ||||||||||||
505 | $task_class = $job['class']; |
|||||||||||
506 | ||||||||||||
507 | $this->ipc_array[$jobUid] = array(); |
|||||||||||
508 | ||||||||||||
509 | // create a comm socket |
|||||||||||
510 | $socket = socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $this->ipc_array[$jobUid]); |
|||||||||||
511 | ||||||||||||
512 | if ( $socket === false ) { |
|||||||||||
513 | ||||||||||||
514 | $this->logger->error("No IPC communication, aborting", array( |
|||||||||||
515 | "JOBUID"=> $jobUid, |
|||||||||||
516 | "ERROR" => socket_strerror(socket_last_error()), |
|||||||||||
517 | "ERRID" => null |
|||||||||||
518 | )); |
|||||||||||
519 | ||||||||||||
520 | array_push($this->completed_processes, array( |
|||||||||||
521 | null, |
|||||||||||
522 | $name, |
|||||||||||
523 | false, |
|||||||||||
524 | $start_timestamp, |
|||||||||||
525 | microtime(true), |
|||||||||||
526 | 'No IPC communication, exiting - '.socket_strerror(socket_last_error()), |
|||||||||||
527 | $id, |
|||||||||||
528 | null |
|||||||||||
529 | )); |
|||||||||||
530 | ||||||||||||
531 | return array( |
|||||||||||
532 | "pid" => null, |
|||||||||||
533 | "name" => $name, |
|||||||||||
534 | "uid" => $jobUid, |
|||||||||||
535 | "timestamp" => $start_timestamp, |
|||||||||||
536 | "id" => $id |
|||||||||||
537 | ); |
|||||||||||
538 | ||||||||||||
539 | } |
|||||||||||
540 | ||||||||||||
541 | list($reader, $writer) = $this->ipc_array[$jobUid]; |
|||||||||||
542 | ||||||||||||
543 | $pid = pcntl_fork(); |
|||||||||||
544 | ||||||||||||
545 | if ( $pid == -1 ) { |
|||||||||||
546 | ||||||||||||
547 | $this->logger->error("Could not fok job, aborting"); |
|||||||||||
548 | ||||||||||||
549 | array_push($this->completed_processes, Array( |
|||||||||||
550 | null, |
|||||||||||
551 | $name, |
|||||||||||
552 | false, |
|||||||||||
553 | $start_timestamp, |
|||||||||||
554 | microtime(true), |
|||||||||||
555 | 'Could not fok job', |
|||||||||||
556 | $id, |
|||||||||||
557 | null |
|||||||||||
558 | )); |
|||||||||||
559 | ||||||||||||
560 | } elseif ( $pid ) { |
|||||||||||
561 | ||||||||||||
562 | //PARENT will take actions on processes later |
|||||||||||
563 | ||||||||||||
564 | self::adjustNiceness($pid, $this->logger); |
|||||||||||
565 | ||||||||||||
566 | } else { |
|||||||||||
567 | ||||||||||||
568 | socket_close($reader); |
|||||||||||
569 | ||||||||||||
570 | $thetask = new $task_class($parameters, $this->logger, null, $name, $start_timestamp, true, $id); |
|||||||||||
571 | ||||||||||||
572 | try { |
|||||||||||
573 | ||||||||||||
574 | $result = $thetask->start(); |
|||||||||||
575 | ||||||||||||
576 | $return = serialize(array( |
|||||||||||
577 | "success" => $result["success"], |
|||||||||||
578 | "result" => $result["result"], |
|||||||||||
579 | "timestamp" => $result["timestamp"], |
|||||||||||
580 | "worklogid" => $result["worklogid"] |
|||||||||||
581 | )); |
|||||||||||
582 | ||||||||||||
583 | $exit = 0; |
|||||||||||
584 | ||||||||||||
585 | } catch (TaskException $te) { |
|||||||||||
586 | ||||||||||||
587 | $return = serialize(Array( |
|||||||||||
588 | "success" => false, |
|||||||||||
589 | "result" => $te->getMessage(), |
|||||||||||
590 | "timestamp" => $te->getEndTimestamp(), |
|||||||||||
591 | "worklogid" => $te->getWorklogId() |
|||||||||||
592 | )); |
|||||||||||
593 | ||||||||||||
594 | $exit = 1; |
|||||||||||
595 | ||||||||||||
596 | } catch (Exception $e) { |
|||||||||||
597 | ||||||||||||
598 | $return = serialize(Array( |
|||||||||||
599 | "success" => false, |
|||||||||||
600 | "result" => $e->getMessage(), |
|||||||||||
601 | "timestamp" => microtime(true), |
|||||||||||
602 | "worklogid" => null |
|||||||||||
603 | )); |
|||||||||||
604 | ||||||||||||
605 | $exit = 1; |
|||||||||||
606 | ||||||||||||
607 | } |
|||||||||||
608 | ||||||||||||
609 | if ( socket_write($writer, $return, strlen($return)) === false ) { |
|||||||||||
610 | ||||||||||||
611 | $this->logger->error("socket_write() failed ", array( |
|||||||||||
612 | "ERROR" => socket_strerror(socket_last_error($writer)) |
|||||||||||
613 | )); |
|||||||||||
614 | ||||||||||||
615 | } |
|||||||||||
616 | ||||||||||||
617 | socket_close($writer); |
|||||||||||
618 | ||||||||||||
619 | exit($exit); |
|||||||||||
620 | ||||||||||||
621 | } |
|||||||||||
622 | ||||||||||||
623 | return array( |
|||||||||||
624 | "pid" => $pid == -1 ? null : $pid, |
|||||||||||
625 | "name" => $name, |
|||||||||||
626 | "uid" => $jobUid, |
|||||||||||
627 | "id" => $id, |
|||||||||||
628 | "timestamp" => $start_timestamp |
|||||||||||
629 | ); |
|||||||||||
630 | ||||||||||||
631 | } |
|||||||||||
632 | ||||||||||||
633 | /** |
|||||||||||
634 | * Return true if process is still running, false otherwise |
|||||||||||
635 | * |
|||||||||||
636 | * @return bool |
|||||||||||
637 | */ |
|||||||||||
638 | private static function isRunning($pid) { |
|||||||||||
639 | ||||||||||||
640 | return (pcntl_waitpid($pid, $status, WNOHANG) === 0); |
|||||||||||
0 ignored issues
–
show
|
||||||||||||
641 | ||||||||||||
642 | } |
|||||||||||
643 | ||||||||||||
644 | /** |
|||||||||||
645 | * Kill a child process |
|||||||||||
646 | * |
|||||||||||
647 | * @return bool |
|||||||||||
648 | */ |
|||||||||||
649 | private static function kill($pid) { |
|||||||||||
650 | ||||||||||||
651 | $kill_time = time() + self::$lagger_timeout; |
|||||||||||
652 | ||||||||||||
653 | $term = posix_kill($pid, SIGTERM); |
|||||||||||
654 | ||||||||||||
655 | while ( time() < $kill_time ) { |
|||||||||||
656 | ||||||||||||
657 | if ( !self::isRunning($pid) ) return $term; |
|||||||||||
658 | ||||||||||||
659 | } |
|||||||||||
660 | ||||||||||||
661 | return posix_kill($pid, SIGKILL); |
|||||||||||
662 | ||||||||||||
663 | } |
|||||||||||
664 | ||||||||||||
665 | /** |
|||||||||||
666 | * Get a job unique identifier |
|||||||||||
667 | * |
|||||||||||
668 | * @return string |
|||||||||||
669 | */ |
|||||||||||
670 | private static function getJobUid() { |
|||||||||||
671 | ||||||||||||
672 | return md5(uniqid(rand(), true), 0); |
|||||||||||
673 | ||||||||||||
674 | } |
|||||||||||
675 | ||||||||||||
676 | /** |
|||||||||||
677 | * Change child process priority according to EXTENDER_NICENESS |
|||||||||||
678 | * |
|||||||||||
679 | */ |
|||||||||||
680 | private static function adjustNiceness($pid, $logger) { |
|||||||||||
681 | ||||||||||||
682 | if ( Checks::multithread() AND defined("EXTENDER_CHILD_NICENESS") ) { |
|||||||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Using logical operators such as
and instead of && is generally not recommended.
PHP has two types of connecting operators (logical operators, and boolean operators):
The difference between these is the order in which they are executed. In most cases,
you would want to use a boolean operator like Let’s take a look at a few examples: // Logical operators have lower precedence:
$f = false or true;
// is executed like this:
($f = false) or true;
// Boolean operators have higher precedence:
$f = false || true;
// is executed like this:
$f = (false || true);
Logical Operators are used for Control-FlowOne case where you explicitly want to use logical operators is for control-flow such as this: $x === 5
or die('$x must be 5.');
// Instead of
if ($x !== 5) {
die('$x must be 5.');
}
Since // The following is currently a parse error.
$x === 5
or throw new RuntimeException('$x must be 5.');
These limitations lead to logical operators rarely being of use in current PHP code.
Loading history...
|
||||||||||||
683 | ||||||||||||
684 | $niceness = pcntl_setpriority($pid, EXTENDER_CHILD_NICENESS); |
|||||||||||
685 | ||||||||||||
686 | if ( $niceness == false ) $logger->warning("Unable to set child process ".$pid." niceness to ".EXTENDER_CHILD_NICENESS); |
|||||||||||
687 | ||||||||||||
688 | } |
|||||||||||
689 | ||||||||||||
690 | } |
|||||||||||
691 | ||||||||||||
692 | } |
|||||||||||
693 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..