mirror of
https://github.com/danog/parallel.git
synced 2024-12-02 09:37:57 +01:00
Update tests; code cleanup
This commit is contained in:
parent
0e1e0393ff
commit
70097cb225
@ -2,6 +2,8 @@ language: php
|
|||||||
|
|
||||||
php:
|
php:
|
||||||
- 7.0
|
- 7.0
|
||||||
|
- 7.1
|
||||||
|
- nightly
|
||||||
|
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
@ -24,6 +26,3 @@ script:
|
|||||||
after_script:
|
after_script:
|
||||||
- composer require satooshi/php-coveralls dev-master
|
- composer require satooshi/php-coveralls dev-master
|
||||||
- vendor/bin/coveralls -v --exclude-no-stmt
|
- vendor/bin/coveralls -v --exclude-no-stmt
|
||||||
|
|
||||||
notifications:
|
|
||||||
slack: icicleio:zLkB18HsYCz8TbdK7RVqbi48
|
|
||||||
|
@ -50,9 +50,8 @@
|
|||||||
},
|
},
|
||||||
"autoload-dev": {
|
"autoload-dev": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Amp\\Benchmarks\\Concurrent\\": "benchmarks",
|
"Amp\\Concurrent\\Example\\": "examples",
|
||||||
"Amp\\Examples\\Concurrent\\": "examples",
|
"Amp\\Concurrent\\Test\\": "test"
|
||||||
"Amp\\Tests\\Concurrent\\": "tests"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Amp\Examples\Concurrent;
|
namespace Amp\Concurrent\Example;
|
||||||
|
|
||||||
use Amp\Concurrent\Worker\Environment;
|
use Amp\Concurrent\Worker\{ Environment, Task };
|
||||||
use Amp\Concurrent\Worker\Task;
|
|
||||||
|
|
||||||
class BlockingTask implements Task
|
class BlockingTask implements Task {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* @var callable
|
* @var callable
|
||||||
*/
|
*/
|
||||||
@ -20,8 +18,7 @@ class BlockingTask implements Task
|
|||||||
* @param callable $function Do not use a closure or non-serializable object.
|
* @param callable $function Do not use a closure or non-serializable object.
|
||||||
* @param mixed ...$args Arguments to pass to the function. Must be serializable.
|
* @param mixed ...$args Arguments to pass to the function. Must be serializable.
|
||||||
*/
|
*/
|
||||||
public function __construct(callable $function, ...$args)
|
public function __construct(callable $function, ...$args) {
|
||||||
{
|
|
||||||
$this->function = $function;
|
$this->function = $function;
|
||||||
$this->args = $args;
|
$this->args = $args;
|
||||||
}
|
}
|
||||||
@ -29,8 +26,7 @@ class BlockingTask implements Task
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function run(Environment $environment)
|
public function run(Environment $environment) {
|
||||||
{
|
|
||||||
return ($this->function)(...$this->args);
|
return ($this->function)(...$this->args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ require dirname(__DIR__).'/vendor/autoload.php';
|
|||||||
|
|
||||||
use Amp\Concurrent\Worker\DefaultPool;
|
use Amp\Concurrent\Worker\DefaultPool;
|
||||||
use Amp\Coroutine;
|
use Amp\Coroutine;
|
||||||
use Amp\Examples\Concurrent\BlockingTask;
|
use Amp\Concurrent\Example\BlockingTask;
|
||||||
|
|
||||||
Amp\execute(function() {
|
Amp\execute(function() {
|
||||||
$timer = Amp\repeat(100, function () {
|
$timer = Amp\repeat(100, function () {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
require dirname(__DIR__).'/vendor/autoload.php';
|
require dirname(__DIR__).'/vendor/autoload.php';
|
||||||
|
|
||||||
use Amp\Concurrent\Worker\DefaultWorkerFactory;
|
use Amp\Concurrent\Worker\DefaultWorkerFactory;
|
||||||
use Amp\Examples\Concurrent\BlockingTask;
|
use Amp\Concurrent\Example\BlockingTask;
|
||||||
|
|
||||||
Amp\execute(function () {
|
Amp\execute(function () {
|
||||||
$factory = new DefaultWorkerFactory();
|
$factory = new DefaultWorkerFactory();
|
||||||
|
@ -25,7 +25,7 @@ class PanicError extends \Error {
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getPanicTrace() {
|
public function getPanicTrace(): string {
|
||||||
return $this->trace;
|
return $this->trace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,8 +305,7 @@ class Process implements ProcessContext {
|
|||||||
*
|
*
|
||||||
* @return mixed[] Array of options.
|
* @return mixed[] Array of options.
|
||||||
*/
|
*/
|
||||||
public function getOptions(): array
|
public function getOptions(): array {
|
||||||
{
|
|
||||||
return $this->options;
|
return $this->options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Amp\Concurrent\Sync;
|
namespace Amp\Concurrent\Sync;
|
||||||
|
|
||||||
use Amp\Concurrent\{ChannelException, SerializationException};
|
use Amp\Concurrent\{ ChannelException, SerializationException };
|
||||||
use Amp\Coroutine;
|
use Amp\Coroutine;
|
||||||
use Amp\Stream\Stream;
|
use Amp\Stream\Stream;
|
||||||
use Interop\Async\Awaitable;
|
use Interop\Async\Awaitable;
|
||||||
|
@ -32,8 +32,7 @@ class FileMutex implements Mutex {
|
|||||||
/**
|
/**
|
||||||
* Creates a new mutex.
|
* Creates a new mutex.
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct() {
|
||||||
{
|
|
||||||
$this->fileName = \tempnam(\sys_get_temp_dir(), 'mutex-') . '.lock';
|
$this->fileName = \tempnam(\sys_get_temp_dir(), 'mutex-') . '.lock';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ class PosixSemaphore implements Semaphore, \Serializable {
|
|||||||
if ($errno !== MSG_ENOMSG) {
|
if ($errno !== MSG_ENOMSG) {
|
||||||
throw new SemaphoreException('Failed to acquire a lock.');
|
throw new SemaphoreException('Failed to acquire a lock.');
|
||||||
}
|
}
|
||||||
} while (yield new Pause(self::LATENCY_TIMEOUT));
|
} while (yield new Pause(self::LATENCY_TIMEOUT, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -216,8 +216,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
*
|
*
|
||||||
* Calling `free()` on an object already freed will have no effect.
|
* Calling `free()` on an object already freed will have no effect.
|
||||||
*/
|
*/
|
||||||
public function free()
|
public function free() {
|
||||||
{
|
|
||||||
if (!$this->isFreed()) {
|
if (!$this->isFreed()) {
|
||||||
// Invalidate the memory block by setting its state to FREED.
|
// Invalidate the memory block by setting its state to FREED.
|
||||||
$this->setHeader(static::STATE_FREED, 0, 0);
|
$this->setHeader(static::STATE_FREED, 0, 0);
|
||||||
@ -239,8 +238,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
*
|
*
|
||||||
* @return string The serialized object handle.
|
* @return string The serialized object handle.
|
||||||
*/
|
*/
|
||||||
public function serialize(): string
|
public function serialize(): string {
|
||||||
{
|
|
||||||
return \serialize([$this->key, $this->semaphore]);
|
return \serialize([$this->key, $this->semaphore]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,8 +247,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
*
|
*
|
||||||
* @param string $serialized The serialized object handle.
|
* @param string $serialized The serialized object handle.
|
||||||
*/
|
*/
|
||||||
public function unserialize($serialized)
|
public function unserialize($serialized) {
|
||||||
{
|
|
||||||
list($this->key, $this->semaphore) = \unserialize($serialized);
|
list($this->key, $this->semaphore) = \unserialize($serialized);
|
||||||
$this->memOpen($this->key, 'w', 0, 0);
|
$this->memOpen($this->key, 'w', 0, 0);
|
||||||
}
|
}
|
||||||
@ -258,8 +255,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function __clone()
|
public function __clone() {
|
||||||
{
|
|
||||||
$value = $this->unwrap();
|
$value = $this->unwrap();
|
||||||
$header = $this->getHeader();
|
$header = $this->getHeader();
|
||||||
$this->init($value, $header['size'], $header['permissions']);
|
$this->init($value, $header['size'], $header['permissions']);
|
||||||
@ -270,8 +266,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
*
|
*
|
||||||
* @return array An array of debugging information.
|
* @return array An array of debugging information.
|
||||||
*/
|
*/
|
||||||
public function __debugInfo()
|
public function __debugInfo() {
|
||||||
{
|
|
||||||
if ($this->isFreed()) {
|
if ($this->isFreed()) {
|
||||||
return [
|
return [
|
||||||
'id' => $this->key,
|
'id' => $this->key,
|
||||||
@ -291,8 +286,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
* Updates the current memory segment handle, handling any moves made on the
|
* Updates the current memory segment handle, handling any moves made on the
|
||||||
* data.
|
* data.
|
||||||
*/
|
*/
|
||||||
private function handleMovedMemory()
|
private function handleMovedMemory() {
|
||||||
{
|
|
||||||
// Read from the memory block and handle moved blocks until we find the
|
// Read from the memory block and handle moved blocks until we find the
|
||||||
// correct block.
|
// correct block.
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -315,8 +309,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
*
|
*
|
||||||
* @return array An associative array of header data.
|
* @return array An associative array of header data.
|
||||||
*/
|
*/
|
||||||
private function getHeader(): array
|
private function getHeader(): array {
|
||||||
{
|
|
||||||
$data = $this->memGet(0, self::MEM_DATA_OFFSET);
|
$data = $this->memGet(0, self::MEM_DATA_OFFSET);
|
||||||
return \unpack('Cstate/Lsize/Spermissions', $data);
|
return \unpack('Cstate/Lsize/Spermissions', $data);
|
||||||
}
|
}
|
||||||
@ -328,8 +321,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
* @param int $size The size of the stored data, or other value.
|
* @param int $size The size of the stored data, or other value.
|
||||||
* @param int $permissions The permissions mask on the memory segment.
|
* @param int $permissions The permissions mask on the memory segment.
|
||||||
*/
|
*/
|
||||||
private function setHeader(int $state, int $size, int $permissions)
|
private function setHeader(int $state, int $size, int $permissions) {
|
||||||
{
|
|
||||||
$header = \pack('CLS', $state, $size, $permissions);
|
$header = \pack('CLS', $state, $size, $permissions);
|
||||||
$this->memSet(0, $header);
|
$this->memSet(0, $header);
|
||||||
}
|
}
|
||||||
@ -342,8 +334,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
* @param int $permissions Process permissions on the shared memory.
|
* @param int $permissions Process permissions on the shared memory.
|
||||||
* @param int $size The size to crate the shared memory in bytes.
|
* @param int $size The size to crate the shared memory in bytes.
|
||||||
*/
|
*/
|
||||||
private function memOpen(int $key, string $mode, int $permissions, int $size)
|
private function memOpen(int $key, string $mode, int $permissions, int $size) {
|
||||||
{
|
|
||||||
$this->handle = @\shmop_open($key, $mode, $permissions, $size);
|
$this->handle = @\shmop_open($key, $mode, $permissions, $size);
|
||||||
if ($this->handle === false) {
|
if ($this->handle === false) {
|
||||||
throw new SharedMemoryException('Failed to create shared memory block.');
|
throw new SharedMemoryException('Failed to create shared memory block.');
|
||||||
@ -358,8 +349,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
*
|
*
|
||||||
* @return string The binary data at the given offset.
|
* @return string The binary data at the given offset.
|
||||||
*/
|
*/
|
||||||
private function memGet(int $offset, int $size): string
|
private function memGet(int $offset, int $size): string {
|
||||||
{
|
|
||||||
$data = \shmop_read($this->handle, $offset, $size);
|
$data = \shmop_read($this->handle, $offset, $size);
|
||||||
if ($data === false) {
|
if ($data === false) {
|
||||||
throw new SharedMemoryException('Failed to read from shared memory block.');
|
throw new SharedMemoryException('Failed to read from shared memory block.');
|
||||||
@ -373,8 +363,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
* @param int $offset The offset to write to.
|
* @param int $offset The offset to write to.
|
||||||
* @param string $data The binary data to write.
|
* @param string $data The binary data to write.
|
||||||
*/
|
*/
|
||||||
private function memSet(int $offset, string $data)
|
private function memSet(int $offset, string $data) {
|
||||||
{
|
|
||||||
if (!\shmop_write($this->handle, $data, $offset)) {
|
if (!\shmop_write($this->handle, $data, $offset)) {
|
||||||
throw new SharedMemoryException('Failed to write to shared memory block.');
|
throw new SharedMemoryException('Failed to write to shared memory block.');
|
||||||
}
|
}
|
||||||
@ -383,8 +372,7 @@ class SharedMemoryParcel implements Parcel, \Serializable {
|
|||||||
/**
|
/**
|
||||||
* Requests the shared memory segment to be deleted.
|
* Requests the shared memory segment to be deleted.
|
||||||
*/
|
*/
|
||||||
private function memDelete()
|
private function memDelete() {
|
||||||
{
|
|
||||||
if (!\shmop_delete($this->handle)) {
|
if (!\shmop_delete($this->handle)) {
|
||||||
throw new SharedMemoryException('Failed to discard shared memory block.');
|
throw new SharedMemoryException('Failed to discard shared memory block.');
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,7 @@ class Thread extends \Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (null === $autoloadPath) {
|
if (null === $autoloadPath) {
|
||||||
echo 'Could not locate autoload.php.';
|
throw new \Error('Could not locate autoload.php');
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
require $autoloadPath;
|
require $autoloadPath;
|
||||||
@ -85,11 +84,7 @@ class Thread extends \Thread {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
\Amp\execute(function () {
|
\Amp\execute(function () {
|
||||||
try {
|
$channel = new ChannelledStream(new Socket($this->socket, false));
|
||||||
$channel = new ChannelledStream(new Socket($this->socket, false));
|
|
||||||
} catch (\Throwable $exception) {
|
|
||||||
return 1; // Parent has destroyed Thread object, so just exit.
|
|
||||||
}
|
|
||||||
|
|
||||||
$watcher = \Amp\repeat(self::KILL_CHECK_FREQUENCY, function () {
|
$watcher = \Amp\repeat(self::KILL_CHECK_FREQUENCY, function () {
|
||||||
if ($this->killed) {
|
if ($this->killed) {
|
||||||
|
@ -25,16 +25,14 @@ class Parcel implements SyncParcel {
|
|||||||
*
|
*
|
||||||
* @param mixed $value The value to store in the container.
|
* @param mixed $value The value to store in the container.
|
||||||
*/
|
*/
|
||||||
public function __construct($value)
|
public function __construct($value) {
|
||||||
{
|
|
||||||
$this->init($value);
|
$this->init($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
*/
|
*/
|
||||||
private function init($value)
|
private function init($value) {
|
||||||
{
|
|
||||||
$this->mutex = new Mutex();
|
$this->mutex = new Mutex();
|
||||||
$this->storage = new Internal\Storage($value);
|
$this->storage = new Internal\Storage($value);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Amp\Concurrent\Threading;
|
namespace Amp\Concurrent\Threading;
|
||||||
|
|
||||||
use Amp\Concurrent\Sync\Semaphore as SyncSemaphore;
|
use Amp\Concurrent\Sync\Semaphore as SyncSemaphore;
|
||||||
|
use Interop\Async\Awaitable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An asynchronous semaphore based on pthreads' synchronization methods.
|
* An asynchronous semaphore based on pthreads' synchronization methods.
|
||||||
@ -28,8 +29,7 @@ class Semaphore implements SyncSemaphore {
|
|||||||
*
|
*
|
||||||
* @param int $locks The maximum number of locks that can be acquired from the semaphore.
|
* @param int $locks The maximum number of locks that can be acquired from the semaphore.
|
||||||
*/
|
*/
|
||||||
public function __construct(int $locks)
|
public function __construct(int $locks) {
|
||||||
{
|
|
||||||
$this->init($locks);
|
$this->init($locks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,8 +38,7 @@ class Semaphore implements SyncSemaphore {
|
|||||||
*
|
*
|
||||||
* @param int $locks
|
* @param int $locks
|
||||||
*/
|
*/
|
||||||
private function init(int $locks)
|
private function init(int $locks) {
|
||||||
{
|
|
||||||
$locks = (int) $locks;
|
$locks = (int) $locks;
|
||||||
if ($locks < 1) {
|
if ($locks < 1) {
|
||||||
$locks = 1;
|
$locks = 1;
|
||||||
@ -52,32 +51,28 @@ class Semaphore implements SyncSemaphore {
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function count(): int
|
public function count(): int {
|
||||||
{
|
|
||||||
return $this->semaphore->count();
|
return $this->semaphore->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getSize(): int
|
public function getSize(): int {
|
||||||
{
|
|
||||||
return $this->maxLocks;
|
return $this->maxLocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function acquire(): \Generator
|
public function acquire(): Awaitable {
|
||||||
{
|
|
||||||
return $this->semaphore->acquire();
|
return $this->semaphore->acquire();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clones the semaphore, creating a new instance with the same number of locks, all available.
|
* Clones the semaphore, creating a new instance with the same number of locks, all available.
|
||||||
*/
|
*/
|
||||||
public function __clone()
|
public function __clone() {
|
||||||
{
|
|
||||||
$this->init($this->getSize());
|
$this->init($this->getSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,6 @@ class Thread implements Strand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($data instanceof ExitStatus) {
|
if ($data instanceof ExitStatus) {
|
||||||
$this->kill();
|
|
||||||
throw new \Error('Cannot send exit status objects.');
|
throw new \Error('Cannot send exit status objects.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,8 +140,7 @@ class DefaultPool implements Pool {
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function getIdleWorkerCount(): int
|
public function getIdleWorkerCount(): int {
|
||||||
{
|
|
||||||
return $this->idleWorkers->count();
|
return $this->idleWorkers->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use Amp\Concurrent\Process\ChannelledProcess;
|
|||||||
*/
|
*/
|
||||||
class WorkerProcess extends AbstractWorker {
|
class WorkerProcess extends AbstractWorker {
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$dir = \dirname(\dirname(__DIR__)) . '/bin';
|
$dir = \dirname(__DIR__, 2) . '/bin';
|
||||||
parent::__construct(new ChannelledProcess($dir . '/worker.php', $dir));
|
parent::__construct(new ChannelledProcess($dir . '/worker.php', $dir));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,11 @@
|
|||||||
<directory>test</directory>
|
<directory>test</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
|
||||||
<filter>
|
<filter>
|
||||||
<whitelist>
|
<whitelist>
|
||||||
<directory suffix=".php">src</directory>
|
<directory suffix=".php">lib</directory>
|
||||||
</whitelist>
|
</whitelist>
|
||||||
</filter>
|
</filter>
|
||||||
|
|
||||||
<logging>
|
<logging>
|
||||||
<log type="coverage-html" target="build/coverage" title="Amp" highlight="true"/>
|
<log type="coverage-html" target="build/coverage" title="Amp" highlight="true"/>
|
||||||
</logging>
|
</logging>
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent;
|
namespace Amp\Concurrent\Test;
|
||||||
|
|
||||||
use Amp\Concurrent\Sync\Internal\ExitSuccess;
|
use Amp\Concurrent\Sync\Internal\ExitSuccess;
|
||||||
use Amp\Coroutine;
|
|
||||||
use Amp\Loop;
|
|
||||||
|
|
||||||
abstract class AbstractContextTest extends TestCase
|
abstract class AbstractContextTest extends TestCase {
|
||||||
{
|
/**
|
||||||
|
* @param callable $function
|
||||||
|
*
|
||||||
|
* @return \Amp\Concurrent\Context
|
||||||
|
*/
|
||||||
abstract public function createContext(callable $function);
|
abstract public function createContext(callable $function);
|
||||||
|
|
||||||
public function testIsRunning()
|
public function testIsRunning() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
usleep(100);
|
usleep(100);
|
||||||
});
|
});
|
||||||
@ -23,16 +24,13 @@ abstract class AbstractContextTest extends TestCase
|
|||||||
|
|
||||||
$this->assertTrue($context->isRunning());
|
$this->assertTrue($context->isRunning());
|
||||||
|
|
||||||
yield from $context->join();
|
yield $context->join();
|
||||||
|
|
||||||
$this->assertFalse($context->isRunning());
|
$this->assertFalse($context->isRunning());
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testKill()
|
public function testKill() {
|
||||||
{
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
usleep(1e6);
|
usleep(1e6);
|
||||||
});
|
});
|
||||||
@ -45,10 +43,9 @@ abstract class AbstractContextTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Amp\Concurrent\Exception\StatusError
|
* @expectedException \Amp\Concurrent\StatusError
|
||||||
*/
|
*/
|
||||||
public function testStartWhileRunningThrowsError()
|
public function testStartWhileRunningThrowsError() {
|
||||||
{
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
usleep(100);
|
usleep(100);
|
||||||
});
|
});
|
||||||
@ -58,224 +55,185 @@ abstract class AbstractContextTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Amp\Concurrent\Exception\StatusError
|
* @expectedException \Amp\Concurrent\StatusError
|
||||||
*/
|
*/
|
||||||
public function testStartMultipleTimesThrowsError()
|
public function testStartMultipleTimesThrowsError() {
|
||||||
{
|
|
||||||
Loop\loop();
|
|
||||||
|
|
||||||
$this->assertRunTimeGreaterThan(function () {
|
$this->assertRunTimeGreaterThan(function () {
|
||||||
Coroutine\create(function () {
|
\Amp\execute(function () {
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
sleep(1);
|
sleep(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
yield from $context->join();
|
yield $context->join();
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
yield from $context->join();
|
yield $context->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}, 2);
|
}, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Amp\Concurrent\Exception\PanicError
|
* @expectedException \Amp\Concurrent\PanicError
|
||||||
*/
|
*/
|
||||||
public function testExceptionInContextPanics()
|
public function testExceptionInContextPanics() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
throw new \Exception('Exception in fork.');
|
throw new \Exception('Exception in fork.');
|
||||||
});
|
});
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
yield from $context->join();
|
yield $context->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Amp\Concurrent\Exception\PanicError
|
* @expectedException \Amp\Concurrent\PanicError
|
||||||
*/
|
*/
|
||||||
public function testReturnUnserializableDataPanics()
|
public function testReturnUnserializableDataPanics() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
return yield function () {};
|
return yield function () {};
|
||||||
});
|
});
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
yield from $context->join();
|
yield $context->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testJoinWaitsForChild()
|
public function testJoinWaitsForChild() {
|
||||||
{
|
|
||||||
Loop\loop();
|
|
||||||
|
|
||||||
$this->assertRunTimeGreaterThan(function () {
|
$this->assertRunTimeGreaterThan(function () {
|
||||||
Coroutine\create(function () {
|
\Amp\execute(function () {
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
sleep(1);
|
sleep(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
yield from $context->join();
|
yield $context->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}, 1);
|
}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Amp\Concurrent\Exception\StatusError
|
* @expectedException \Amp\Concurrent\StatusError
|
||||||
*/
|
*/
|
||||||
public function testJoinWithoutStartThrowsError()
|
public function testJoinWithoutStartThrowsError() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
usleep(100);
|
usleep(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
yield from $context->join();
|
yield $context->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testJoinResolvesWithContextReturn()
|
public function testJoinResolvesWithContextReturn() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
return 42;
|
return 42;
|
||||||
});
|
});
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
$this->assertSame(42, yield from $context->join());
|
$this->assertSame(42, yield $context->join());
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSendAndReceive()
|
public function testSendAndReceive() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
yield from $this->send(1);
|
yield $this->send(1);
|
||||||
$value = yield from $this->receive();
|
$value = yield $this->receive();
|
||||||
return $value;
|
return $value;
|
||||||
});
|
});
|
||||||
|
|
||||||
$value = 42;
|
$value = 42;
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
$this->assertSame(1, yield from $context->receive());
|
$this->assertSame(1, yield $context->receive());
|
||||||
yield from $context->send($value);
|
yield $context->send($value);
|
||||||
$this->assertSame($value, yield from $context->join());
|
$this->assertSame($value, yield $context->join());
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendAndReceive
|
* @depends testSendAndReceive
|
||||||
* @expectedException \Amp\Concurrent\Exception\SynchronizationError
|
* @expectedException \Amp\Concurrent\SynchronizationError
|
||||||
*/
|
*/
|
||||||
public function testJoinWhenContextSendingData()
|
public function testJoinWhenContextSendingData() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
yield from $this->send(0);
|
yield $this->send(0);
|
||||||
return 42;
|
return 42;
|
||||||
});
|
});
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
$value = yield from $context->join();
|
$value = yield $context->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendAndReceive
|
* @depends testSendAndReceive
|
||||||
* @expectedException \Amp\Concurrent\Exception\StatusError
|
* @expectedException \Amp\Concurrent\StatusError
|
||||||
*/
|
*/
|
||||||
public function testReceiveBeforeContextHasStarted()
|
public function testReceiveBeforeContextHasStarted() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
yield from $this->send(0);
|
yield $this->send(0);
|
||||||
return 42;
|
return 42;
|
||||||
});
|
});
|
||||||
|
|
||||||
$value = yield from $context->receive();
|
$value = yield $context->receive();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendAndReceive
|
* @depends testSendAndReceive
|
||||||
* @expectedException \Amp\Concurrent\Exception\StatusError
|
* @expectedException \Amp\Concurrent\StatusError
|
||||||
*/
|
*/
|
||||||
public function testSendBeforeContextHasStarted()
|
public function testSendBeforeContextHasStarted() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
yield from $this->send(0);
|
yield $this->send(0);
|
||||||
return 42;
|
return 42;
|
||||||
});
|
});
|
||||||
|
|
||||||
yield from $context->send(0);
|
yield $context->send(0);
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendAndReceive
|
* @depends testSendAndReceive
|
||||||
* @expectedException \Amp\Concurrent\Exception\SynchronizationError
|
* @expectedException \Amp\Concurrent\SynchronizationError
|
||||||
*/
|
*/
|
||||||
public function testReceiveWhenContextHasReturned()
|
public function testReceiveWhenContextHasReturned() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
yield from $this->send(0);
|
yield $this->send(0);
|
||||||
return 42;
|
return 42;
|
||||||
});
|
});
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
$value = yield from $context->receive();
|
$value = yield $context->receive();
|
||||||
$value = yield from $context->receive();
|
$value = yield $context->receive();
|
||||||
$value = yield from $context->join();
|
$value = yield $context->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendAndReceive
|
* @depends testSendAndReceive
|
||||||
* @expectedException \Amp\Exception\InvalidArgumentError
|
* @expectedException \Error
|
||||||
*/
|
*/
|
||||||
public function testSendExitStatus()
|
public function testSendExitStatus() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$context = $this->createContext(function () {
|
$context = $this->createContext(function () {
|
||||||
$value = yield from $this->receive();
|
$value = yield $this->receive();
|
||||||
return 42;
|
return 42;
|
||||||
});
|
});
|
||||||
|
|
||||||
$context->start();
|
$context->start();
|
||||||
yield from $context->send(new ExitSuccess(0));
|
yield $context->send(new ExitSuccess(0));
|
||||||
$value = yield from $context->join();
|
$value = yield $context->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Forking;
|
namespace Amp\Concurrent\Test\Forking;
|
||||||
|
|
||||||
use Amp\Concurrent\Forking\Fork;
|
use Amp\Concurrent\Forking\Fork;
|
||||||
use Amp\Coroutine;
|
use Amp\Concurrent\Test\AbstractContextTest;
|
||||||
use Amp\Loop;
|
|
||||||
use Amp\Tests\Concurrent\AbstractContextTest;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group forking
|
* @group forking
|
||||||
* @requires extension pcntl
|
* @requires extension pcntl
|
||||||
*/
|
*/
|
||||||
class ForkTest extends AbstractContextTest
|
class ForkTest extends AbstractContextTest {
|
||||||
{
|
public function createContext(callable $function) {
|
||||||
public function createContext(callable $function)
|
|
||||||
{
|
|
||||||
return new Fork($function);
|
return new Fork($function);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSpawnStartsFork()
|
public function testSpawnStartsFork() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$fork = Fork::spawn(function () {
|
$fork = Fork::spawn(function () {
|
||||||
usleep(100);
|
usleep(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
return yield from $fork->join();
|
return yield $fork->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Stub;
|
namespace Amp\Concurrent\Test\Stub;
|
||||||
|
|
||||||
class CallbackStub
|
class CallbackStub {
|
||||||
{
|
|
||||||
public function __invoke() {}
|
public function __invoke() {}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Sync;
|
namespace Amp\Concurrent\Test\Sync;
|
||||||
|
|
||||||
use Amp\Coroutine;
|
use Amp\Concurrent\Test\TestCase;
|
||||||
use Amp\Loop;
|
|
||||||
use Amp\Tests\Concurrent\TestCase;
|
|
||||||
|
|
||||||
abstract class AbstractParcelTest extends TestCase
|
abstract class AbstractParcelTest extends TestCase {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* @return \Amp\Concurrent\Sync\Parcel
|
* @return \Amp\Concurrent\Sync\Parcel
|
||||||
*/
|
*/
|
||||||
abstract protected function createParcel($value);
|
abstract protected function createParcel($value);
|
||||||
|
|
||||||
public function testConstructor()
|
public function testConstructor() {
|
||||||
{
|
|
||||||
$object = $this->createParcel(new \stdClass());
|
$object = $this->createParcel(new \stdClass());
|
||||||
$this->assertInternalType('object', $object->unwrap());
|
$this->assertInternalType('object', $object->unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUnwrapIsOfCorrectType()
|
public function testUnwrapIsOfCorrectType() {
|
||||||
{
|
|
||||||
$object = $this->createParcel(new \stdClass());
|
$object = $this->createParcel(new \stdClass());
|
||||||
$this->assertInstanceOf('stdClass', $object->unwrap());
|
$this->assertInstanceOf('stdClass', $object->unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUnwrapIsEqual()
|
public function testUnwrapIsEqual() {
|
||||||
{
|
|
||||||
$object = new \stdClass();
|
$object = new \stdClass();
|
||||||
$shared = $this->createParcel($object);
|
$shared = $this->createParcel($object);
|
||||||
$this->assertEquals($object, $shared->unwrap());
|
$this->assertEquals($object, $shared->unwrap());
|
||||||
@ -35,50 +29,46 @@ abstract class AbstractParcelTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @depends testUnwrapIsEqual
|
* @depends testUnwrapIsEqual
|
||||||
*/
|
*/
|
||||||
public function testSynchronized()
|
public function testSynchronized() {
|
||||||
{
|
|
||||||
$parcel = $this->createParcel(0);
|
$parcel = $this->createParcel(0);
|
||||||
|
|
||||||
$coroutine = new Coroutine($parcel->synchronized(function ($value) {
|
$awaitable = $parcel->synchronized(function ($value) {
|
||||||
$this->assertSame(0, $value);
|
$this->assertSame(0, $value);
|
||||||
usleep(1e4);
|
usleep(1e4);
|
||||||
return 1;
|
return 1;
|
||||||
}));
|
});
|
||||||
|
|
||||||
$callback = $this->createCallback(1);
|
$callback = $this->createCallback(1);
|
||||||
$callback->method('__invoke')
|
$callback->method('__invoke')
|
||||||
->with($this->identicalTo(1));
|
->with($this->identicalTo(null), $this->identicalTo(1));
|
||||||
|
|
||||||
$coroutine->done($callback);
|
$awaitable->when($callback);
|
||||||
|
|
||||||
$coroutine = new Coroutine($parcel->synchronized(function ($value) {
|
$awaitable = $parcel->synchronized(function ($value) {
|
||||||
$this->assertSame(1, $value);
|
$this->assertSame(1, $value);
|
||||||
usleep(1e4);
|
usleep(1e4);
|
||||||
return 2;
|
return 2;
|
||||||
}));
|
});
|
||||||
|
|
||||||
$callback = $this->createCallback(1);
|
$callback = $this->createCallback(1);
|
||||||
$callback->method('__invoke')
|
$callback->method('__invoke')
|
||||||
->with($this->identicalTo(2));
|
->with($this->identicalTo(null), $this->identicalTo(2));
|
||||||
|
|
||||||
$coroutine->done($callback);
|
$awaitable->when($callback);
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSynchronized
|
* @depends testSynchronized
|
||||||
*/
|
*/
|
||||||
public function testCloneIsNewParcel()
|
public function testCloneIsNewParcel() {
|
||||||
{
|
|
||||||
$original = $this->createParcel(1);
|
$original = $this->createParcel(1);
|
||||||
|
|
||||||
$clone = clone $original;
|
$clone = clone $original;
|
||||||
|
|
||||||
$coroutine = new Coroutine($clone->synchronized(function () {
|
$awaitable = $clone->synchronized(function () {
|
||||||
return 2;
|
return 2;
|
||||||
}));
|
});
|
||||||
$coroutine->wait();
|
\Amp\wait($awaitable);
|
||||||
|
|
||||||
$this->assertSame(1, $original->unwrap());
|
$this->assertSame(1, $original->unwrap());
|
||||||
$this->assertSame(2, $clone->unwrap());
|
$this->assertSame(2, $clone->unwrap());
|
||||||
|
@ -1,123 +1,109 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Sync;
|
namespace Amp\Concurrent\Test\Sync;
|
||||||
|
|
||||||
use Amp\Concurrent\Sync\Lock;
|
use Amp\Concurrent\Sync\Lock;
|
||||||
use Amp\Coroutine;
|
use Amp\Concurrent\Test\TestCase;
|
||||||
use Amp\Loop;
|
use Amp\Pause;
|
||||||
use Amp\Tests\Concurrent\TestCase;
|
|
||||||
|
|
||||||
abstract class AbstractSemaphoreTest extends TestCase
|
abstract class AbstractSemaphoreTest extends TestCase {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* @var \Amp\Concurrent\Sync\Semaphore
|
* @var \Amp\Concurrent\Sync\Semaphore
|
||||||
*/
|
*/
|
||||||
protected $semaphore;
|
protected $semaphore;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param int $locks Number of locks in the semaphore.
|
||||||
|
*
|
||||||
* @return \Amp\Concurrent\Sync\Semaphore
|
* @return \Amp\Concurrent\Sync\Semaphore
|
||||||
*/
|
*/
|
||||||
abstract public function createSemaphore($locks);
|
abstract public function createSemaphore(int $locks);
|
||||||
|
|
||||||
public function testCount()
|
public function testCount() {
|
||||||
{
|
|
||||||
$this->semaphore = $this->createSemaphore(4);
|
$this->semaphore = $this->createSemaphore(4);
|
||||||
|
|
||||||
$this->assertCount(4, $this->semaphore);
|
$this->assertCount(4, $this->semaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAcquire()
|
public function testAcquire() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$this->semaphore = $this->createSemaphore(1);
|
$this->semaphore = $this->createSemaphore(1);
|
||||||
|
|
||||||
$lock = yield from $this->semaphore->acquire();
|
$lock = yield $this->semaphore->acquire();
|
||||||
|
|
||||||
$this->assertFalse($lock->isReleased());
|
$this->assertFalse($lock->isReleased());
|
||||||
|
|
||||||
$lock->release();
|
$lock->release();
|
||||||
|
|
||||||
$this->assertTrue($lock->isReleased());
|
$this->assertTrue($lock->isReleased());
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAcquireMultiple()
|
public function testAcquireMultiple() {
|
||||||
{
|
|
||||||
$this->assertRunTimeGreaterThan(function () {
|
$this->assertRunTimeGreaterThan(function () {
|
||||||
$this->semaphore = $this->createSemaphore(1);
|
$this->semaphore = $this->createSemaphore(1);
|
||||||
|
|
||||||
Coroutine\create(function () {
|
\Amp\execute(function () {
|
||||||
$lock1 = yield from $this->semaphore->acquire();
|
$lock1 = yield $this->semaphore->acquire();
|
||||||
Loop\timer(0.5, function () use ($lock1) {
|
\Amp\delay(500, function () use ($lock1) {
|
||||||
$lock1->release();
|
$lock1->release();
|
||||||
});
|
});
|
||||||
|
|
||||||
$lock2 = yield from $this->semaphore->acquire();
|
$lock2 = yield $this->semaphore->acquire();
|
||||||
Loop\timer(0.5, function () use ($lock2) {
|
\Amp\delay(500, function () use ($lock2) {
|
||||||
$lock2->release();
|
$lock2->release();
|
||||||
});
|
});
|
||||||
|
|
||||||
$lock3 = yield from $this->semaphore->acquire();
|
$lock3 = yield $this->semaphore->acquire();
|
||||||
Loop\timer(0.5, function () use ($lock3) {
|
\Amp\delay(500, function () use ($lock3) {
|
||||||
$lock3->release();
|
$lock3->release();
|
||||||
});
|
});
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}, 1.5);
|
}, 1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCloneIsNewSemaphore()
|
public function testCloneIsNewSemaphore() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$this->semaphore = $this->createSemaphore(1);
|
$this->semaphore = $this->createSemaphore(1);
|
||||||
$clone = clone $this->semaphore;
|
$clone = clone $this->semaphore;
|
||||||
|
|
||||||
$lock = yield from $clone->acquire();
|
$lock = yield $clone->acquire();
|
||||||
|
|
||||||
$this->assertCount(1, $this->semaphore);
|
$this->assertCount(1, $this->semaphore);
|
||||||
$this->assertCount(0, $clone);
|
$this->assertCount(0, $clone);
|
||||||
|
|
||||||
$lock->release();
|
$lock->release();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSerializedIsSameSemaphore()
|
public function testSerializedIsSameSemaphore() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$this->semaphore = $this->createSemaphore(1);
|
$this->semaphore = $this->createSemaphore(1);
|
||||||
$unserialized = unserialize(serialize($this->semaphore));
|
$unserialized = unserialize(serialize($this->semaphore));
|
||||||
|
|
||||||
$lock = yield from $unserialized->acquire();
|
$lock = yield $unserialized->acquire();
|
||||||
|
|
||||||
$this->assertCount(0, $this->semaphore);
|
$this->assertCount(0, $this->semaphore);
|
||||||
$this->assertCount(0, $unserialized);
|
$this->assertCount(0, $unserialized);
|
||||||
|
|
||||||
$lock->release();
|
$lock->release();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSimultaneousAcquire()
|
public function testSimultaneousAcquire() {
|
||||||
{
|
|
||||||
$this->semaphore = $this->createSemaphore(1);
|
$this->semaphore = $this->createSemaphore(1);
|
||||||
|
|
||||||
$coroutine1 = new Coroutine\Coroutine($this->semaphore->acquire());
|
\Amp\execute(function () {
|
||||||
$coroutine2 = new Coroutine\Coroutine($this->semaphore->acquire());
|
$awaitable1 = $this->semaphore->acquire();
|
||||||
|
$awaitable2 = $this->semaphore->acquire();
|
||||||
$coroutine1->delay(0.5)->then(function (Lock $lock) {
|
|
||||||
$lock->release();
|
yield new Pause(500);
|
||||||
|
|
||||||
|
(yield $awaitable1)->release();
|
||||||
|
|
||||||
|
yield new Pause(500);
|
||||||
|
|
||||||
|
(yield $awaitable2)->release();
|
||||||
});
|
});
|
||||||
|
|
||||||
$coroutine2->delay(0.5)->then(function (Lock $lock) {
|
|
||||||
$lock->release();
|
|
||||||
});
|
|
||||||
|
|
||||||
$this->assertRunTimeGreaterThan('Amp\Loop\run', 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,74 +1,57 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Sync;
|
namespace Amp\Concurrent\Test\Sync;
|
||||||
|
|
||||||
use Amp\Concurrent\Sync\ChannelledStream;
|
use Amp\Concurrent\Sync\ChannelledStream;
|
||||||
use Amp\Coroutine;
|
use Amp\Stream\Stream;
|
||||||
use Amp\Loop;
|
use Amp\Stream\ClosedException;
|
||||||
use Amp\Stream\{DuplexStream, ReadableStream};
|
use Amp\Concurrent\Test\TestCase;
|
||||||
use Amp\Stream\Exception\{UnreadableException, UnwritableException};
|
use Amp\Success;
|
||||||
use Amp\Tests\Concurrent\TestCase;
|
|
||||||
|
|
||||||
class ChannelledStreamTest extends TestCase
|
class ChannelledStreamTest extends TestCase {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* @return \Amp\Stream\DuplexStream|\PHPUnit_Framework_MockObject_MockObject
|
* @return \Amp\Stream\Stream|\PHPUnit_Framework_MockObject_MockObject
|
||||||
*/
|
*/
|
||||||
protected function createMockStream()
|
protected function createMockStream() {
|
||||||
{
|
$mock = $this->createMock(Stream::class);
|
||||||
$mock = $this->getMock(DuplexStream::class);
|
|
||||||
|
|
||||||
$buffer = '';
|
$buffer = '';
|
||||||
|
|
||||||
$mock->method('write')
|
$mock->method('write')
|
||||||
->will($this->returnCallback(function ($data) use (&$buffer) {
|
->will($this->returnCallback(function ($data) use (&$buffer) {
|
||||||
$buffer .= $data;
|
$buffer .= $data;
|
||||||
return yield strlen($data);
|
return new Success(\strlen($data));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
$mock->method('read')
|
$mock->method('read')
|
||||||
->will($this->returnCallback(function ($length, $byte = null, $timeout = 0) use (&$buffer) {
|
->will($this->returnCallback(function ($length, $byte = null, $timeout = 0) use (&$buffer) {
|
||||||
$result = substr($buffer, 0, $length);
|
$result = \substr($buffer, 0, $length);
|
||||||
$buffer = substr($buffer, $length);
|
$buffer = \substr($buffer, $length);
|
||||||
return yield $result;
|
return new Success($result);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return $mock;
|
return $mock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function testSendReceive() {
|
||||||
* @expectedException \Amp\Exception\InvalidArgumentError
|
\Amp\execute(function () {
|
||||||
*/
|
|
||||||
public function testReadableWithoutWritable()
|
|
||||||
{
|
|
||||||
$mock = $this->getMock(ReadableStream::class);
|
|
||||||
|
|
||||||
$channel = new ChannelledStream($mock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSendReceive()
|
|
||||||
{
|
|
||||||
Coroutine\create(function () {
|
|
||||||
$mock = $this->createMockStream();
|
$mock = $this->createMockStream();
|
||||||
$a = new ChannelledStream($mock);
|
$a = new ChannelledStream($mock);
|
||||||
$b = new ChannelledStream($mock);
|
$b = new ChannelledStream($mock);
|
||||||
|
|
||||||
$message = 'hello';
|
$message = 'hello';
|
||||||
|
|
||||||
yield from $a->send($message);
|
yield $a->send($message);
|
||||||
$data = yield from $b->receive();
|
$data = yield $b->receive();
|
||||||
$this->assertSame($message, $data);
|
$this->assertSame($message, $data);
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendReceive
|
* @depends testSendReceive
|
||||||
*/
|
*/
|
||||||
public function testSendReceiveLongData()
|
public function testSendReceiveLongData() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$mock = $this->createMockStream();
|
$mock = $this->createMockStream();
|
||||||
$a = new ChannelledStream($mock);
|
$a = new ChannelledStream($mock);
|
||||||
$b = new ChannelledStream($mock);
|
$b = new ChannelledStream($mock);
|
||||||
@ -79,90 +62,81 @@ class ChannelledStreamTest extends TestCase
|
|||||||
$message .= chr(mt_rand(0, 255));
|
$message .= chr(mt_rand(0, 255));
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from $a->send($message);
|
yield $a->send($message);
|
||||||
$data = yield from $b->receive();
|
$data = yield $b->receive();
|
||||||
$this->assertSame($message, $data);
|
$this->assertSame($message, $data);
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendReceive
|
* @depends testSendReceive
|
||||||
* @expectedException \Amp\Concurrent\Exception\ChannelException
|
* @expectedException \Amp\Concurrent\ChannelException
|
||||||
*/
|
*/
|
||||||
public function testInvalidDataReceived()
|
public function testInvalidDataReceived() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$mock = $this->createMockStream();
|
$mock = $this->createMockStream();
|
||||||
$a = new ChannelledStream($mock);
|
$a = new ChannelledStream($mock);
|
||||||
$b = new ChannelledStream($mock);
|
$b = new ChannelledStream($mock);
|
||||||
|
|
||||||
// Close $a. $b should close on next read...
|
// Close $a. $b should close on next read...
|
||||||
yield from $mock->write(pack('L', 10) . '1234567890');
|
yield $mock->write(pack('L', 10) . '1234567890');
|
||||||
$data = yield from $b->receive();
|
$data = yield $b->receive();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendReceive
|
* @depends testSendReceive
|
||||||
* @expectedException \Amp\Concurrent\Exception\ChannelException
|
* @expectedException \Amp\Concurrent\ChannelException
|
||||||
*/
|
*/
|
||||||
public function testSendUnserializableData()
|
public function testSendUnserializableData() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$mock = $this->createMockStream();
|
$mock = $this->createMockStream();
|
||||||
$a = new ChannelledStream($mock);
|
$a = new ChannelledStream($mock);
|
||||||
$b = new ChannelledStream($mock);
|
$b = new ChannelledStream($mock);
|
||||||
|
|
||||||
// Close $a. $b should close on next read...
|
// Close $a. $b should close on next read...
|
||||||
yield from $a->send(function () {});
|
yield $a->send(function () {});
|
||||||
$data = yield from $b->receive();
|
$data = yield $b->receive();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendReceive
|
* @depends testSendReceive
|
||||||
* @expectedException \Amp\Concurrent\Exception\ChannelException
|
* @expectedException \Amp\Concurrent\ChannelException
|
||||||
*/
|
*/
|
||||||
public function testSendAfterClose()
|
public function testSendAfterClose() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
$mock = $this->createMock(Stream::class);
|
||||||
$mock = $this->getMock(DuplexStream::class);
|
|
||||||
$mock->expects($this->once())
|
$mock->expects($this->once())
|
||||||
->method('write')
|
->method('write')
|
||||||
->will($this->throwException(new UnwritableException()));
|
->will($this->throwException(new ClosedException));
|
||||||
|
|
||||||
$a = new ChannelledStream($mock);
|
$a = new ChannelledStream($mock);
|
||||||
$b = new ChannelledStream($this->getMock(DuplexStream::class));
|
$b = new ChannelledStream($this->createMock(Stream::class));
|
||||||
|
|
||||||
yield from $a->send('hello');
|
yield $a->send('hello');
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testSendReceive
|
* @depends testSendReceive
|
||||||
* @expectedException \Amp\Concurrent\Exception\ChannelException
|
* @expectedException \Amp\Concurrent\ChannelException
|
||||||
*/
|
*/
|
||||||
public function testReceiveAfterClose()
|
public function testReceiveAfterClose() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
$mock = $this->createMock(Stream::class);
|
||||||
$mock = $this->getMock(DuplexStream::class);
|
|
||||||
$mock->expects($this->once())
|
$mock->expects($this->once())
|
||||||
->method('read')
|
->method('read')
|
||||||
->will($this->throwException(new UnreadableException()));
|
->will($this->throwException(new ClosedException));
|
||||||
|
|
||||||
$a = new ChannelledStream($mock);
|
$a = new ChannelledStream($mock);
|
||||||
|
|
||||||
$data = yield from $a->receive();
|
$data = yield $a->receive();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,41 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Sync;
|
namespace Amp\Concurrent\Test\Sync;
|
||||||
|
|
||||||
use Amp\Concurrent\Sync\FileMutex;
|
use Amp\Concurrent\Sync\FileMutex;
|
||||||
use Amp\Coroutine;
|
use Amp\Concurrent\Test\TestCase;
|
||||||
use Amp\Loop;
|
|
||||||
use Amp\Tests\Concurrent\TestCase;
|
|
||||||
|
|
||||||
class FileMutexTest extends TestCase
|
class FileMutexTest extends TestCase {
|
||||||
{
|
public function testAcquire() {
|
||||||
public function testAcquire()
|
\Amp\execute(function () {
|
||||||
{
|
|
||||||
Coroutine\create(function () {
|
|
||||||
$mutex = new FileMutex();
|
$mutex = new FileMutex();
|
||||||
$lock = yield from $mutex->acquire();
|
$lock = yield $mutex->acquire();
|
||||||
$lock->release();
|
$lock->release();
|
||||||
$this->assertTrue($lock->isReleased());
|
$this->assertTrue($lock->isReleased());
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAcquireMultiple()
|
public function testAcquireMultiple() {
|
||||||
{
|
|
||||||
Loop\loop();
|
|
||||||
|
|
||||||
$this->assertRunTimeGreaterThan(function () {
|
$this->assertRunTimeGreaterThan(function () {
|
||||||
Coroutine\create(function () {
|
\Amp\execute(function () {
|
||||||
$mutex = new FileMutex();
|
$mutex = new FileMutex();
|
||||||
|
|
||||||
$lock1 = yield from $mutex->acquire();
|
$lock1 = yield $mutex->acquire();
|
||||||
Loop\timer(0.5, function () use ($lock1) {
|
\Amp\delay(500, function () use ($lock1) {
|
||||||
$lock1->release();
|
$lock1->release();
|
||||||
});
|
});
|
||||||
|
|
||||||
$lock2 = yield from $mutex->acquire();
|
$lock2 = yield $mutex->acquire();
|
||||||
Loop\timer(0.5, function () use ($lock2) {
|
\Amp\delay(500, function () use ($lock2) {
|
||||||
$lock2->release();
|
$lock2->release();
|
||||||
});
|
});
|
||||||
|
|
||||||
$lock3 = yield from $mutex->acquire();
|
$lock3 = yield $mutex->acquire();
|
||||||
Loop\timer(0.5, function () use ($lock3) {
|
\Amp\delay(500, function () use ($lock3) {
|
||||||
$lock3->release();
|
$lock3->release();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}, 1.5);
|
}, 1.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Sync;
|
namespace Amp\Concurrent\Test\Sync;
|
||||||
|
|
||||||
use Amp\Concurrent\Sync\Lock;
|
use Amp\Concurrent\Sync\Lock;
|
||||||
use Amp\Tests\Concurrent\TestCase;
|
use Amp\Concurrent\Test\TestCase;
|
||||||
|
|
||||||
class LockTest extends TestCase
|
class LockTest extends TestCase {
|
||||||
{
|
public function testIsReleased() {
|
||||||
public function testIsReleased()
|
|
||||||
{
|
|
||||||
$lock = new Lock($this->createCallback(1));
|
$lock = new Lock($this->createCallback(1));
|
||||||
$this->assertFalse($lock->isReleased());
|
$this->assertFalse($lock->isReleased());
|
||||||
$lock->release();
|
$lock->release();
|
||||||
$this->assertTrue($lock->isReleased());
|
$this->assertTrue($lock->isReleased());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsReleasedOnDestruct()
|
public function testIsReleasedOnDestruct() {
|
||||||
{
|
|
||||||
$lock = new Lock($this->createCallback(1));
|
$lock = new Lock($this->createCallback(1));
|
||||||
unset($lock);
|
unset($lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Amp\Concurrent\Exception\LockAlreadyReleasedError
|
* @expectedException \Amp\Concurrent\LockAlreadyReleasedError
|
||||||
*/
|
*/
|
||||||
public function testThrowsOnMultiRelease()
|
public function testThrowsOnMultiRelease() {
|
||||||
{
|
|
||||||
$lock = new Lock($this->createCallback(1));
|
$lock = new Lock($this->createCallback(1));
|
||||||
$lock->release();
|
$lock->release();
|
||||||
$lock->release();
|
$lock->release();
|
||||||
|
@ -1,37 +1,36 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Sync;
|
namespace Amp\Concurrent\Test\Sync;
|
||||||
|
|
||||||
use Amp\Concurrent\Forking\Fork;
|
use Amp\Concurrent\Forking\Fork;
|
||||||
use Amp\Concurrent\Sync\{PosixSemaphore, Semaphore};
|
use Amp\Concurrent\Sync\{PosixSemaphore, Semaphore};
|
||||||
use Amp\Coroutine;
|
|
||||||
use Amp\Loop;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group posix
|
* @group posix
|
||||||
* @requires extension sysvmsg
|
* @requires extension sysvmsg
|
||||||
*/
|
*/
|
||||||
class PosixSemaphoreTest extends AbstractSemaphoreTest
|
class PosixSemaphoreTest extends AbstractSemaphoreTest {
|
||||||
{
|
/**
|
||||||
public function createSemaphore($locks)
|
* @param $locks
|
||||||
{
|
*
|
||||||
|
* @return \Amp\Concurrent\Sync\PosixSemaphore
|
||||||
|
*/
|
||||||
|
public function createSemaphore(int $locks) {
|
||||||
return new PosixSemaphore($locks);
|
return new PosixSemaphore($locks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown()
|
public function tearDown() {
|
||||||
{
|
|
||||||
if ($this->semaphore && !$this->semaphore->isFreed()) {
|
if ($this->semaphore && !$this->semaphore->isFreed()) {
|
||||||
$this->semaphore->free();
|
$this->semaphore->free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCloneIsNewSemaphore()
|
public function testCloneIsNewSemaphore() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$this->semaphore = $this->createSemaphore(1);
|
$this->semaphore = $this->createSemaphore(1);
|
||||||
$clone = clone $this->semaphore;
|
$clone = clone $this->semaphore;
|
||||||
|
|
||||||
$lock = yield from $clone->acquire();
|
$lock = yield $clone->acquire();
|
||||||
|
|
||||||
$this->assertCount(1, $this->semaphore);
|
$this->assertCount(1, $this->semaphore);
|
||||||
$this->assertCount(0, $clone);
|
$this->assertCount(0, $clone);
|
||||||
@ -39,13 +38,11 @@ class PosixSemaphoreTest extends AbstractSemaphoreTest
|
|||||||
$lock->release();
|
$lock->release();
|
||||||
|
|
||||||
$clone->free();
|
$clone->free();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFree()
|
public function testFree() {
|
||||||
{
|
|
||||||
$this->semaphore = $this->createSemaphore(1);
|
$this->semaphore = $this->createSemaphore(1);
|
||||||
|
|
||||||
$this->assertFalse($this->semaphore->isFreed());
|
$this->assertFalse($this->semaphore->isFreed());
|
||||||
@ -58,29 +55,28 @@ class PosixSemaphoreTest extends AbstractSemaphoreTest
|
|||||||
/**
|
/**
|
||||||
* @requires extension pcntl
|
* @requires extension pcntl
|
||||||
*/
|
*/
|
||||||
public function testAcquireInMultipleForks()
|
public function testAcquireInMultipleForks() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$this->semaphore = $this->createSemaphore(1);
|
$this->semaphore = $this->createSemaphore(1);
|
||||||
|
|
||||||
$fork1 = new Fork(function (Semaphore $semaphore) {
|
$fork1 = new Fork(function (Semaphore $semaphore) {
|
||||||
$lock = yield from $semaphore->acquire();
|
$lock = yield $semaphore->acquire();
|
||||||
|
|
||||||
usleep(1e5);
|
usleep(1e5);
|
||||||
|
|
||||||
$lock->release();
|
$lock->release();
|
||||||
|
|
||||||
yield 0;
|
return 0;
|
||||||
}, $this->semaphore);
|
}, $this->semaphore);
|
||||||
|
|
||||||
$fork2 = new Fork(function (Semaphore $semaphore) {
|
$fork2 = new Fork(function (Semaphore $semaphore) {
|
||||||
$lock = yield from $semaphore->acquire();
|
$lock = yield $semaphore->acquire();
|
||||||
|
|
||||||
usleep(1e5);
|
usleep(1e5);
|
||||||
|
|
||||||
$lock->release();
|
$lock->release();
|
||||||
|
|
||||||
yield 1;
|
return 1;
|
||||||
}, $this->semaphore);
|
}, $this->semaphore);
|
||||||
|
|
||||||
$start = microtime(true);
|
$start = microtime(true);
|
||||||
@ -88,12 +84,10 @@ class PosixSemaphoreTest extends AbstractSemaphoreTest
|
|||||||
$fork1->start();
|
$fork1->start();
|
||||||
$fork2->start();
|
$fork2->start();
|
||||||
|
|
||||||
yield from $fork1->join();
|
yield $fork1->join();
|
||||||
yield from $fork2->join();
|
yield $fork2->join();
|
||||||
|
|
||||||
$this->assertGreaterThan(1, microtime(true) - $start);
|
$this->assertGreaterThan(0.1, microtime(true) - $start);
|
||||||
});
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,49 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Sync;
|
namespace Amp\Concurrent\Test\Sync;
|
||||||
|
|
||||||
use Amp\Concurrent\Sync\SharedMemoryParcel;
|
use Amp\Concurrent\Sync\SharedMemoryParcel;
|
||||||
use Amp\Coroutine;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @requires extension shmop
|
* @requires extension shmop
|
||||||
* @requires extension sysvmsg
|
* @requires extension sysvmsg
|
||||||
*/
|
*/
|
||||||
class SharedMemoryParcelTest extends AbstractParcelTest
|
class SharedMemoryParcelTest extends AbstractParcelTest {
|
||||||
{
|
|
||||||
private $parcel;
|
private $parcel;
|
||||||
|
|
||||||
protected function createParcel($value)
|
protected function createParcel($value) {
|
||||||
{
|
|
||||||
$this->parcel = new SharedMemoryParcel($value);
|
$this->parcel = new SharedMemoryParcel($value);
|
||||||
return $this->parcel;
|
return $this->parcel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown()
|
public function tearDown() {
|
||||||
{
|
|
||||||
if ($this->parcel !== null) {
|
if ($this->parcel !== null) {
|
||||||
$this->parcel->free();
|
$this->parcel->free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNewObjectIsNotFreed()
|
public function testNewObjectIsNotFreed() {
|
||||||
{
|
|
||||||
$object = new SharedMemoryParcel(new \stdClass());
|
$object = new SharedMemoryParcel(new \stdClass());
|
||||||
$this->assertFalse($object->isFreed());
|
$this->assertFalse($object->isFreed());
|
||||||
$object->free();
|
$object->free();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFreeReleasesObject()
|
public function testFreeReleasesObject() {
|
||||||
{
|
|
||||||
$object = new SharedMemoryParcel(new \stdClass());
|
$object = new SharedMemoryParcel(new \stdClass());
|
||||||
$object->free();
|
$object->free();
|
||||||
$this->assertTrue($object->isFreed());
|
$this->assertTrue($object->isFreed());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Amp\Concurrent\Exception\SharedMemoryException
|
* @expectedException \Amp\Concurrent\SharedMemoryException
|
||||||
*/
|
*/
|
||||||
public function testUnwrapThrowsErrorIfFreed()
|
public function testUnwrapThrowsErrorIfFreed() {
|
||||||
{
|
|
||||||
$object = new SharedMemoryParcel(new \stdClass());
|
$object = new SharedMemoryParcel(new \stdClass());
|
||||||
$object->free();
|
$object->free();
|
||||||
$object->unwrap();
|
$object->unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCloneIsNewObject()
|
public function testCloneIsNewObject() {
|
||||||
{
|
|
||||||
$object = new \stdClass();
|
$object = new \stdClass();
|
||||||
$shared = new SharedMemoryParcel($object);
|
$shared = new SharedMemoryParcel($object);
|
||||||
$clone = clone $shared;
|
$clone = clone $shared;
|
||||||
@ -64,13 +56,12 @@ class SharedMemoryParcelTest extends AbstractParcelTest
|
|||||||
$shared->free();
|
$shared->free();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testObjectOverflowMoved()
|
public function testObjectOverflowMoved() {
|
||||||
{
|
|
||||||
$object = new SharedMemoryParcel('hi', 14);
|
$object = new SharedMemoryParcel('hi', 14);
|
||||||
$coroutine = new Coroutine($object->synchronized(function () {
|
$awaitable = $object->synchronized(function () {
|
||||||
return 'hello world';
|
return 'hello world';
|
||||||
}));
|
});
|
||||||
$coroutine->wait();
|
\Amp\wait($awaitable);
|
||||||
|
|
||||||
$this->assertEquals('hello world', $object->unwrap());
|
$this->assertEquals('hello world', $object->unwrap());
|
||||||
$object->free();
|
$object->free();
|
||||||
@ -80,15 +71,14 @@ class SharedMemoryParcelTest extends AbstractParcelTest
|
|||||||
* @group posix
|
* @group posix
|
||||||
* @requires extension pcntl
|
* @requires extension pcntl
|
||||||
*/
|
*/
|
||||||
public function testSetInSeparateProcess()
|
public function testSetInSeparateProcess() {
|
||||||
{
|
|
||||||
$object = new SharedMemoryParcel(42);
|
$object = new SharedMemoryParcel(42);
|
||||||
|
|
||||||
$this->doInFork(function () use ($object) {
|
$this->doInFork(function () use ($object) {
|
||||||
$coroutine = new Coroutine($object->synchronized(function () {
|
$awaitable = $object->synchronized(function () {
|
||||||
return 43;
|
return 43;
|
||||||
}));
|
});
|
||||||
$coroutine->wait();
|
\Amp\wait($awaitable);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->assertEquals(43, $object->unwrap());
|
$this->assertEquals(43, $object->unwrap());
|
||||||
@ -99,8 +89,7 @@ class SharedMemoryParcelTest extends AbstractParcelTest
|
|||||||
* @group posix
|
* @group posix
|
||||||
* @requires extension pcntl
|
* @requires extension pcntl
|
||||||
*/
|
*/
|
||||||
public function testFreeInSeparateProcess()
|
public function testFreeInSeparateProcess() {
|
||||||
{
|
|
||||||
$object = new SharedMemoryParcel(42);
|
$object = new SharedMemoryParcel(42);
|
||||||
|
|
||||||
$this->doInFork(function () use ($object) {
|
$this->doInFork(function () use ($object) {
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent;
|
namespace Amp\Concurrent\Test;
|
||||||
|
|
||||||
use Amp\Tests\Concurrent\Stub\CallbackStub;
|
use Amp\Concurrent\Test\Stub\CallbackStub;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract test class with methods for creating callbacks and asserting runtimes.
|
* Abstract test class with methods for creating callbacks and asserting runtimes.
|
||||||
*/
|
*/
|
||||||
abstract class TestCase extends \PHPUnit_Framework_TestCase
|
abstract class TestCase extends \PHPUnit_Framework_TestCase {
|
||||||
{
|
|
||||||
const RUNTIME_PRECISION = 2; // Number of decimals to use in runtime calculations/comparisons.
|
const RUNTIME_PRECISION = 2; // Number of decimals to use in runtime calculations/comparisons.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,9 +17,8 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
*
|
*
|
||||||
* @return callable Object that is callable and expects to be called the given number of times.
|
* @return callable Object that is callable and expects to be called the given number of times.
|
||||||
*/
|
*/
|
||||||
public function createCallback($count)
|
public function createCallback($count) {
|
||||||
{
|
$mock = $this->createMock(CallbackStub::class);
|
||||||
$mock = $this->getMock(CallbackStub::class);
|
|
||||||
|
|
||||||
$mock->expects($this->exactly($count))
|
$mock->expects($this->exactly($count))
|
||||||
->method('__invoke');
|
->method('__invoke');
|
||||||
@ -35,8 +33,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
* @param float $maxRunTime
|
* @param float $maxRunTime
|
||||||
* @param mixed[]|null $args Function arguments.
|
* @param mixed[]|null $args Function arguments.
|
||||||
*/
|
*/
|
||||||
public function assertRunTimeLessThan(callable $callback, $maxRunTime, array $args = null)
|
public function assertRunTimeLessThan(callable $callback, $maxRunTime, array $args = null) {
|
||||||
{
|
|
||||||
$this->assertRunTimeBetween($callback, 0, $maxRunTime, $args);
|
$this->assertRunTimeBetween($callback, 0, $maxRunTime, $args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,8 +44,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
* @param float $minRunTime
|
* @param float $minRunTime
|
||||||
* @param mixed[]|null $args Function arguments.
|
* @param mixed[]|null $args Function arguments.
|
||||||
*/
|
*/
|
||||||
public function assertRunTimeGreaterThan(callable $callback, $minRunTime, array $args = null)
|
public function assertRunTimeGreaterThan(callable $callback, $minRunTime, array $args = null) {
|
||||||
{
|
|
||||||
$this->assertRunTimeBetween($callback, $minRunTime, 0, $args);
|
$this->assertRunTimeBetween($callback, $minRunTime, 0, $args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,19 +57,18 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
* @param float $maxRunTime
|
* @param float $maxRunTime
|
||||||
* @param mixed[]|null $args Function arguments.
|
* @param mixed[]|null $args Function arguments.
|
||||||
*/
|
*/
|
||||||
public function assertRunTimeBetween(callable $callback, $minRunTime, $maxRunTime, array $args = null)
|
public function assertRunTimeBetween(callable $callback, $minRunTime, $maxRunTime, array $args = null) {
|
||||||
{
|
|
||||||
$start = microtime(true);
|
$start = microtime(true);
|
||||||
|
|
||||||
call_user_func_array($callback, $args ?: []);
|
\call_user_func_array($callback, $args ?: []);
|
||||||
|
|
||||||
$runTime = round(microtime(true) - $start, self::RUNTIME_PRECISION);
|
$runTime = \round(\microtime(true) - $start, self::RUNTIME_PRECISION);
|
||||||
|
|
||||||
if (0 < $maxRunTime) {
|
if (0 < $maxRunTime) {
|
||||||
$this->assertLessThanOrEqual(
|
$this->assertLessThanOrEqual(
|
||||||
$maxRunTime,
|
$maxRunTime,
|
||||||
$runTime,
|
$runTime,
|
||||||
sprintf('The run time of %.2fs was greater than the max run time of %.2fs.', $runTime, $maxRunTime)
|
\sprintf('The run time of %.2fs was greater than the max run time of %.2fs.', $runTime, $maxRunTime)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,14 +76,13 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertGreaterThanOrEqual(
|
$this->assertGreaterThanOrEqual(
|
||||||
$minRunTime,
|
$minRunTime,
|
||||||
$runTime,
|
$runTime,
|
||||||
sprintf('The run time of %.2fs was less than the min run time of %.2fs.', $runTime, $minRunTime)
|
\sprintf('The run time of %.2fs was less than the min run time of %.2fs.', $runTime, $minRunTime)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function doInFork(callable $function)
|
final protected function doInFork(callable $function) {
|
||||||
{
|
switch ($pid = \pcntl_fork()) {
|
||||||
switch ($pid = pcntl_fork()) {
|
|
||||||
case -1:
|
case -1:
|
||||||
$this->fail('Failed to fork process.');
|
$this->fail('Failed to fork process.');
|
||||||
break;
|
break;
|
||||||
@ -96,7 +90,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
|
|||||||
$status = (int) $function();
|
$status = (int) $function();
|
||||||
exit($status);
|
exit($status);
|
||||||
default:
|
default:
|
||||||
if (pcntl_waitpid($pid, $status) === -1) {
|
if (\pcntl_waitpid($pid, $status) === -1) {
|
||||||
$this->fail('Failed to fork process.');
|
$this->fail('Failed to fork process.');
|
||||||
}
|
}
|
||||||
return $status;
|
return $status;
|
||||||
|
@ -1,55 +1,45 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Threading;
|
namespace Amp\Concurrent\Test\Threading;
|
||||||
|
|
||||||
use Amp\Concurrent\Threading\Mutex;
|
use Amp\Concurrent\Threading\Mutex;
|
||||||
use Amp\Coroutine;
|
use Amp\Concurrent\Test\TestCase;
|
||||||
use Amp\Loop;
|
|
||||||
use Amp\Tests\Concurrent\TestCase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group threading
|
* @group threading
|
||||||
* @requires extension pthreads
|
* @requires extension pthreads
|
||||||
*/
|
*/
|
||||||
class MutexTest extends TestCase
|
class MutexTest extends TestCase {
|
||||||
{
|
public function testAcquire() {
|
||||||
public function testAcquire()
|
\Amp\execute(function () {
|
||||||
{
|
|
||||||
Coroutine\create(function () {
|
|
||||||
$mutex = new Mutex();
|
$mutex = new Mutex();
|
||||||
$lock = yield from $mutex->acquire();
|
$lock = yield $mutex->acquire();
|
||||||
$lock->release();
|
$lock->release();
|
||||||
$this->assertTrue($lock->isReleased());
|
$this->assertTrue($lock->isReleased());
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAcquireMultiple()
|
public function testAcquireMultiple() {
|
||||||
{
|
|
||||||
Loop\loop();
|
|
||||||
|
|
||||||
$this->assertRunTimeGreaterThan(function () {
|
$this->assertRunTimeGreaterThan(function () {
|
||||||
Coroutine\create(function () {
|
\Amp\execute(function () {
|
||||||
$mutex = new Mutex();
|
$mutex = new Mutex();
|
||||||
|
|
||||||
$lock1 = yield from $mutex->acquire();
|
$lock1 = yield $mutex->acquire();
|
||||||
Loop\timer(0.5, function () use ($lock1) {
|
\Amp\delay(500, function () use ($lock1) {
|
||||||
$lock1->release();
|
$lock1->release();
|
||||||
});
|
});
|
||||||
|
|
||||||
$lock2 = yield from $mutex->acquire();
|
$lock2 = yield $mutex->acquire();
|
||||||
Loop\timer(0.5, function () use ($lock2) {
|
\Amp\delay(500, function () use ($lock2) {
|
||||||
$lock2->release();
|
$lock2->release();
|
||||||
});
|
});
|
||||||
|
|
||||||
$lock3 = yield from $mutex->acquire();
|
$lock3 = yield $mutex->acquire();
|
||||||
Loop\timer(0.5, function () use ($lock3) {
|
\Amp\delay(500, function () use ($lock3) {
|
||||||
$lock3->release();
|
$lock3->release();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}, 1.5);
|
}, 1.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Threading;
|
namespace Amp\Concurrent\Test\Threading;
|
||||||
|
|
||||||
use Amp\Concurrent\Threading\Parcel;
|
use Amp\Concurrent\Threading\Parcel;
|
||||||
use Amp\Tests\Concurrent\Sync\AbstractParcelTest;
|
use Amp\Concurrent\Test\Sync\AbstractParcelTest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @requires extension pthreads
|
* @requires extension pthreads
|
||||||
*/
|
*/
|
||||||
class ParcelTest extends AbstractParcelTest
|
class ParcelTest extends AbstractParcelTest {
|
||||||
{
|
protected function createParcel($value) {
|
||||||
protected function createParcel($value)
|
|
||||||
{
|
|
||||||
return new Parcel($value);
|
return new Parcel($value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,26 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Threading;
|
namespace Amp\Concurrent\Test\Threading;
|
||||||
|
|
||||||
use Amp\Concurrent\Sync\Semaphore as SyncSemaphore;
|
use Amp\Concurrent\Sync\Semaphore as SyncSemaphore;
|
||||||
use Amp\Concurrent\Threading\{Semaphore, Thread};
|
use Amp\Concurrent\Threading\{Semaphore, Thread};
|
||||||
use Amp\Coroutine;
|
use Amp\Concurrent\Test\Sync\AbstractSemaphoreTest;
|
||||||
use Amp\Loop;
|
|
||||||
use Amp\Tests\Concurrent\Sync\AbstractSemaphoreTest;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group threading
|
* @group threading
|
||||||
* @requires extension pthreads
|
* @requires extension pthreads
|
||||||
*/
|
*/
|
||||||
class SemaphoreTest extends AbstractSemaphoreTest
|
class SemaphoreTest extends AbstractSemaphoreTest {
|
||||||
{
|
public function createSemaphore(int $locks) {
|
||||||
public function createSemaphore($locks)
|
|
||||||
{
|
|
||||||
return new Semaphore($locks);
|
return new Semaphore($locks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAcquireInMultipleThreads()
|
public function testAcquireInMultipleThreads() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$this->semaphore = $this->createSemaphore(1);
|
$this->semaphore = $this->createSemaphore(1);
|
||||||
|
|
||||||
$thread1 = new Thread(function (SyncSemaphore $semaphore) {
|
$thread1 = new Thread(function (SyncSemaphore $semaphore) {
|
||||||
$lock = yield from $semaphore->acquire();
|
$lock = yield $semaphore->acquire();
|
||||||
|
|
||||||
usleep(1e5);
|
usleep(1e5);
|
||||||
|
|
||||||
@ -35,7 +30,7 @@ class SemaphoreTest extends AbstractSemaphoreTest
|
|||||||
}, $this->semaphore);
|
}, $this->semaphore);
|
||||||
|
|
||||||
$thread2 = new Thread(function (SyncSemaphore $semaphore) {
|
$thread2 = new Thread(function (SyncSemaphore $semaphore) {
|
||||||
$lock = yield from $semaphore->acquire();
|
$lock = yield $semaphore->acquire();
|
||||||
|
|
||||||
usleep(1e5);
|
usleep(1e5);
|
||||||
|
|
||||||
@ -49,12 +44,10 @@ class SemaphoreTest extends AbstractSemaphoreTest
|
|||||||
$thread1->start();
|
$thread1->start();
|
||||||
$thread2->start();
|
$thread2->start();
|
||||||
|
|
||||||
yield from $thread1->join();
|
yield $thread1->join();
|
||||||
yield from $thread2->join();
|
yield $thread2->join();
|
||||||
|
|
||||||
$this->assertGreaterThan(1, microtime(true) - $start);
|
$this->assertGreaterThan(0.1, microtime(true) - $start);
|
||||||
});
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Threading;
|
namespace Amp\Concurrent\Test\Threading;
|
||||||
|
|
||||||
use Amp\Concurrent\Threading\Thread;
|
use Amp\Concurrent\Threading\Thread;
|
||||||
use Amp\Coroutine;
|
use Amp\Concurrent\Test\AbstractContextTest;
|
||||||
use Amp\Loop;
|
|
||||||
use Amp\Tests\Concurrent\AbstractContextTest;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group threading
|
* @group threading
|
||||||
* @requires extension pthreads
|
* @requires extension pthreads
|
||||||
*/
|
*/
|
||||||
class ThreadTest extends AbstractContextTest
|
class ThreadTest extends AbstractContextTest {
|
||||||
{
|
public function createContext(callable $function) {
|
||||||
public function createContext(callable $function)
|
|
||||||
{
|
|
||||||
return new Thread($function);
|
return new Thread($function);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSpawnStartsThread()
|
public function testSpawnStartsThread() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$thread = Thread::spawn(function () {
|
$thread = Thread::spawn(function () {
|
||||||
usleep(100);
|
usleep(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
return yield from $thread->join();
|
return yield $thread->join();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Awaitable;
|
use Amp\Concurrent\Test\TestCase;
|
||||||
use Amp\Coroutine;
|
|
||||||
use Amp\Loop;
|
|
||||||
use Amp\Tests\Concurrent\TestCase;
|
|
||||||
|
|
||||||
abstract class AbstractPoolTest extends TestCase
|
abstract class AbstractPoolTest extends TestCase {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* @param int $min
|
* @param int $min
|
||||||
* @param int $max
|
* @param int $max
|
||||||
@ -17,105 +13,96 @@ abstract class AbstractPoolTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
abstract protected function createPool($min = null, $max = null);
|
abstract protected function createPool($min = null, $max = null);
|
||||||
|
|
||||||
public function testIsRunning()
|
public function testIsRunning() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\run(function () {
|
|
||||||
$pool = $this->createPool();
|
$pool = $this->createPool();
|
||||||
$this->assertFalse($pool->isRunning());
|
$this->assertFalse($pool->isRunning());
|
||||||
|
|
||||||
$pool->start();
|
$pool->start();
|
||||||
$this->assertTrue($pool->isRunning());
|
$this->assertTrue($pool->isRunning());
|
||||||
|
|
||||||
yield from $pool->shutdown();
|
yield $pool->shutdown();
|
||||||
$this->assertFalse($pool->isRunning());
|
$this->assertFalse($pool->isRunning());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsIdleOnStart()
|
public function testIsIdleOnStart() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\run(function () {
|
|
||||||
$pool = $this->createPool();
|
$pool = $this->createPool();
|
||||||
$pool->start();
|
$pool->start();
|
||||||
|
|
||||||
$this->assertTrue($pool->isIdle());
|
$this->assertTrue($pool->isIdle());
|
||||||
|
|
||||||
yield from $pool->shutdown();
|
yield $pool->shutdown();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetMinSize()
|
public function testGetMinSize() {
|
||||||
{
|
|
||||||
$pool = $this->createPool(7, 24);
|
$pool = $this->createPool(7, 24);
|
||||||
$this->assertEquals(7, $pool->getMinSize());
|
$this->assertEquals(7, $pool->getMinSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetMaxSize()
|
public function testGetMaxSize() {
|
||||||
{
|
|
||||||
$pool = $this->createPool(3, 17);
|
$pool = $this->createPool(3, 17);
|
||||||
$this->assertEquals(17, $pool->getMaxSize());
|
$this->assertEquals(17, $pool->getMaxSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMinWorkersSpawnedOnStart()
|
public function testMinWorkersSpawnedOnStart() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\run(function () {
|
|
||||||
$pool = $this->createPool(8, 32);
|
$pool = $this->createPool(8, 32);
|
||||||
$pool->start();
|
$pool->start();
|
||||||
|
|
||||||
$this->assertEquals(8, $pool->getWorkerCount());
|
$this->assertEquals(8, $pool->getWorkerCount());
|
||||||
|
|
||||||
yield from $pool->shutdown();
|
yield $pool->shutdown();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWorkersIdleOnStart()
|
public function testWorkersIdleOnStart() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\run(function () {
|
|
||||||
$pool = $this->createPool(8, 32);
|
$pool = $this->createPool(8, 32);
|
||||||
$pool->start();
|
$pool->start();
|
||||||
|
|
||||||
$this->assertEquals(8, $pool->getIdleWorkerCount());
|
$this->assertEquals(8, $pool->getIdleWorkerCount());
|
||||||
|
|
||||||
yield from $pool->shutdown();
|
yield $pool->shutdown();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEnqueue()
|
public function testEnqueue() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\run(function () {
|
|
||||||
$pool = $this->createPool();
|
$pool = $this->createPool();
|
||||||
$pool->start();
|
$pool->start();
|
||||||
|
|
||||||
$returnValue = yield from $pool->enqueue(new TestTask(42));
|
$returnValue = yield $pool->enqueue(new TestTask(42));
|
||||||
$this->assertEquals(42, $returnValue);
|
$this->assertEquals(42, $returnValue);
|
||||||
|
|
||||||
yield from $pool->shutdown();
|
yield $pool->shutdown();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEnqueueMultiple()
|
public function testEnqueueMultiple() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\run(function () {
|
|
||||||
$pool = $this->createPool();
|
$pool = $this->createPool();
|
||||||
$pool->start();
|
$pool->start();
|
||||||
|
|
||||||
$values = yield Awaitable\all([
|
$values = yield \Amp\all([
|
||||||
new Coroutine\Coroutine($pool->enqueue(new TestTask(42))),
|
$pool->enqueue(new TestTask(42)),
|
||||||
new Coroutine\Coroutine($pool->enqueue(new TestTask(56))),
|
$pool->enqueue(new TestTask(56)),
|
||||||
new Coroutine\Coroutine($pool->enqueue(new TestTask(72)))
|
$pool->enqueue(new TestTask(72))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals([42, 56, 72], $values);
|
$this->assertEquals([42, 56, 72], $values);
|
||||||
|
|
||||||
yield from $pool->shutdown();
|
yield $pool->shutdown();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testKill()
|
public function testKill() {
|
||||||
{
|
|
||||||
$pool = $this->createPool();
|
$pool = $this->createPool();
|
||||||
$pool->start();
|
$pool->start();
|
||||||
|
|
||||||
$this->assertRunTimeLessThan([$pool, 'kill'], 0.5);
|
$this->assertRunTimeLessThan([$pool, 'kill'], 1);
|
||||||
$this->assertFalse($pool->isRunning());
|
$this->assertFalse($pool->isRunning());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,102 +1,87 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Awaitable;
|
use Amp\Concurrent\Test\TestCase;
|
||||||
use Amp\Coroutine;
|
|
||||||
use Amp\Loop;
|
|
||||||
use Amp\Tests\Concurrent\TestCase;
|
|
||||||
|
|
||||||
abstract class AbstractWorkerTest extends TestCase
|
abstract class AbstractWorkerTest extends TestCase {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* @return \Amp\Concurrent\Worker\Worker
|
* @return \Amp\Concurrent\Worker\Worker
|
||||||
*/
|
*/
|
||||||
abstract protected function createWorker();
|
abstract protected function createWorker();
|
||||||
|
|
||||||
public function testIsRunning()
|
public function testIsRunning() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$worker = $this->createWorker();
|
$worker = $this->createWorker();
|
||||||
$this->assertFalse($worker->isRunning());
|
$this->assertFalse($worker->isRunning());
|
||||||
|
|
||||||
$worker->start();
|
$worker->start();
|
||||||
$this->assertTrue($worker->isRunning());
|
$this->assertTrue($worker->isRunning());
|
||||||
|
|
||||||
yield from $worker->shutdown();
|
yield $worker->shutdown();
|
||||||
$this->assertFalse($worker->isRunning());
|
$this->assertFalse($worker->isRunning());
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsIdleOnStart()
|
public function testIsIdleOnStart() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$worker = $this->createWorker();
|
$worker = $this->createWorker();
|
||||||
$worker->start();
|
$worker->start();
|
||||||
|
|
||||||
$this->assertTrue($worker->isIdle());
|
$this->assertTrue($worker->isIdle());
|
||||||
|
|
||||||
yield from $worker->shutdown();
|
yield $worker->shutdown();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEnqueue()
|
public function testEnqueue() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$worker = $this->createWorker();
|
$worker = $this->createWorker();
|
||||||
$worker->start();
|
$worker->start();
|
||||||
|
|
||||||
$returnValue = yield from $worker->enqueue(new TestTask(42));
|
$returnValue = yield $worker->enqueue(new TestTask(42));
|
||||||
$this->assertEquals(42, $returnValue);
|
$this->assertEquals(42, $returnValue);
|
||||||
|
|
||||||
yield from $worker->shutdown();
|
yield $worker->shutdown();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEnqueueMultiple()
|
public function testEnqueueMultiple() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$worker = $this->createWorker();
|
$worker = $this->createWorker();
|
||||||
$worker->start();
|
$worker->start();
|
||||||
|
|
||||||
$values = yield Awaitable\all([
|
$values = yield \Amp\all([
|
||||||
new Coroutine\Coroutine($worker->enqueue(new TestTask(42))),
|
$worker->enqueue(new TestTask(42)),
|
||||||
new Coroutine\Coroutine($worker->enqueue(new TestTask(56))),
|
$worker->enqueue(new TestTask(56)),
|
||||||
new Coroutine\Coroutine($worker->enqueue(new TestTask(72)))
|
$worker->enqueue(new TestTask(72))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals([42, 56, 72], $values);
|
$this->assertEquals([42, 56, 72], $values);
|
||||||
|
|
||||||
yield from $worker->shutdown();
|
yield $worker->shutdown();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNotIdleOnEnqueue()
|
public function testNotIdleOnEnqueue() {
|
||||||
{
|
\Amp\execute(function () {
|
||||||
Coroutine\create(function () {
|
|
||||||
$worker = $this->createWorker();
|
$worker = $this->createWorker();
|
||||||
$worker->start();
|
$worker->start();
|
||||||
|
|
||||||
$coroutine = new Coroutine\Coroutine($worker->enqueue(new TestTask(42)));
|
$coroutine = $worker->enqueue(new TestTask(42));
|
||||||
$this->assertFalse($worker->isIdle());
|
$this->assertFalse($worker->isIdle());
|
||||||
yield $coroutine;
|
yield $coroutine;
|
||||||
|
|
||||||
yield from $worker->shutdown();
|
yield $worker->shutdown();
|
||||||
})->done();
|
});
|
||||||
|
|
||||||
Loop\run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testKill()
|
public function testKill() {
|
||||||
{
|
|
||||||
$worker = $this->createWorker();
|
$worker = $this->createWorker();
|
||||||
$worker->start();
|
$worker->start();
|
||||||
|
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Concurrent\Worker\{DefaultPool, WorkerFactory, WorkerFork};
|
use Amp\Concurrent\Worker\{ DefaultPool, WorkerFactory, WorkerFork };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group forking
|
* @group forking
|
||||||
* @requires extension pcntl
|
* @requires extension pcntl
|
||||||
*/
|
*/
|
||||||
class ForkPoolTest extends AbstractPoolTest
|
class ForkPoolTest extends AbstractPoolTest {
|
||||||
{
|
protected function createPool($min = null, $max = null) {
|
||||||
protected function createPool($min = null, $max = null)
|
$factory = $this->createMock(WorkerFactory::class);
|
||||||
{
|
|
||||||
$factory = $this->getMock(WorkerFactory::class);
|
|
||||||
$factory->method('create')->will($this->returnCallback(function () {
|
$factory->method('create')->will($this->returnCallback(function () {
|
||||||
return new WorkerFork();
|
return new WorkerFork();
|
||||||
}));
|
}));
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Concurrent\Worker;
|
use Amp\Concurrent\Worker;
|
||||||
use Amp\Concurrent\Worker\{Environment, Pool, Task, WorkerFactory};
|
use Amp\Concurrent\Worker\{ Environment, Pool, Task, WorkerFactory };
|
||||||
use Amp\Coroutine;
|
use Amp\Concurrent\Test\TestCase;
|
||||||
use Amp\Tests\Concurrent\TestCase;
|
use Amp\Success;
|
||||||
|
use Interop\Async\Awaitable;
|
||||||
|
|
||||||
class FunctionsTest extends TestCase
|
class FunctionsTest extends TestCase {
|
||||||
{
|
public function testPool() {
|
||||||
public function testPool()
|
$pool = $this->createMock(Pool::class);
|
||||||
{
|
|
||||||
$pool = $this->getMock(Pool::class);
|
|
||||||
|
|
||||||
Worker\pool($pool);
|
Worker\pool($pool);
|
||||||
|
|
||||||
@ -21,12 +20,11 @@ class FunctionsTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @depends testPool
|
* @depends testPool
|
||||||
*/
|
*/
|
||||||
public function testEnqueue()
|
public function testEnqueue() {
|
||||||
{
|
$pool = $this->createMock(Pool::class);
|
||||||
$pool = $this->getMock(Pool::class);
|
|
||||||
$pool->method('enqueue')
|
$pool->method('enqueue')
|
||||||
->will($this->returnCallback(function (Task $task) {
|
->will($this->returnCallback(function (Task $task): Awaitable {
|
||||||
return yield $task->run($this->getMock(Environment::class));
|
return new Success($task->run($this->createMock(Environment::class)));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Worker\pool($pool);
|
Worker\pool($pool);
|
||||||
@ -35,29 +33,27 @@ class FunctionsTest extends TestCase
|
|||||||
|
|
||||||
$task = new TestTask($value);
|
$task = new TestTask($value);
|
||||||
|
|
||||||
$coroutine = new Coroutine(Worker\enqueue($task));
|
$awaitable = Worker\enqueue($task);
|
||||||
|
|
||||||
$this->assertSame($value, $coroutine->wait());
|
$this->assertSame($value, \Amp\wait($awaitable));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @depends testPool
|
* @depends testPool
|
||||||
*/
|
*/
|
||||||
public function testGet()
|
public function testGet() {
|
||||||
{
|
$pool = $this->createMock(Pool::class);
|
||||||
$pool = $this->getMock(Pool::class);
|
|
||||||
$pool->expects($this->once())
|
$pool->expects($this->once())
|
||||||
->method('get')
|
->method('get')
|
||||||
->will($this->returnValue($this->getMock(Worker\Worker::class)));
|
->will($this->returnValue($this->createMock(Worker\Worker::class)));
|
||||||
|
|
||||||
Worker\pool($pool);
|
Worker\pool($pool);
|
||||||
|
|
||||||
$worker = Worker\get();
|
$worker = Worker\get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFactory()
|
public function testFactory() {
|
||||||
{
|
$factory = $this->createMock(WorkerFactory::class);
|
||||||
$factory = $this->getMock(WorkerFactory::class);
|
|
||||||
|
|
||||||
Worker\factory($factory);
|
Worker\factory($factory);
|
||||||
|
|
||||||
@ -67,12 +63,11 @@ class FunctionsTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @depends testFactory
|
* @depends testFactory
|
||||||
*/
|
*/
|
||||||
public function testCreate()
|
public function testCreate() {
|
||||||
{
|
$factory = $this->createMock(WorkerFactory::class);
|
||||||
$factory = $this->getMock(WorkerFactory::class);
|
|
||||||
$factory->expects($this->once())
|
$factory->expects($this->once())
|
||||||
->method('create')
|
->method('create')
|
||||||
->will($this->returnValue($this->getMock(Worker\Worker::class)));
|
->will($this->returnValue($this->createMock(Worker\Worker::class)));
|
||||||
|
|
||||||
Worker\factory($factory);
|
Worker\factory($factory);
|
||||||
|
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Concurrent\Worker\{DefaultPool, WorkerFactory, WorkerProcess};
|
use Amp\Concurrent\Worker\{ DefaultPool, WorkerFactory, WorkerProcess };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group process
|
* @group process
|
||||||
*/
|
*/
|
||||||
class ProcessPoolTest extends AbstractPoolTest
|
class ProcessPoolTest extends AbstractPoolTest {
|
||||||
{
|
protected function createPool($min = null, $max = null) {
|
||||||
protected function createPool($min = null, $max = null)
|
$factory = $this->createMock(WorkerFactory::class);
|
||||||
{
|
|
||||||
$factory = $this->getMock(WorkerFactory::class);
|
|
||||||
$factory->method('create')->will($this->returnCallback(function () {
|
$factory->method('create')->will($this->returnCallback(function () {
|
||||||
return new WorkerProcess();
|
return new WorkerProcess();
|
||||||
}));
|
}));
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Concurrent\Worker\{Environment, Task};
|
use Amp\Concurrent\Worker\{ Environment, Task };
|
||||||
|
|
||||||
class TestTask implements Task
|
class TestTask implements Task {
|
||||||
{
|
|
||||||
private $returnValue;
|
private $returnValue;
|
||||||
|
|
||||||
public function __construct($returnValue)
|
public function __construct($returnValue) {
|
||||||
{
|
|
||||||
$this->returnValue = $returnValue;
|
$this->returnValue = $returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(Environment $environment)
|
public function run(Environment $environment) {
|
||||||
{
|
|
||||||
return $this->returnValue;
|
return $this->returnValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Concurrent\Worker\{DefaultPool, WorkerFactory, WorkerThread};
|
use Amp\Concurrent\Worker\{ DefaultPool, WorkerFactory, WorkerThread };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group threading
|
* @group threading
|
||||||
* @requires extension pthreads
|
* @requires extension pthreads
|
||||||
*/
|
*/
|
||||||
class ThreadPoolTest extends AbstractPoolTest
|
class ThreadPoolTest extends AbstractPoolTest {
|
||||||
{
|
protected function createPool($min = null, $max = null) {
|
||||||
protected function createPool($min = null, $max = null)
|
$factory = $this->createMock(WorkerFactory::class);
|
||||||
{
|
|
||||||
$factory = $this->getMock(WorkerFactory::class);
|
|
||||||
$factory->method('create')->will($this->returnCallback(function () {
|
$factory->method('create')->will($this->returnCallback(function () {
|
||||||
return new WorkerThread();
|
return new WorkerThread();
|
||||||
}));
|
}));
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Concurrent\Worker\WorkerFork;
|
use Amp\Concurrent\Worker\WorkerFork;
|
||||||
|
|
||||||
@ -8,10 +8,8 @@ use Amp\Concurrent\Worker\WorkerFork;
|
|||||||
* @group forking
|
* @group forking
|
||||||
* @requires extension pcntl
|
* @requires extension pcntl
|
||||||
*/
|
*/
|
||||||
class WorkerForkTest extends AbstractWorkerTest
|
class WorkerForkTest extends AbstractWorkerTest {
|
||||||
{
|
protected function createWorker() {
|
||||||
protected function createWorker()
|
|
||||||
{
|
|
||||||
return new WorkerFork();
|
return new WorkerFork();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Concurrent\Worker\WorkerProcess;
|
use Amp\Concurrent\Worker\WorkerProcess;
|
||||||
|
|
||||||
class WorkerProcessTest extends AbstractWorkerTest
|
class WorkerProcessTest extends AbstractWorkerTest {
|
||||||
{
|
protected function createWorker() {
|
||||||
protected function createWorker()
|
|
||||||
{
|
|
||||||
return new WorkerProcess();
|
return new WorkerProcess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Amp\Tests\Concurrent\Worker;
|
namespace Amp\Concurrent\Test\Worker;
|
||||||
|
|
||||||
use Amp\Concurrent\Worker\WorkerThread;
|
use Amp\Concurrent\Worker\WorkerThread;
|
||||||
|
|
||||||
@ -8,10 +8,8 @@ use Amp\Concurrent\Worker\WorkerThread;
|
|||||||
* @group threading
|
* @group threading
|
||||||
* @requires extension pthreads
|
* @requires extension pthreads
|
||||||
*/
|
*/
|
||||||
class WorkerThreadTest extends AbstractWorkerTest
|
class WorkerThreadTest extends AbstractWorkerTest {
|
||||||
{
|
protected function createWorker() {
|
||||||
protected function createWorker()
|
|
||||||
{
|
|
||||||
return new WorkerThread();
|
return new WorkerThread();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user