Completed
Pull Request — master (#16)
by Akihito
02:30
created

Snidel::fork()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 3
eloc 8
nc 4
nop 3
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 array */
27
    private $signals = [
28
        SIGTERM,
29
        SIGINT,
30
    ];
31
32
    /**
33
     * @param   array $parameter
34
     */
35
    public function __construct($parameter = [])
36
    {
37
        $this->config    = new Config($parameter);
38
        $this->log       = new Log($this->config->get('ownerPid'), $this->config->get('logger'));
39
        $this->container = new Container($this->config, $this->log);
40
        $this->pcntl     = new Pcntl();
41
42
        foreach ($this->signals as $sig) {
43
            $this->pcntl->signal(
44
                $sig,
45
                function ($sig)  {
46
                    $this->log->info('received signal. signo: ' . $sig);
47
                    $this->log->info('--> sending a signal " to children.');
48
                    $this->container->sendSignalToMaster($sig);
49
                    $this->log->info('<-- signal handling has been completed successfully.');
50
                    exit;
51
                },
52
                false
53
            );
54
        }
55
56
        $this->log->info('parent pid: ' . $this->config->get('ownerPid'));
57
    }
58
59
    /**
60
     * this method uses master / worker model.
61
     *
62
     * @param   callable    $callable
63
     * @param   mixed       $args
64
     * @param   string      $tag
65
     * @return  void
66
     * @throws  \RuntimeException
67
     */
68
    public function fork($callable, $args = [], $tag = null)
69
    {
70
        if (!$this->container->existsMaster()) {
71
            $this->container->forkMaster();
72
        }
73
74
        try {
75
            $this->container->enqueue(new Task($callable, $args, $tag));
76
        } catch (\RuntimeException $e) {
77
            throw $e;
78
        }
79
80
        $this->log->info('queued task #' . $this->container->queuedCount());
81
    }
82
83
    /**
84
     * waits until all tasks that queued by Snidel::fork() are completed
85
     *
86
     * @return  void
87
     */
88
    public function wait()
89
    {
90
        $this->container->wait();
91
    }
92
93
    /**
94
     * returns generator which returns a result
95
     *
96
     * @return \Generator
97
     */
98
    public function results()
99
    {
100
        foreach($this->container->results() as $r) {
101
            yield $r;
102
        }
103
    }
104
105
    /**
106
     * @return  bool
107
     */
108
    public function hasError()
109
    {
110
        return $this->container->hasError();
111
    }
112
113
    /**
114
     * @return  \Ackintosh\Snidel\Error
115
     */
116
    public function getError()
117
    {
118
        return $this->container->getError();
119
    }
120
121
    public function __destruct()
122
    {
123
        if ($this->config->get('ownerPid') === getmypid()) {
124
            $this->wait();
125
            if ($this->container->existsMaster()) {
126
                $this->log->info('shutdown master process.');
127
                $this->container->sendSignalToMaster();
128
            }
129
130
            unset($this->container);
131
        }
132
    }
133
}
134