]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/parallelizingdaemon.php
Abstract out the parallelizing daemon stuff
[quix0rs-gnu-social.git] / lib / parallelizingdaemon.php
1 <?php
2 /**
3  * Laconica, 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   Laconica
24  * @author    Zach Copley <zach@controlyourself.ca>
25  * @author    Evan Prodromou <evan@controlyourself.ca>
26  * @copyright 2009 Control Yourself, Inc.
27  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
28  * @link      http://laconi.ca/
29  */
30
31 if (!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  Laconica
42  * @author   Zach Copley <zach@controlyourself.ca>
43  * @author   Evan Prodromou <evan@controlyourself.ca>
44  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
45  * @link     http://laconi.ca/
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                     if (isset($this->_debug)) {
111                         echo $this->name() .
112                           " (parent) forked new child - pid $pid.\n";
113
114                     }
115
116                     $this->_children[] = $pid;
117
118                 } else {
119
120                     // Child
121
122                     // Do something with each object
123                     $this->childTask($o);
124
125                     exit();
126                 }
127
128                 // Remove child from ps list as it finishes
129                 while (($c = pcntl_wait($status, WNOHANG OR WUNTRACED)) > 0) {
130
131                     if (isset($this->_debug)) {
132                         echo $this->name() . " child $c finished.\n";
133                     }
134
135                     $this->removePs($this->_children, $c);
136                 }
137
138                 // Wait! We have too many damn kids.
139                 if (sizeof($this->_children) >= $this->_max_children) {
140
141                     if (isset($this->_debug)) {
142                         echo $this->name() . " - Too many children. Waiting...\n";
143                     }
144
145                     if (($c = pcntl_wait($status, WUNTRACED)) > 0) {
146
147                         if (isset($this->_debug)) {
148                             echo $this->name() .
149                               " - Finished waiting for child $c.\n";
150                         }
151
152                         $this->removePs($this->_children, $c);
153                     }
154                 }
155             }
156
157             // Remove all children from the process list before restarting
158             while (($c = pcntl_wait($status, WUNTRACED)) > 0) {
159
160                 if (isset($this->_debug)) {
161                     echo $this->name() . " child $c finished.\n";
162                 }
163
164                 $this->removePs($this->_children, $c);
165             }
166
167             // Rest for a bit
168
169             if (isset($this->_debug)) {
170                 echo $this->name() . ' - Waiting ' . $this->_interval .
171                   " secs before running again.\n";
172             }
173
174             if ($this->_interval > 0) {
175                 sleep($this->_interval);
176             }
177
178         } while (true);
179     }
180
181     /**
182      * Remove a child process from the list of children
183      *
184      * @param array &$plist array of processes
185      * @param int   $ps     process id
186      *
187      * @return void
188      */
189
190     function removePs(&$plist, $ps)
191     {
192         for ($i = 0; $i < sizeof($plist); $i++) {
193             if ($plist[$i] == $ps) {
194                 unset($plist[$i]);
195                 $plist = array_values($plist);
196                 break;
197             }
198         }
199     }
200
201     /**
202      * Get a list of objects to work on in parallel
203      *
204      * @return array An array of objects to work on
205      */
206
207     function getObjects()
208     {
209         die('Implement ParallelizingDaemon::getObjects().');
210     }
211
212     /**
213      * Do something with each object in parallel
214      *
215      * @param mixed $object data to work on
216      *
217      * @return void
218      */
219
220     function childTask($object)
221     {
222         die("Implement ParallelizingDaemon::childTask($object).");
223     }
224
225 }