Пример #1
0
 /**
  * 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;
     }
 }