]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/parallelizingdaemon.php
Merge remote-tracking branch 'origin/1.0.x' into 1.0.x
[quix0rs-gnu-social.git] / lib / parallelizingdaemon.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * Base class for making daemons that can do several tasks in parallel.
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  Daemon
23  * @package   StatusNet
24  * @author    Zach Copley <zach@status.net>
25  * @author    Evan Prodromou <evan@status.net>
26  * @copyright 2009 StatusNet, Inc.
27  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
28  * @link      http://status.net/
29  */
30
31 if (!defined('STATUSNET') && !defined('LACONICA')) {
32     exit(1);
33 }
34
35 declare(ticks = 1);
36
37 /**
38  * Daemon able to spawn multiple child processes to do work in parallel
39  *
40  * @category Daemon
41  * @package  StatusNet
42  * @author   Zach Copley <zach@status.net>
43  * @author   Evan Prodromou <evan@status.net>
44  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
45  * @link     http://status.net/
46  */
47
48 class ParallelizingDaemon extends Daemon
49 {
50     private $_children     = array();
51     private $_interval     = 0; // seconds
52     private $_max_children = 0; // maximum number of children
53     private $_debug        = false;
54
55     /**
56      *  Constructor
57      *
58      * @param string  $id           the name/id of this daemon
59      * @param int     $interval     sleep this long before doing everything again
60      * @param int     $max_children maximum number of child processes at a time
61      * @param boolean $debug        debug output flag
62      *
63      * @return void
64      *
65      **/
66
67     function __construct($id = null, $interval = 60, $max_children = 2,
68                          $debug = null)
69     {
70         parent::__construct(true); // daemonize
71
72         $this->_interval     = $interval;
73         $this->_max_children = $max_children;
74         $this->_debug        = $debug;
75
76         if (isset($id)) {
77             $this->set_id($id);
78         }
79     }
80
81     /**
82      * Run the daemon
83      *
84      * @return void
85      */
86
87     function run()
88     {
89         if (isset($this->_debug)) {
90             echo $this->name() . " - Debugging output enabled.\n";
91         }
92
93         do {
94
95             $objects = $this->getObjects();
96
97             foreach ($objects as $o) {
98
99                 // Fork a child for each object
100
101                 $pid = pcntl_fork();
102
103                 if ($pid == -1) {
104                     die ($this->name() . ' - Couldn\'t fork!');
105                 }
106
107                 if ($pid) {
108
109                     // Parent
110
111                     if (isset($this->_debug)) {
112                         echo $this->name() .
113                           " - Forked new child - pid $pid.\n";
114
115                     }
116
117                     $this->_children[] = $pid;
118
119                 } else {
120
121                     // Child
122
123                     // Do something with each object
124
125                     $this->childTask($o);
126
127                     exit();
128                 }
129
130                 // Remove child from ps list as it finishes
131
132                 while (($c = pcntl_wait($status, WNOHANG OR WUNTRACED)) > 0) {
133
134                     if (isset($this->_debug)) {
135                         echo $this->name() . " - Child $c finished.\n";
136                     }
137
138                     $this->removePs($this->_children, $c);
139                 }
140
141                 // Wait! We have too many damn kids.
142
143                 if (sizeof($this->_children) >= $this->_max_children) {
144
145                     if (isset($this->_debug)) {
146                         echo $this->name() . " - Too many children. Waiting...\n";
147                     }
148
149                     if (($c = pcntl_wait($status, WUNTRACED)) > 0) {
150
151                         if (isset($this->_debug)) {
152                             echo $this->name() .
153                               " - Finished waiting for child $c.\n";
154                         }
155
156                         $this->removePs($this->_children, $c);
157                     }
158                 }
159             }
160
161             // Remove all children from the process list before restarting
162             while (($c = pcntl_wait($status, WUNTRACED)) > 0) {
163
164                 if (isset($this->_debug)) {
165                     echo $this->name() . " - Child $c finished.\n";
166                 }
167
168                 $this->removePs($this->_children, $c);
169             }
170
171             // Rest for a bit
172
173             if (isset($this->_debug)) {
174                 echo $this->name() . ' - Waiting ' . $this->_interval .
175                   " secs before running again.\n";
176             }
177
178             if ($this->_interval > 0) {
179                 sleep($this->_interval);
180             }
181
182         } while (true);
183     }
184
185     /**
186      * Remove a child process from the list of children
187      *
188      * @param array &$plist array of processes
189      * @param int   $ps     process id
190      *
191      * @return void
192      */
193
194     function removePs(&$plist, $ps)
195     {
196         for ($i = 0; $i < sizeof($plist); $i++) {
197             if ($plist[$i] == $ps) {
198                 unset($plist[$i]);
199                 $plist = array_values($plist);
200                 break;
201             }
202         }
203     }
204
205     /**
206      * Get a list of objects to work on in parallel
207      *
208      * @return array An array of objects to work on
209      */
210
211     function getObjects()
212     {
213         die('Implement ParallelizingDaemon::getObjects().');
214     }
215
216     /**
217      * Do something with each object in parallel
218      *
219      * @param mixed $object data to work on
220      *
221      * @return void
222      */
223
224     function childTask($object)
225     {
226         die("Implement ParallelizingDaemon::childTask($object).");
227     }
228
229 }