This repository has been archived by the owner on May 23, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAsyncWorker.php
150 lines (120 loc) · 4.54 KB
/
AsyncWorker.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<?php
/* Object pcntl_fork wrapper
Usage examples are included as comments below
*/
class AsyncWorker {
private $pid = 0;
private $failed = false;
private $result_file_handler = null; //child will write result in this file
private $success_file_handler = null; //child will write in this file if command was successful
const SUCCESS_CODE = 'OK';
public function __construct() {
$this->result_file_handler = tmpfile();
$this->success_file_handler = tmpfile();
if(!$this->result_file_handler || !$this->success_file_handler)
{
trigger_error("ASyncWorker: Failed to create result or success file handles", E_USER_ERROR);
$this->failed = true;
return;
}
$this->pid = pcntl_fork();
if ($this->pid === -1) {
trigger_error("ASyncWorker: Failed to fork", E_USER_ERROR);
$this->failed = true;
} elseif ($this->pid === 0) {
// $pid = 0, this is the child thread
$args = func_get_args();
if(sizeof($args) < 1) {
trigger_error("ASyncWorker: No function to execute given", E_USER_ERROR);
exit(1);
}
$function = $args[0];
array_shift($args); //remove function from array, leaving only function arguments
if(!is_callable($function)) {
trigger_error("ASyncWorker: Function $function not found or not callable", E_USER_ERROR);
exit(2);
}
$result = call_user_func_array($function, $args);
/* do not check result value, call_user_func_array will return false on error, but error may be a valid return value of the given function.
I assume is_callable prevent all errors cases for call_user_func_array, this may be wrong
*/
$ok = fwrite($this->result_file_handler, serialize($result));
if(!$ok) {
trigger_error("ASyncWorker: Failed to write result file", E_USER_ERROR);
exit(3);
}
$ok = fwrite($this->success_file_handler, self::SUCCESS_CODE);
if(!$ok) {
trigger_error("ASyncWorker: Failed to write success file", E_USER_ERROR);
exit(4);
}
exit(0); //exit this fork
} //else this is the parent thread, nothing to do
}
public function wait() {
if($this->failed == true)
return false;
$status = null;
pcntl_waitpid($this->pid, $status);
//anormal exit !
if(!pcntl_wifexited($status) || pcntl_wexitstatus($status) != 0) {
trigger_error("ASyncWorker: Forked process did not exit properly", E_USER_ERROR);
$this->failed = true;
return false;
}
fseek($this->success_file_handler, 0);
$success = fgets($this->success_file_handler);
if($success != self::SUCCESS_CODE) {
trigger_error("ASyncWorker: Success file not found or invalid", E_USER_ERROR);
$this->failed = true;
return false;
}
fseek($this->result_file_handler, 0);
$result_serialized = fgets($this->result_file_handler);
if($result_serialized === false) {
trigger_error("ASyncWorker: No data in result file, or couldn't read it", E_USER_ERROR);
$this->failed = true;
return false;
}
fclose($this->result_file_handler);
fclose($this->success_file_handler);
$result = unserialize($result_serialized);
return $result;
}
public function has_failed() {
return $this->failed;
}
}
/* Examples
function my_funky_function($arg1, $arg2)
{
// do some work here
sleep(1); //simulate working time
// test arguments
echo $arg1 . PHP_EOL;
echo $arg2 . PHP_EOL;
return "returned";
}
function my_funky_function_array($arg1, $arg2, $arg3)
{
// do some work here
sleep(2); //simulate working time
// test arguments
echo $arg1 . PHP_EOL;
echo $arg2 . PHP_EOL;
echo $arg3 . PHP_EOL;
return array("returned_array");
}
$worker = new AsyncWorker("my_funky_function", "echo1", "echo2"); //will start working immediately
$worker2 = new AsyncWorker("my_funky_function_array", "echo3", "echo4", "echo5");
$result = $worker->wait();
if($worker->has_failed())
echo "FAILURE" . PHP_EOL;
else
echo "Result: $result" . PHP_EOL;
$result2 = $worker2->wait();
if($worker2->has_failed())
echo "FAILURE" . PHP_EOL;
else
echo print_r($result2) . PHP_EOL;
*/