Completed
Push — master ( 468cfb...a6bc20 )
by Tim
06:21 queued 02:51
created

ThreadWorker::getServerContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * \AppserverIo\Server\Workers\ThreadWorker
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Johann Zelger <[email protected]>
15
 * @copyright 2015 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io/server
18
 * @link      http://www.appserver.io
19
 */
20
21
namespace AppserverIo\Server\Workers;
22
23
use AppserverIo\Server\Dictionaries\ServerVars;
24
use AppserverIo\Server\Interfaces\ConfigInterface;
25
use AppserverIo\Server\Interfaces\ConnectionHandlerInterface;
26
use AppserverIo\Server\Interfaces\RequestContextInterface;
27
use AppserverIo\Server\Interfaces\ServerContextInterface;
28
use AppserverIo\Server\Interfaces\ServerInterface;
29
use AppserverIo\Server\Interfaces\WorkerInterface;
30
use AppserverIo\Server\Exceptions\ModuleNotFoundException;
31
use AppserverIo\Server\Exceptions\ConnectionHandlerNotFoundException;
32
use AppserverIo\Server\RequestHandlerThread;
33
use AppserverIo\Server\Sockets\SocketInterface;
34
use AppserverIo\Server\Sockets\StreamSocket;
35
36
/**
37
 * Class ThreadWorker
38
 *
39
 * @author    Johann Zelger <[email protected]>
40
 * @copyright 2015 TechDivision GmbH <[email protected]>
41
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
42
 * @link      https://github.com/appserver-io/server
43
 * @link      http://www.appserver.io
44
 */
45
class ThreadWorker extends \Thread implements WorkerInterface
46
{
47
    /**
48
     * Define's the default value for accept min count
49
     *
50
     * @var int
51
     */
52
    const DEFAULT_ACCEPT_MIN = 8;
53
54
    /**
55
     * Define's the default value for accept max count
56
     *
57
     * @var int
58
     */
59
    const DEFAULT_ACCEPT_MAX = 32;
60
61
    /**
62
     * Hold's the serer connection resource
63
     *
64
     * @var resource
65
     */
66
    protected $serverConnectionResource;
67
68
    /**
69
     * Holds the server context object
70
     *
71
     * @var \AppserverIo\Server\Interfaces\ServerContextInterface
72
     */
73
    protected $serverContext;
74
75
    /**
76
     * Hold's an array of connection handlers to use
77
     *
78
     * @var array
79
     */
80
    protected $connectionHandlers;
81
82
    /**
83
     * Defines the minimum count of connections for the worker to accept
84
     *
85
     * @var int
86
     */
87
    protected $acceptMin;
88
89
    /**
90
     * Defines the maximum count of connections for the worker to accept
91
     *
92
     * @var int
93
     */
94
    protected $acceptMax;
95
96
    /**
97
     * Flag if worker should be restarted by server
98
     *
99
     * @var bool
100
     */
101
    public $shouldRestart;
102
103
    /**
104
     * Constructs the worker by setting the server context
105
     *
106
     * @param resource                                              $serverConnectionResource The server's file descriptor resource
107
     * @param \AppserverIo\Server\Interfaces\ServerContextInterface $serverContext            The server's context
108
     * @param array                                                 $connectionHandlers       An array of connection handlers to use
109
     */
110
    public function __construct($serverConnectionResource, ServerContextInterface $serverContext, array $connectionHandlers)
111
    {
112
        $this->serverConnectionResource = $serverConnectionResource;
113
        // connection context init
114
        $this->serverContext = $serverContext;
115
        // connection handler init
116
        $this->connectionHandlers = $connectionHandlers;
117
        // init woker
118
        $this->init();
119
        // autostart worker
120
        $this->start(PTHREADS_INHERIT_NONE | PTHREADS_INHERIT_CONSTANTS | PTHREADS_ALLOW_HEADERS);
121
    }
122
123
    /**
124
     * Init's the worker before it runs
125
     *
126
     * @return void
127
     */
128
    public function init()
129
    {
130
        // get server config to local ref
131
        $serverConfig = $this->getServerContext()->getServerConfig();
132
        // read min and max accept stuff out of config
133
        $this->acceptMin = $serverConfig->getWorkerAcceptMin();
134
        $this->acceptMax = $serverConfig->getWorkerAcceptMax();
135
    }
136
137
    /**
138
     * Return's an array of connection handlers to use
139
     *
140
     * @return array
141
     */
142
    public function getConnectionHandlers()
143
    {
144
        return $this->connectionHandlers;
145
    }
146
147
    /**
148
     * Return's the server context instance
149
     *
150
     * @return \AppserverIo\Server\Interfaces\ServerContextInterface The server's context
151
     */
152
    public function getServerContext()
153
    {
154
        return $this->serverContext;
155
    }
156
157
    /**
158
     * Return's the server's connection resource ref
159
     *
160
     * @return resource
161
     */
162
    protected function getServerConnectionResource()
163
    {
164
        return $this->serverConnectionResource;
165
    }
166
167
    /**
168
     * Starts the worker doing logic.
169
     *
170
     * @return void
171
     */
172
    public function run()
173
    {
174
        // set current dir to base dir for relative dirs
175
        chdir(SERVER_BASEDIR);
176
        // setup environment for worker
177
        require SERVER_AUTOLOADER;
178
        // prepare worker for upcoming connections in specific context
179
        $this->prepare();
180
        // register shutdown handler
181
        register_shutdown_function(array(&$this, "shutdown"));
182
        // do work
183
        $this->work();
184
    }
185
186
    /**
187
     * Prepares the worker's in it's own context for upcoming work to do on things
188
     * that can not be shared by using the init method in the parent's context.
189
     *
190
     * @return void
191
     */
192
    public function prepare()
193
    {
194
        // get local ref of connection handlers
195
        $connectionHandlers = $this->getConnectionHandlers();
196
        // iterate then and call prepare on the it's modules
197
        foreach ($connectionHandlers as $connectionHandler) {
198
            // iterate all modules of connection handler
199
            foreach ($connectionHandler->getModules() as $name => $moduleInstance) {
200
                // prepare things in worker context
201
                $moduleInstance->prepare();
202
            }
203
        }
204
    }
205
206
    /**
207
     * Implements the workers actual logic
208
     *
209
     * @return void
210
     *
211
     * @throws \AppserverIo\Server\Exceptions\ModuleNotFoundException
212
     * @throws \AppserverIo\Server\Exceptions\ConnectionHandlerNotFoundException
213
     */
214
    public function work()
215
    {
216
        // get server context
217
        $serverContext = $this->getServerContext();
218
        // get connection handlers
219
        $connectionHandlers = $this->getConnectionHandlers();
220
221
        // set should restart initial flag
222
        $this->shouldRestart = false;
223
224
        try {
225
            // get socket type
226
            $socketType = $serverContext->getServerConfig()->getSocketType();
227
228
            /** @var SocketInterface $socketType */
229
            // build connection instance by resource
230
            $serverConnection = $socketType::getInstance($this->serverConnectionResource);
231
232
            // init connection count
233
            $connectionCount = 0;
234
            $connectionLimit = rand($this->getAcceptMin(), $this->getAcceptMax());
235
236
            // while worker not reached connection limit accept connections and process
237
            while (++$connectionCount <= $connectionLimit) {
238
                // accept connections and process working connection by handlers
239
                if (($connection = $serverConnection->accept()) !== false) {
240
                    /**
241
                     * This is for testing async request processing only.
242
                     *
243
                     * It'll delegate the request handling to another thread which will be processed async.
244
                     *
245
                    // call async request handler to handle connection
246
                    $requestHandler = new RequestHandlerThread(
247
                        $connection->getConnectionResource(),
248
                        $connectionHandlers,
249
                        $serverContext,
250
                        $this
251
                    ); */
252
253
                    // iterate all connection handlers to handle connection right
254
                    foreach ($connectionHandlers as $connectionHandler) {
255
                        // if connectionHandler handled connection than break out of foreach
256
                        if ($connectionHandler->handle($connection, $this)) {
257
                            break;
258
                        }
259
                    }
260
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
261
                }
262
            }
263
        } catch (\Exception $e) {
264
            // log error
265
            $serverContext->getLogger()->error($e->__toString());
266
        }
267
        // call internal shutdown
268
        $this->shutdown();
269
    }
270
271
    /**
272
     * Does shutdown logic for worker if something breaks in process.
273
     *
274
     * This shutdown function will be called from specific connection handler if an error occurs, so the connection
275
     * handler can send an response in the correct protocol specifications and a new worker can be started
276
     *
277
     * @return void
278
     */
279
    public function shutdown()
280
    {
281
        // check if there was a fatal error caused shutdown
282
        $lastError = error_get_last();
283
        if ($lastError['type'] === E_ERROR || $lastError['type'] === E_USER_ERROR) {
284
            // log error
285
            $this->getServerContext()->getLogger()->error($lastError['message']);
286
        }
287
        $this->shouldRestart = true;
288
    }
289
290
    /**
291
     * Return's if worker should be restarted by server
292
     *
293
     * @return bool
294
     */
295
    public function shouldRestart()
296
    {
297
        return $this->shouldRestart;
298
    }
299
300
    /**
301
     * Return's the max count for the worker to accept
302
     *
303
     * @return int
304
     */
305
    public function getAcceptMax()
306
    {
307
        if ($this->acceptMax) {
308
            return $this->acceptMax;
309
        }
310
        return self::DEFAULT_ACCEPT_MAX;
311
    }
312
313
    /**
314
     * Return's the min count for the worker to accept
315
     *
316
     * @return int
317
     */
318
    public function getAcceptMin()
319
    {
320
        if ($this->acceptMin) {
321
            return $this->acceptMin;
322
        }
323
        return self::DEFAULT_ACCEPT_MIN;
324
    }
325
}
326