Manager::stop()   B
last analyzed

Complexity

Conditions 7
Paths 8

Size

Total Lines 27
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 11
nc 8
nop 1
dl 0
loc 27
ccs 0
cts 13
cp 0
crap 56
rs 8.8333
c 0
b 0
f 0
1
<?php namespace Comodojo\Daemon\Worker;
2
3
use \Comodojo\Daemon\Daemon;
4
use \Comodojo\Daemon\Utils\ProcessTools;
5
use \Comodojo\Daemon\Utils\PosixSignals;
6
use \Comodojo\Foundation\Events\EventsTrait;
7
use \Comodojo\Foundation\Logging\LoggerTrait;
8
use \Comodojo\Foundation\Events\Manager as EventsManager;
9
use \Comodojo\Foundation\DataAccess\IteratorTrait;
10
use \Comodojo\Foundation\DataAccess\CountableTrait;
11
use \Psr\Log\LoggerInterface;
12
use \Iterator;
13
use \Countable;
14
use \Exception;
15
16
/**
17
 * @package     Comodojo Daemon
18
 * @author      Marco Giovinazzi <[email protected]>
19
 * @license     MIT
20
 *
21
 * LICENSE:
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29
 * THE SOFTWARE.
30
 */
31
32
 class Manager implements Iterator, Countable {
33
34
    use IteratorTrait;
35
    use CountableTrait;
36
    use EventsTrait;
37
    use LoggerTrait;
38
39
    /**
40
     * The stack :)
41
     *
42
     * @var array
43
     */
44
    private $data = [];
45
46
    /**
47
     * Pointer to parent daemon
48
     *
49
     * @var Daemon
50
     */
51
    private $daemon;
52
53
    /**
54
     * Manager constructor
55
     *
56
     * @param LoggerInterface $logger;
57
     * @param EventsManager $events;
58
     * @param Daemon $daemon;
59
     */
60
    public function __construct(
61
        LoggerInterface $logger,
62
        EventsManager $events,
63
        Daemon $daemon
64
    ) {
65
66
        $this->logger = $logger;
67
        $this->events = $events;
68
        $this->daemon = $daemon;
69
70
    }
71
72
    /**
73
     * Install a worker into the stack
74
     *
75
     * @param WorkerInterface $worker
76
     * @param int $looptime
77
     * @param bool $forever
78
     * @return Manager
79
     */
80
    public function install(
81
        WorkerInterface $worker,
82
        $looptime = 1,
83
        $forever = false
84
    ) {
85
86
        $name = $worker->getName();
87
88
        if ( $this->isInstalled($name) ) {
89
            throw new Exception("Worker already installed");
90
        }
91
92
        $w = Worker::create()
93
            ->setInstance($worker)
94
            ->setLooptime($looptime)
95
            ->setForever($forever)
96
            ->setInputChannel(new SharedMemory((int) '1'.hexdec($worker->getId())))
97
            ->setOutputChannel(new SharedMemory((int) '2'.hexdec($worker->getId())));
98
99
        $this->data[$name] = $w;
100
101
        return $this;
102
103
    }
104
105
    public function setPid($name, $pid) {
106
107
        if ( !$this->isInstalled($name) ) {
108
            throw new Exception("Worker not installed");
109
        }
110
111
        $this->data[$name]->setPid($pid);
112
113
        return $this;
114
115
    }
116
117
    public function get($name = null) {
118
119
        if ( is_null($name) ) return $this->data;
120
121
        if ( !$this->isInstalled($name) ) {
122
            throw new Exception("Worker not installed");
123
        }
124
125
        return $this->data[$name];
126
127
    }
128
129
    public function isInstalled($name) {
130
131
        return array_key_exists($name, $this->data);
132
133
    }
134
135
    public function start($name, $restart = false) {
136
137
        // re-install shmop (if restart)
138
        if ( $restart === true ) {
139
            $worker = $this->get($name);
140
            // close the previous channels
141
            $worker->getInputChannel()->close();
142
            $worker->getOutputChannel()->close();
143
            // open brand new channels
144
            $worker->setInputChannel(new SharedMemory((int) '1'.hexdec($worker->getInstance()->getId())))
145
                ->setOutputChannel(new SharedMemory((int) '2'.hexdec($worker->getInstance()->getId())));
146
        }
147
148
        // fork worker
149
        $pid = pcntl_fork();
150
151
        if ( $pid == -1 ) {
152
            $this->logger->error("Could not create worker $name (fork error)");
153
            $daemon->end(1);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $daemon seems to be never defined.
Loading history...
154
        }
155
156
        if ( $pid ) {
157
            $this->logger->notice("Worker $name created with pid $pid");
158
            $this->setPid($name, $pid);
159
            return;
160
        }
161
162
        // get daemon and worker
163
        $worker = $this->get($name);
164
        $daemon = $this->daemon;
165
166
        // update pid reference
167
        $daemon->setPid(ProcessTools::getPid());
168
169
        // cleanup events
170
        $daemon->getSignals()->any()->setDefault();
171
172
        // unmask signals (if restart)
173
        if ( $restart === true ) {
174
            $daemon->getSignals()->any()->unmask();
175
        }
176
177
        // inject events, logger and signals
178
        $logger = $daemon->getLogger()->withName($name);
0 ignored issues
show
Bug introduced by
The method withName() does not exist on Psr\Log\LoggerInterface. It seems like you code against a sub-type of Psr\Log\LoggerInterface such as Monolog\Logger. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

178
        $logger = $daemon->getLogger()->/** @scrutinizer ignore-call */ withName($name);
Loading history...
179
        // $events = new EventsManager($logger);
180
        $events = clone $this->getEvents();
181
        $signals = new PosixSignals;
182
        $worker->getInstance()->setLogger($logger);
183
        $worker->getInstance()->setEvents($events);
184
        $worker->getInstance()->setSignals($signals);
185
186
        $loop = new Loop($worker);
0 ignored issues
show
Bug introduced by
It seems like $worker can also be of type array; however, parameter $worker of Comodojo\Daemon\Worker\Loop::__construct() does only seem to accept Comodojo\Daemon\Worker\Worker, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

186
        $loop = new Loop(/** @scrutinizer ignore-type */ $worker);
Loading history...
187
188
        $daemon->declass();
189
190
        $loop->start();
191
192
        $daemon->end(0);
193
194
    }
195
196
    public function stop($name = null) {
197
198
        foreach ( $this->data as $wname => $worker ) {
199
200
            $wpid = $worker->getPid();
201
202
            if ( is_null($name) || $name == $wname ) {
203
204
                // fix the wait time;
205
                $time = time() + 5;
206
207
                // try to gently ask the worker to close
208
                $worker->getOutputChannel()->send('stop');
209
210
                while ( time() < $time ) {
211
212
                    if ( !$this->running($wpid) ) break;
213
                    usleep(20000);
214
215
                }
216
217
                // close the shared memory block
218
                $worker->getInputChannel()->close();
219
                $worker->getOutputChannel()->close();
220
221
                // terminate the worker if still alive
222
                if ( $this->running($wpid) ) ProcessTools::term($wpid, 5, SIGTERM);
223
224
            }
225
226
        }
227
228
    }
229
230
    public function pause($name = null) {
231
232
        if ( empty($name) ) {
233
            $result = [];
234
            foreach ( $this->data as $name => $worker ) {
235
                $result[$name] = $worker->getOutputChannel()->send('pause') > 0;
236
            }
237
            return $result;
238
        }
239
240
        return $this->get($name)->getOutputChannel()->send('pause') > 0;
241
242
    }
243
244
    public function resume($name = null) {
245
246
        if ( empty($name) ) {
247
            $result = [];
248
            foreach ( $this->data as $name => $worker ) {
249
                $result[$name] = $worker->getOutputChannel()->send('resume') > 0;
250
            }
251
            return $result;
252
        }
253
254
        return $this->get($name)->getOutputChannel()->send('resume') > 0;
255
256
    }
257
258
    public function running($pid) {
259
260
        return ProcessTools::isRunning($pid);
261
262
    }
263
264
    public function status($name = null) {
265
266
        if ( $name === null ) {
267
268
            $result = [];
269
            foreach ( $this->data as $name => $worker ) {
270
                $result[$name] = $this->getStatus($worker);
271
            }
272
            return $result;
273
274
        }
275
276
        $worker = $this->get($name);
277
278
        return $this->getStatus($worker);
0 ignored issues
show
Bug introduced by
It seems like $worker can also be of type array; however, parameter $worker of Comodojo\Daemon\Worker\Manager::getStatus() does only seem to accept Comodojo\Daemon\Worker\Worker, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

278
        return $this->getStatus(/** @scrutinizer ignore-type */ $worker);
Loading history...
279
280
    }
281
282
    private function getStatus(Worker $worker) {
283
284
        return $worker->getInputChannel()->read();
285
286
    }
287
288
 }
289