mirror of
https://github.com/danog/parallel-functions.git
synced 2024-11-26 20:34:49 +01:00
Rewrite on top of opis/closure
This commit is contained in:
parent
43c6fc74c9
commit
f945e64f32
@ -21,7 +21,7 @@
|
|||||||
"php": ">=7",
|
"php": ">=7",
|
||||||
"amphp/parallel": "^0.1",
|
"amphp/parallel": "^0.1",
|
||||||
"amphp/amp": "^2",
|
"amphp/amp": "^2",
|
||||||
"jeremeamia/SuperClosure": "^2.3"
|
"opis/closure": "^3.0.7"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"amphp/phpunit-util": "^1.0",
|
"amphp/phpunit-util": "^1.0",
|
||||||
|
@ -4,20 +4,9 @@ namespace Amp\ParallelFunctions\Internal;
|
|||||||
|
|
||||||
use Amp\Parallel\Worker\Environment;
|
use Amp\Parallel\Worker\Environment;
|
||||||
use Amp\Parallel\Worker\Task;
|
use Amp\Parallel\Worker\Task;
|
||||||
use SuperClosure\Exception\ClosureUnserializationException;
|
|
||||||
use SuperClosure\Serializer;
|
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
class ParallelTask implements Task {
|
class ParallelTask implements Task {
|
||||||
const TYPE_SIMPLE = 0;
|
|
||||||
const TYPE_CLOSURE = 1;
|
|
||||||
|
|
||||||
/** @var Serializer */
|
|
||||||
private static $serializer;
|
|
||||||
|
|
||||||
/** @var int */
|
|
||||||
private $type;
|
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $function;
|
private $function;
|
||||||
|
|
||||||
@ -25,30 +14,16 @@ class ParallelTask implements Task {
|
|||||||
private $args;
|
private $args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $type Type of function.
|
|
||||||
* @param string $function Serialized function.
|
* @param string $function Serialized function.
|
||||||
* @param array $args Arguments to pass to the function. Must be serializable.
|
* @param array $args Arguments to pass to the function. Must be serializable.
|
||||||
*
|
|
||||||
* @throws ClosureUnserializationException
|
|
||||||
*/
|
*/
|
||||||
public function __construct(int $type, string $function, array $args) {
|
public function __construct(string $function, array $args) {
|
||||||
$this->type = $type;
|
|
||||||
$this->function = $function;
|
$this->function = $function;
|
||||||
$this->args = $args;
|
$this->args = $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(Environment $environment) {
|
public function run(Environment $environment) {
|
||||||
if (self::$serializer === null) {
|
$callable = \unserialize($this->function, ['allowed_classes' => true]);
|
||||||
static::$serializer = new Serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->type === self::TYPE_SIMPLE) {
|
|
||||||
$callable = $this->function;
|
|
||||||
} elseif ($this->type === self::TYPE_CLOSURE) {
|
|
||||||
$callable = self::$serializer->unserialize($this->function);
|
|
||||||
} else {
|
|
||||||
throw \Error('Unsupported parallel task type: ' . $this->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $callable(...$this->args);
|
return $callable(...$this->args);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ namespace Amp\ParallelFunctions;
|
|||||||
use Amp\MultiReasonException;
|
use Amp\MultiReasonException;
|
||||||
use Amp\ParallelFunctions\Internal\ParallelTask;
|
use Amp\ParallelFunctions\Internal\ParallelTask;
|
||||||
use Amp\Promise;
|
use Amp\Promise;
|
||||||
use SuperClosure\Serializer;
|
use Opis\Closure\SerializableClosure;
|
||||||
use function Amp\call;
|
use function Amp\call;
|
||||||
use function Amp\Parallel\Worker\enqueue;
|
use function Amp\Parallel\Worker\enqueue;
|
||||||
use function Amp\Promise\any;
|
use function Amp\Promise\any;
|
||||||
@ -19,39 +19,20 @@ use function Amp\Promise\any;
|
|||||||
* @throws \Error If the passed callable is not safely serializable.
|
* @throws \Error If the passed callable is not safely serializable.
|
||||||
*/
|
*/
|
||||||
function parallel(callable $callable): callable {
|
function parallel(callable $callable): callable {
|
||||||
static $serializer, $errorHandler;
|
try {
|
||||||
|
if (\is_string($callable)) {
|
||||||
if ($serializer === null) {
|
$payload = \serialize($callable);
|
||||||
$serializer = new Serializer;
|
} elseif ($callable instanceof \Closure) {
|
||||||
|
$payload = \serialize(new SerializableClosure($callable));
|
||||||
|
} else {
|
||||||
|
throw new \Error('Unsupported callable type: ' . \gettype($callable));
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw new \Error('Unsupported callable: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (\is_string($callable)) {
|
return function (...$args) use ($payload): Promise {
|
||||||
$payload = $callable;
|
return enqueue(new ParallelTask($payload, $args));
|
||||||
$type = ParallelTask::TYPE_SIMPLE;
|
|
||||||
} elseif ($callable instanceof \Closure) {
|
|
||||||
if ($errorHandler === null) {
|
|
||||||
$errorHandler = function ($errno, $errstr) {
|
|
||||||
if ($errno & \error_reporting()) {
|
|
||||||
throw new \Error($errstr);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set custom error handler because Serializer only issues a notice if serialization fails.
|
|
||||||
\set_error_handler($errorHandler);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$payload = $serializer->serialize($callable);
|
|
||||||
$type = ParallelTask::TYPE_CLOSURE;
|
|
||||||
} finally {
|
|
||||||
\restore_error_handler();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new \Error('Unsupported callable type: ' . \gettype($callable));
|
|
||||||
}
|
|
||||||
|
|
||||||
return function (...$args) use ($type, $payload): Promise {
|
|
||||||
return enqueue(new ParallelTask($type, $payload, $args));
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +43,7 @@ function parallel(callable $callable): callable {
|
|||||||
* @param callable $callable
|
* @param callable $callable
|
||||||
*
|
*
|
||||||
* @return Promise Resolves to the result once the operation finished.
|
* @return Promise Resolves to the result once the operation finished.
|
||||||
* @throws \Error
|
* @throws \Error If the passed callable is not safely serializable.
|
||||||
*/
|
*/
|
||||||
function parallelMap(array $array, callable $callable): Promise {
|
function parallelMap(array $array, callable $callable): Promise {
|
||||||
return call(function () use ($array, $callable) {
|
return call(function () use ($array, $callable) {
|
||||||
@ -86,6 +67,7 @@ function parallelMap(array $array, callable $callable): Promise {
|
|||||||
* @param int $flag
|
* @param int $flag
|
||||||
*
|
*
|
||||||
* @return Promise
|
* @return Promise
|
||||||
|
* @throws \Error If the passed callable is not safely serializable.
|
||||||
*/
|
*/
|
||||||
function parallelFilter(array $array, callable $callable = null, int $flag = 0): Promise {
|
function parallelFilter(array $array, callable $callable = null, int $flag = 0): Promise {
|
||||||
return call(function () use ($array, $callable, $flag) {
|
return call(function () use ($array, $callable, $flag) {
|
||||||
|
@ -8,12 +8,12 @@ use function Amp\ParallelFunctions\parallel;
|
|||||||
class ParallelTest extends TestCase {
|
class ParallelTest extends TestCase {
|
||||||
/**
|
/**
|
||||||
* @expectedException \Error
|
* @expectedException \Error
|
||||||
* @expectedExceptionMessage Serialization of closure failed
|
* @expectedExceptionMessage Unsupported callable: Serialization of 'class@anonymous' is not allowed
|
||||||
*/
|
*/
|
||||||
public function testUnserializableClosure() {
|
public function testUnserializableClosure() {
|
||||||
$unserializable = new class {
|
$unserializable = new class {
|
||||||
};
|
};
|
||||||
$callable = parallel(function () use ($unserializable) {
|
parallel(function () use ($unserializable) {
|
||||||
return 1;
|
return 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user