Completed
Pull Request — master (#16)
by Akihito
03:28
created

Snidel::__destruct()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 17
rs 8.8571
cc 6
eloc 10
nc 6
nop 0
1
<?php
2
declare(ticks = 1);
3
4
namespace Ackintosh;
5
6
use Ackintosh\Snidel\Config;
7
use Ackintosh\Snidel\Fork\Container;
8
use Ackintosh\Snidel\Log;
9
use Ackintosh\Snidel\Pcntl;
10
use Ackintosh\Snidel\Task\Task;
11
12
class Snidel
13
{
14
    /** @var \Ackintosh\Snidel\Config */
15
    private $config;
16
17
    /** @var \Ackintosh\Snidel\Fork\Container */
18
    private $container;
19
20
    /** @var \Ackintosh\Snidel\Pcntl */
21
    private $pcntl;
22
23
    /** @var \Ackintosh\Snidel\Log */
24
    private $log;
25
26
    /** @var bool */
27
    private $joined = false;
28
29
    /** @var array */
30
    private $signals = [
31
        SIGTERM,
32
        SIGINT,
33
    ];
34
35
    /** @var int */
36
    private $receivedSignal;
37
38
    /**
39
     * @param   mixed $parameter
40
     * @throws  \InvalidArgumentException
41
     */
42
    public function __construct($parameter = null)
43
    {
44
        if (is_null($parameter)) {
45
            $this->config = new Config();
46
        } elseif (is_int($parameter) && $parameter >= 1) {
47
            $this->config = new Config(
48
                ['concurrency' => $parameter]
49
            );
50
        } elseif (is_array($parameter)) {
51
            $this->config = new Config($parameter);
52
        } else {
53
            throw new \InvalidArgumentException();
54
        }
55
56
        $this->log       = new Log($this->config->get('ownerPid'), $this->config->get('logger'));
57
        $this->container = new Container($this->config, $this->log);
58
        $this->pcntl     = new Pcntl();
59
60
        foreach ($this->signals as $sig) {
61
            $this->pcntl->signal(
62
                $sig,
63
                function ($sig)  {
64
                    $this->log->info('received signal. signo: ' . $sig);
65
                    $this->setReceivedSignal($sig);
66
67
                    $this->log->info('--> sending a signal " to children.');
68
                    $this->container->sendSignalToMaster($sig);
69
                    $this->log->info('<-- signal handling has been completed successfully.');
70
                    exit;
71
                },
72
                false
73
            );
74
        }
75
76
        $this->log->info('parent pid: ' . $this->config->get('ownerPid'));
77
    }
78
79
    /**
80
     * this method uses master / worker model.
81
     *
82
     * @param   callable    $callable
83
     * @param   mixed       $args
84
     * @param   string      $tag
85
     * @return  void
86
     * @throws  \RuntimeException
87
     */
88
    public function fork($callable, $args = [], $tag = null)
89
    {
90
        $this->joined = false;
91
92
        if (!$this->container->existsMaster()) {
93
            $this->container->forkMaster();
94
        }
95
96
        try {
97
            $this->container->enqueue(new Task($callable, $args, $tag));
98
        } catch (\RuntimeException $e) {
99
            throw $e;
100
        }
101
102
        $this->log->info('queued task #' . $this->container->queuedCount());
103
    }
104
105
    /**
106
     * waits until all tasks that queued by Snidel::fork() are completed
107
     *
108
     * @return  void
109
     */
110
    public function wait()
111
    {
112
        $this->container->wait();
113
        $this->joined = true;
114
    }
115
116
    /**
117
     * returns generator which returns a result
118
     *
119
     * @return \Generator
120
     */
121
    public function results()
122
    {
123
        foreach($this->container->results() as $r) {
124
            yield $r;
125
        }
126
127
        $this->joined = true;
128
    }
129
130
    /**
131
     * @return  bool
132
     */
133
    public function hasError()
134
    {
135
        return $this->container->hasError();
136
    }
137
138
    /**
139
     * @return  \Ackintosh\Snidel\Error
140
     */
141
    public function getError()
142
    {
143
        return $this->container->getError();
144
    }
145
146
    public function setReceivedSignal($sig)
147
    {
148
        $this->receivedSignal = $sig;
149
    }
150
151
    public function __destruct()
152
    {
153
        if ($this->config->get('ownerPid') === getmypid()) {
154
            if ($this->container->existsMaster()) {
155
                $this->log->info('shutdown master process.');
156
                $this->container->sendSignalToMaster();
157
            }
158
159
            unset($this->container);
160
        }
161
162
        if ($this->config->get('ownerPid') === getmypid() && !$this->joined && $this->receivedSignal === null) {
163
            $message = 'snidel will have to wait for the child process is completed. please use Snidel::wait()';
164
            $this->log->error($message);
165
            throw new \LogicException($message);
166
        }
167
    }
168
}
169