/** * Will create PGP Fatal on remote thread within loop * @throws Exception */ public function emulateFatalOnRemoteLoop() { if ($this->isExternal()) { $this->loop->nextTick(function () { $this->emulateFatal(); }); } else { $this->callOnChild(__FUNCTION__, func_get_args()); } }
/** * setup the readStream event */ protected function attachReadStream() { $this->ThrowOnConnectionInvalid(); $this->loop->addReadStream($this->connection, function () { $this->readToBuffer(); }); }
public function __call($name, $arguments) { if (method_exists($this->threadClass, $name)) { $threadToCall = array_pop($this->threadsLazy); if ($threadToCall === null) { if ($this->getNumberOfThreads() >= $this->maximumNumberOfThreads) { throw new \RuntimeException("Maximum number of threads reached. Increase the maximum or limit your calls"); } else { $threadToCall = $this->createThreadRunning(); } } $result = call_user_func_array(array($threadToCall, $name), $arguments); //is async? if ($result instanceof AsyncMessage && false === $result->isIsResolved()) { $originalCallback = $result->getResolvedCallback(); //wrap message callback to handle multiple messages at once $newCallback = function (AsyncMessage $message) use($originalCallback, $threadToCall) { if ($message->isIsResolved()) { unset($this->threadsWorking[spl_object_hash($threadToCall)]); $this->threadsLazy[spl_object_hash($threadToCall)] = $threadToCall; $this->CheckThreadLimits(); if (count($this->threadsWorking) <= 0 && $this->lazyThreadTimeoutSec > 0) { $this->loop->addTimer($this->lazyThreadTimeoutSec, function () { $this->CheckThreadLimits(); }); } } if (is_callable($originalCallback)) { $originalCallback($message); } }; $result->setResolvedCallback($newCallback); $this->threadsWorking[spl_object_hash($threadToCall)] = $threadToCall; $this->CheckThreadLimits(); } else { $this->threadsLazy[spl_object_hash($threadToCall)] = $threadToCall; } } else { throw new InvalidArgumentException("Method {$this->threadClass}::{$name} does not exists or is not accessable"); } }
public function fork() { $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP); $pid = pcntl_fork(); $this->isRunning = true; if ($pid <= -1) { $this->isRunning = false; throw new \RuntimeException("Unable to fork child: " . pcntl_strerror(pcntl_get_last_error())); } elseif ($pid === 0) { //child proc try { $this->isExternal = true; $this->childPid = posix_getpid(); set_error_handler(array(ThreadConfig::GetErrorHandler(), 'OnUncaughtErrorTriggered')); register_shutdown_function(function () { //catch fatal on some cases $fatalErrorCodes = array(E_ERROR, E_COMPILE_ERROR, E_PARSE); $error = error_get_last(); if (is_array($error) && isset($error['type']) && in_array($error['type'], $fatalErrorCodes)) { $exception = new SerializableFatalException($error['message'], $error['file'], $error['line'], $error['type']); if ($this->messageInProgress instanceof AsyncMessage) { //mark as error and send back to parent $this->messageInProgress->Error($exception); $this->connection->writeSync($this->messageInProgress); } else { $message = new AsyncMessage(array(), true); $message->setIsOneWay(true); $message->Error($exception); $this->connection->writeSync($message); } } }); $this->loop = $this->loop->afterForkChild(); fclose($sockets[1]); $this->socket = $sockets[0]; $this->connection = new ThreadConnection($this->loop, $this->socket); $this->connection->on('message', function ($connection, AsyncMessage $message) { $this->AttachMessage($message); }); $this->loop->nextTick(function () { $this->messageHandler->InitializeExternal($this); }); $this->loop->run(); } catch (\Exception $e) { $handler = ThreadConfig::GetErrorHandler(); $result = @$handler->OnUncaughtException($e); //redirect exception to client. if ($this->messageInProgress instanceof AsyncMessage) { //mark as error and send back to parent $this->messageInProgress->Error($result); $this->connection->writeSync($this->messageInProgress); } else { $message = new AsyncMessage(array(), true); $message->setIsOneWay(true); $message->Error($result); $this->connection->writeSync($message); } //no more chance to handle here :-( } exit; } else { // parent fclose($sockets[0]); $this->socket = $sockets[1]; $this->getLoop()->afterForkParent(); $this->connection = new ThreadConnection($this->loop, $this->socket); $this->connection->on('message', function ($connection, AsyncMessage $message) { $this->AttachMessage($message); }); //keep an eye on the thread $this->pidCollector = $this->getLoop()->addPeriodicTimer(5, function (TimerInterface $timer) { $this->waitForCompletion($timer); }); $this->childPid = $pid; } }