diff --git a/.gitattributes b/.gitattributes index 27b765f..f11271b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ /tests export-ignore /.github export-ignore +/types export-ignore diff --git a/.github/workflows/close-pull-request.yml b/.github/workflows/close-pull-request.yml index 591822c..4cbca96 100644 --- a/.github/workflows/close-pull-request.yml +++ b/.github/workflows/close-pull-request.yml @@ -2,12 +2,8 @@ name: Close Pull Request on: pull_request_target: - types: [opened] + types: [ opened ] jobs: run: - runs-on: ubuntu-latest - steps: - - uses: superbrothers/close-pull-request@v3 - with: - comment: "Hi, this is a READ-ONLY repository, please submit your PR on the https://github.com/hyperf/hyperf repository.

This Pull Request will close automatically.

Thanks! " + uses: hyperf/.github/.github/workflows/close-pull-request.yml@master diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ebb5d0..8ec4a62 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,18 +8,4 @@ name: Release jobs: release: - name: Release - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false + uses: hyperf/.github/.github/workflows/release.yml@master diff --git a/composer.json b/composer.json index 1d60f65..202a79e 100644 --- a/composer.json +++ b/composer.json @@ -10,16 +10,16 @@ ], "homepage": "https://hyperf.io", "support": { - "docs": "https://hyperf.wiki", "issues": "https://github.com/hyperf/hyperf/issues", - "pull-request": "https://github.com/hyperf/hyperf/pulls", - "source": "https://github.com/hyperf/hyperf" + "source": "https://github.com/hyperf/hyperf", + "docs": "https://hyperf.wiki", + "pull-request": "https://github.com/hyperf/hyperf/pulls" }, "require": { - "php": ">=8.0", - "hyperf/context": "~3.0.0", - "hyperf/contract": "~3.0.0", - "hyperf/engine": "^1.2|^2.0" + "php": ">=8.1", + "hyperf/context": "~3.1.0", + "hyperf/contract": "~3.1.0", + "hyperf/engine": "^2.14.0" }, "autoload": { "psr-4": { @@ -39,7 +39,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-master": "3.1-dev" } } } diff --git a/src/Barrier.php b/src/Barrier.php new file mode 100644 index 0000000..b01182a --- /dev/null +++ b/src/Barrier.php @@ -0,0 +1,17 @@ +getId(); - } catch (\Throwable) { + } catch (Throwable) { return -1; } } + /** + * Create a coroutine with a copy of the parent coroutine context. + */ + public static function fork(callable $callable, array $keys = []): int + { + $cid = static::id(); + $callable = static function () use ($callable, $cid, $keys) { + Context::copy($cid, $keys); + $callable(); + }; + + return static::create($callable); + } + public static function inCoroutine(): bool { return Co::id() > 0; @@ -93,6 +119,14 @@ public static function exists(int $id): bool return Co::exists($id); } + /** + * @return iterable + */ + public static function list(): iterable + { + return Co::list(); + } + private static function printLog(Throwable $throwable): void { if (ApplicationContext::hasContainer()) { diff --git a/src/Exception/ChannelClosedException.php b/src/Exception/ChannelClosedException.php index a90c444..d352918 100644 --- a/src/Exception/ChannelClosedException.php +++ b/src/Exception/ChannelClosedException.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine\Exception; use RuntimeException; diff --git a/src/Exception/ExceptionThrower.php b/src/Exception/ExceptionThrower.php index 5b2f94e..4568efc 100644 --- a/src/Exception/ExceptionThrower.php +++ b/src/Exception/ExceptionThrower.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine\Exception; use Throwable; diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index c9b8614..0676d71 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine\Exception; class InvalidArgumentException extends \InvalidArgumentException diff --git a/src/Exception/ParallelExecutionException.php b/src/Exception/ParallelExecutionException.php index c674dca..304a43d 100644 --- a/src/Exception/ParallelExecutionException.php +++ b/src/Exception/ParallelExecutionException.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine\Exception; use RuntimeException; diff --git a/src/Exception/TimeoutException.php b/src/Exception/TimeoutException.php index fae8576..77e353c 100644 --- a/src/Exception/TimeoutException.php +++ b/src/Exception/TimeoutException.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine\Exception; use RuntimeException; diff --git a/src/Exception/WaitTimeoutException.php b/src/Exception/WaitTimeoutException.php index 8393231..1ad1296 100644 --- a/src/Exception/WaitTimeoutException.php +++ b/src/Exception/WaitTimeoutException.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine\Exception; class WaitTimeoutException extends TimeoutException diff --git a/src/Functions.php b/src/Functions.php index e4c97ac..35ded47 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -9,11 +9,13 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine; use Closure; use Hyperf\Context\ApplicationContext; use RuntimeException; +use Swoole\Runtime; /** * @param callable[] $callables @@ -28,6 +30,12 @@ function parallel(array $callables, int $concurrent = 0): array return $parallel->wait(); } +/** + * @template TReturn + * + * @param Closure():TReturn $closure + * @return TReturn + */ function wait(Closure $closure, ?float $timeout = null) { if (ApplicationContext::hasContainer()) { @@ -65,11 +73,11 @@ function run($callbacks, int $flags = SWOOLE_HOOK_ALL): bool throw new RuntimeException('Function \'run\' only execute in non-coroutine environment.'); } - \Swoole\Runtime::enableCoroutine($flags); + Runtime::enableCoroutine($flags); /* @phpstan-ignore-next-line */ $result = \Swoole\Coroutine\run(...(array) $callbacks); - \Swoole\Runtime::enableCoroutine(false); + Runtime::enableCoroutine(0); return $result; } diff --git a/src/Locker.php b/src/Locker.php index 3a4678a..3d59473 100644 --- a/src/Locker.php +++ b/src/Locker.php @@ -9,58 +9,36 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine; -use Hyperf\Coroutine\Traits\Container; -use Hyperf\Engine\Constant; -use Hyperf\Engine\Coroutine as Co; -use Swoole\Coroutine as SwooleCoroutine; +use Hyperf\Engine\Channel; class Locker { - use Container; - - public static function add(string $key, int $id): void - { - self::$container[$key][] = $id; - } - - public static function clear(string $key): void - { - unset(self::$container[$key]); - } + /** + * @var Channel[] + */ + protected static array $channels = []; public static function lock(string $key): bool { - if (! self::has($key)) { - self::add($key, 0); + if (! isset(static::$channels[$key])) { + static::$channels[$key] = new Channel(1); return true; } - self::add($key, Coroutine::id()); - // TODO: When the verion of `hyperf/engine` >= 2.0, use `Co::yield()` instead. - match (Constant::ENGINE) { - 'Swoole' => SwooleCoroutine::yield(), - /* @phpstan-ignore-next-line */ - default => Co::yield(), - }; + + $channel = static::$channels[$key]; + $channel->pop(-1); return false; } public static function unlock(string $key): void { - if (self::has($key)) { - $ids = self::get($key); - foreach ($ids as $id) { - if ($id > 0) { - // TODO: When the verion of `hyperf/engine` >= 2.0, use `Co::resumeById()` instead. - match (Constant::ENGINE) { - 'Swoole' => SwooleCoroutine::resume($id), - /* @phpstan-ignore-next-line */ - default => Co::resumeById($id), - }; - } - } - self::clear($key); + if (isset(static::$channels[$key])) { + $channel = static::$channels[$key]; + static::$channels[$key] = null; + $channel->close(); } } } diff --git a/src/Mutex.php b/src/Mutex.php new file mode 100644 index 0000000..ed2d3b5 --- /dev/null +++ b/src/Mutex.php @@ -0,0 +1,61 @@ +push(1, $timeout); + if ($channel->isTimeout() || $channel->isClosing()) { + return false; + } + + return true; + } + + public static function unlock(string $key, float $timeout = 5): bool + { + if (isset(static::$channels[$key])) { + $channel = static::$channels[$key]; + $channel->pop($timeout); + if ($channel->isTimeout()) { + // unlock more than once + return false; + } + } + + return true; + } + + public static function clear(string $key): void + { + if (isset(static::$channels[$key])) { + $channel = static::$channels[$key]; + static::$channels[$key] = null; + $channel->close(); + } + } +} diff --git a/src/Parallel.php b/src/Parallel.php index 22d4083..14f464a 100644 --- a/src/Parallel.php +++ b/src/Parallel.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine; use Hyperf\Coroutine\Exception\ParallelExecutionException; diff --git a/src/Traits/Container.php b/src/Traits/Container.php deleted file mode 100644 index a1be284..0000000 --- a/src/Traits/Container.php +++ /dev/null @@ -1,64 +0,0 @@ -wg = new WaitGroup(); + } + + public function create(callable $callable): void + { + $this->wg->add(); + + $callable = function () use ($callable) { + try { + $callable(); + } finally { + $this->wg->done(); + } + }; + + parent::create($callable); + } + + public function wait(float $timeout = -1): bool + { + return $this->wg->wait($timeout); + } +} diff --git a/src/WaitGroup.php b/src/WaitGroup.php index 67837d9..fa1fa0c 100644 --- a/src/WaitGroup.php +++ b/src/WaitGroup.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine; use BadMethodCallException; diff --git a/src/Waiter.php b/src/Waiter.php index 7d41ed0..c0231d5 100644 --- a/src/Waiter.php +++ b/src/Waiter.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace Hyperf\Coroutine; use Closure; @@ -29,7 +30,11 @@ public function __construct(float $timeout = 10.0) } /** + * @template TReturn + * + * @param Closure():TReturn $closure * @param null|float $timeout seconds + * @return TReturn */ public function wait(Closure $closure, ?float $timeout = null) { diff --git a/tests/BarrierTest.php b/tests/BarrierTest.php new file mode 100644 index 0000000..e78248d --- /dev/null +++ b/tests/BarrierTest.php @@ -0,0 +1,40 @@ +assertSame($N, $count); + } +} diff --git a/tests/Channel/CallerTest.php b/tests/Channel/CallerTest.php index 9c0f62a..9a480ac 100644 --- a/tests/Channel/CallerTest.php +++ b/tests/Channel/CallerTest.php @@ -9,10 +9,12 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace HyperfTest\Coroutine\Channel; use Hyperf\Coroutine\Channel\Caller; use Hyperf\Coroutine\Exception\WaitTimeoutException; +use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; use stdClass; @@ -22,6 +24,7 @@ * @internal * @coversNothing */ +#[CoversNothing] class CallerTest extends TestCase { public function testCallerWithNull() diff --git a/tests/Channel/ChannelManagerTest.php b/tests/Channel/ChannelManagerTest.php index 3c04882..19c830f 100644 --- a/tests/Channel/ChannelManagerTest.php +++ b/tests/Channel/ChannelManagerTest.php @@ -9,10 +9,12 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace HyperfTest\Coroutine\Channel; use Hyperf\Coroutine\Channel\Manager as ChannelManager; use Hyperf\Engine\Channel; +use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; use function Hyperf\Coroutine\go; @@ -21,6 +23,7 @@ * @internal * @coversNothing */ +#[CoversNothing] class ChannelManagerTest extends TestCase { public function testChannelManager() diff --git a/tests/ConcurrentTest.php b/tests/ConcurrentTest.php index 6363152..9b5168b 100644 --- a/tests/ConcurrentTest.php +++ b/tests/ConcurrentTest.php @@ -9,12 +9,14 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace HyperfTest\Coroutine; use Exception; use Hyperf\Context\ApplicationContext; use Hyperf\Coroutine\Concurrent; use Mockery; +use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Swoole\Coroutine; @@ -23,6 +25,7 @@ * @internal * @coversNothing */ +#[CoversNothing] class ConcurrentTest extends TestCase { protected function setUp(): void diff --git a/tests/ContainerTest.php b/tests/ContainerTest.php new file mode 100644 index 0000000..6943fcf --- /dev/null +++ b/tests/ContainerTest.php @@ -0,0 +1,32 @@ +assertSame($id, Foo::get('test')); + $this->assertFalse(Bar::has('test')); + $this->assertEmpty(Bar::list()); + } +} diff --git a/tests/CoroutineTest.php b/tests/CoroutineTest.php index 947b1e9..a8c67dc 100644 --- a/tests/CoroutineTest.php +++ b/tests/CoroutineTest.php @@ -9,6 +9,7 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace HyperfTest\Coroutine; use Exception; @@ -19,6 +20,8 @@ use Hyperf\Engine\Exception\CoroutineDestroyedException; use Hyperf\ExceptionHandler\Formatter\FormatterInterface; use Mockery; +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; use Throwable; @@ -31,6 +34,7 @@ * @internal * @coversNothing */ +#[CoversNothing] class CoroutineTest extends TestCase { protected function tearDown(): void @@ -68,9 +72,7 @@ public function testCoroutineParentIdHasBeenDestroyed() } } - /** - * @group NonCoroutine - */ + #[Group('NonCoroutine')] public function testCoroutineInTopCoroutine() { run(function () { diff --git a/tests/FunctionTest.php b/tests/FunctionTest.php index 7d3b40b..b1dcc6c 100644 --- a/tests/FunctionTest.php +++ b/tests/FunctionTest.php @@ -9,10 +9,13 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace HyperfTest\Coroutine; use Hyperf\Coroutine\Coroutine; use Hyperf\Engine\Channel; +use PHPUnit\Framework\Attributes\CoversNothing; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; use Swoole\Runtime; @@ -25,6 +28,7 @@ * @internal * @coversNothing */ +#[CoversNothing] class FunctionTest extends TestCase { public function testReturnOfGo() @@ -38,9 +42,7 @@ public function testReturnOfGo() $this->assertSame('Hyperf', $uniqid); } - /** - * @group NonCoroutine - */ + #[Group('NonCoroutine')] public function testRun() { $asserts = [ diff --git a/tests/LockerTest.php b/tests/LockerTest.php index c3c2fe0..91fc2a8 100644 --- a/tests/LockerTest.php +++ b/tests/LockerTest.php @@ -9,10 +9,12 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace HyperfTest\Coroutine; use Hyperf\Coroutine\Locker; use Hyperf\Engine\Channel; +use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\TestCase; use function Hyperf\Coroutine\go; @@ -21,6 +23,7 @@ * @internal * @coversNothing */ +#[CoversNothing] class LockerTest extends TestCase { public function testLockAndUnlock() diff --git a/tests/MutexTest.php b/tests/MutexTest.php new file mode 100644 index 0000000..679e458 --- /dev/null +++ b/tests/MutexTest.php @@ -0,0 +1,60 @@ +push($value); + } finally { + Mutex::unlock('test'); + } + } + }; + + $wg = new WaitGroup(5); + foreach (['h', 'e', 'l', 'l', 'o'] as $value) { + go(function () use ($func, $value, $wg) { + $func($value); + $wg->done(); + }); + } + + $res = ''; + $wg->wait(1); + for ($i = 0; $i < 5; ++$i) { + $res .= $chan->pop(1); + } + + $this->assertSame('hello', $res); + } +} diff --git a/tests/ParallelTest.php b/tests/ParallelTest.php index 29f9983..00d7475 100644 --- a/tests/ParallelTest.php +++ b/tests/ParallelTest.php @@ -9,12 +9,14 @@ * @contact group@hyperf.io * @license https://github.com/hyperf/hyperf/blob/master/LICENSE */ + namespace HyperfTest\Coroutine; use Exception; use Hyperf\Coroutine\Coroutine; use Hyperf\Coroutine\Exception\ParallelExecutionException; use Hyperf\Coroutine\Parallel; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; use RuntimeException; use Throwable; @@ -23,8 +25,9 @@ /** * @internal - * @covers \Hyperf\Coroutine\Parallel + * @coversNothing */ +#[CoversClass(Parallel::class)] class ParallelTest extends TestCase { public function testParallel() diff --git a/tests/Stub/Bar.php b/tests/Stub/Bar.php new file mode 100644 index 0000000..e59fae5 --- /dev/null +++ b/tests/Stub/Bar.php @@ -0,0 +1,20 @@ +