1
0
mirror of https://github.com/danog/parallel.git synced 2024-12-02 17:52:14 +01:00
parallel/lib/Context/Internal/Thread.php

149 lines
4.4 KiB
PHP
Raw Normal View History

2016-12-30 02:16:04 +01:00
<?php
namespace Amp\Parallel\Context\Internal;
2016-08-18 18:04:48 +02:00
2017-05-18 09:51:31 +02:00
use Amp\Loop;
use Amp\Parallel\Sync\Channel;
use Amp\Parallel\Sync\ChannelException;
use Amp\Parallel\Sync\ChannelledSocket;
use Amp\Parallel\Sync\ExitFailure;
use Amp\Parallel\Sync\ExitSuccess;
2017-05-18 09:51:31 +02:00
use Amp\Parallel\Sync\SerializationException;
2017-07-19 06:44:25 +02:00
use function Amp\call;
/**
* An internal thread that executes a given function concurrently.
*
* @internal
*/
2018-10-21 17:54:46 +02:00
final class Thread extends \Thread
2018-10-07 16:50:45 +02:00
{
2016-08-18 18:04:48 +02:00
const KILL_CHECK_FREQUENCY = 250;
2016-08-26 17:10:03 +02:00
/** @var callable The function to execute in the thread. */
private $function;
2016-08-26 17:10:03 +02:00
/** @var mixed[] Arguments to pass to the function. */
private $args;
2016-08-26 17:10:03 +02:00
/** @var resource */
private $socket;
2016-08-26 17:10:03 +02:00
/** @var bool */
private $killed = false;
/**
* Creates a new thread object.
*
* @param resource $socket IPC communication socket.
* @param callable $function The function to execute in the thread.
* @param mixed[] $args Arguments to pass to the function.
*/
2018-10-07 16:50:45 +02:00
public function __construct($socket, callable $function, array $args = [])
{
$this->function = $function;
$this->args = $args;
$this->socket = $socket;
}
/**
* Runs the thread code and the initialized function.
*
* @codeCoverageIgnore Only executed in thread.
*/
2018-10-07 16:50:45 +02:00
public function run()
{
/* First thing we need to do is re-initialize the class autoloader. If
* we don't do this first, any object of a class that was loaded after
* the thread started will just be garbage data and unserializable
* values (like resources) will be lost. This happens even with
* thread-safe objects.
*/
// Protect scope by using an unbound closure (protects static access as well).
2017-11-25 15:59:07 +01:00
(static function () {
$paths = [
\dirname(__DIR__, 3) . \DIRECTORY_SEPARATOR . "vendor" . \DIRECTORY_SEPARATOR . "autoload.php",
\dirname(__DIR__, 5) . \DIRECTORY_SEPARATOR . "autoload.php",
];
foreach ($paths as $path) {
if (\file_exists($path)) {
$autoloadPath = $path;
break;
}
}
if (!isset($autoloadPath)) {
2017-12-08 04:26:55 +01:00
throw new \Error("Could not locate autoload.php");
2017-11-25 15:59:07 +01:00
}
require $autoloadPath;
})->bindTo(null, null)();
// At this point, the thread environment has been prepared so begin using the thread.
if ($this->killed) {
return; // Thread killed while requiring autoloader, simply exit.
}
Loop::run(function () {
$watcher = Loop::repeat(self::KILL_CHECK_FREQUENCY, function () {
if ($this->killed) {
Loop::stop();
}
});
Loop::unreference($watcher);
2017-11-30 04:50:04 +01:00
try {
$channel = new ChannelledSocket($this->socket, $this->socket);
2017-12-27 06:16:44 +01:00
yield from $this->execute($channel);
2017-11-30 04:50:04 +01:00
} catch (\Throwable $exception) {
return; // Parent context exited or destroyed thread, no need to continue.
2017-12-27 06:16:44 +01:00
} finally {
Loop::cancel($watcher);
2017-11-30 04:50:04 +01:00
}
});
}
/**
* Sets a local variable to true so the running event loop can check for a kill signal.
*/
2018-10-07 16:50:45 +02:00
public function kill()
{
return $this->killed = true;
}
/**
2016-08-23 23:47:40 +02:00
* @param \Amp\Parallel\Sync\Channel $channel
*
* @return \Generator
*
* @codeCoverageIgnore Only executed in thread.
*/
2018-10-07 16:50:45 +02:00
private function execute(Channel $channel): \Generator
{
try {
$result = new ExitSuccess(yield call($this->function, $channel, ...$this->args));
2016-01-23 07:00:56 +01:00
} catch (\Throwable $exception) {
$result = new ExitFailure($exception);
}
2017-07-19 06:44:25 +02:00
if ($this->killed) {
return; // Parent is not listening for a result.
}
2015-12-12 07:34:41 +01:00
// Attempt to return the result.
try {
2016-01-23 18:20:58 +01:00
try {
2017-07-19 06:44:25 +02:00
yield $channel->send($result);
2016-01-23 18:20:58 +01:00
} catch (SerializationException $exception) {
// Serializing the result failed. Send the reason why.
2017-07-19 06:44:25 +02:00
yield $channel->send(new ExitFailure($exception));
2016-01-23 18:20:58 +01:00
}
} catch (ChannelException $exception) {
// The result was not sendable! The parent context must have died or killed the context.
2015-12-12 07:34:41 +01:00
}
}
}