Skip to content

Commit

Permalink
Make the ReactPHP Tasque EventLoop thread "shout" any Throwables
Browse files Browse the repository at this point in the history
  • Loading branch information
asmblah committed Nov 7, 2023
1 parent 5f67217 commit f707e72
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 18 deletions.
32 changes: 16 additions & 16 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 32 additions & 2 deletions src/TasqueEventLoop.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@

namespace Tasque\EventLoop;

use LogicException;
use Nytris\Core\Package\PackageContextInterface;
use Nytris\Core\Package\PackageInterface;
use React\EventLoop\Loop;
use Tasque\Core\Thread\Control\ExternalControlInterface;
use Tasque\Tasque;

/**
Expand All @@ -28,6 +30,7 @@
class TasqueEventLoop implements TasqueEventLoopInterface
{
private static bool $installed = false;
private static ?ExternalControlInterface $eventLoopThread = null;

/**
* @inheritDoc
Expand All @@ -45,13 +48,28 @@ public static function getVendor(): string
return 'tasque';
}

/**
* @inheritDoc
*/
public static function getEventLoopThread(): ExternalControlInterface
{
if (self::$eventLoopThread === null) {
throw new LogicException(
'Event loop thread is not set - did you forget to install this package in nytris.config.php?'
);
}

return self::$eventLoopThread;
}

/**
* @inheritDoc
*/
public static function install(PackageContextInterface $packageContext, PackageInterface $package): void
{
self::$installed = true;

// Don't tock-ify the ReactPHP event loop logic itself for efficiency.
Tasque::excludeComposerPackage('react/event-loop');

/*
Expand Down Expand Up @@ -85,18 +103,30 @@ public static function install(PackageContextInterface $packageContext, PackageI
$tasque = new Tasque();

// Run the ReactPHP event loop itself inside a Tasque green thread.
$eventLoopThread = $tasque->createThread(function () {
self::$eventLoopThread = $tasque->createThread(function () {
Loop::run();
});

$eventLoopThread->start();
// Propagate any Throwables from the event loop up to the main thread.
self::$eventLoopThread->shout();

self::$eventLoopThread->start();
}

/**
* @inheritDoc
*/
public static function isInstalled(): bool
{
return self::$installed;
}

/**
* @inheritDoc
*/
public static function uninstall(): void
{
self::$eventLoopThread = null;
self::$installed = false;
}
}
5 changes: 5 additions & 0 deletions src/TasqueEventLoopInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace Tasque\EventLoop;

use Nytris\Core\Package\PackageFacadeInterface;
use Tasque\Core\Thread\Control\ExternalControlInterface;

/**
* Interface TasqueEventLoopInterface.
Expand All @@ -24,4 +25,8 @@
*/
interface TasqueEventLoopInterface extends PackageFacadeInterface
{
/**
* Fetches the Tasque thread that is running the ReactPHP event loop.
*/
public static function getEventLoopThread(): ExternalControlInterface;
}
96 changes: 96 additions & 0 deletions tests/Unit/TasqueEventLoopTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

/*
* Tasque EventLoop - ReactPHP event loop using a Tasque green thread.
* Copyright (c) Dan Phillimore (asmblah)
* https://github.com/nytris/tasque-event-loop/
*
* Released under the MIT license.
* https://github.com/nytris/tasque-event-loop/raw/main/MIT-LICENSE.txt
*/

declare(strict_types=1);

namespace Tasque\EventLoop\Tests\Unit;

use LogicException;
use Mockery\MockInterface;
use Nytris\Core\Package\PackageContextInterface;
use Nytris\Core\Package\PackageInterface;
use Tasque\Core\Thread\Control\ExternalControlInterface;
use Tasque\EventLoop\TasqueEventLoop;
use Tasque\EventLoop\Tests\AbstractTestCase;
use Tasque\Tasque;
use Tasque\TasquePackageInterface;

/**
* Class TasqueEventLoopTest.
*
* @author Dan Phillimore <dan@ovms.co>
*/
class TasqueEventLoopTest extends AbstractTestCase
{
private MockInterface&PackageContextInterface $eventLoopPackageContext;
private MockInterface&PackageInterface $eventLoopPackage;
private MockInterface&PackageContextInterface $tasquePackageContext;
private MockInterface&TasquePackageInterface $tasquePackage;

public function setUp(): void
{
$this->eventLoopPackage = mock(PackageInterface::class);
$this->eventLoopPackageContext = mock(PackageContextInterface::class);
$this->tasquePackage = mock(TasquePackageInterface::class, [
'getSchedulerStrategy' => null,
]);
$this->tasquePackageContext = mock(PackageContextInterface::class);

TasqueEventLoop::uninstall();
}

public function tearDown(): void
{
TasqueEventLoop::uninstall();
}

public function testPackageIsDefinedCorrectly(): void
{
static::assertSame('tasque', TasqueEventLoop::getVendor());
static::assertSame('event-loop', TasqueEventLoop::getName());
}

public function testGetEventLoopThreadRaisesExceptionWhenPackageNotLoaded(): void
{
$this->expectException(LogicException::class);
$this->expectExceptionMessage(
'Event loop thread is not set - did you forget to install this package in nytris.config.php?'
);

TasqueEventLoop::getEventLoopThread();
}

public function testInstallCreatesTasqueEventLoopThread(): void
{
Tasque::install($this->tasquePackageContext, $this->tasquePackage);
TasqueEventLoop::install($this->eventLoopPackageContext, $this->eventLoopPackage);

static::assertInstanceOf(ExternalControlInterface::class, TasqueEventLoop::getEventLoopThread());
}

public function testInstallMarksTasqueEventLoopThreadAsShouting(): void
{
Tasque::install($this->tasquePackageContext, $this->tasquePackage);
TasqueEventLoop::install($this->eventLoopPackageContext, $this->eventLoopPackage);

static::assertTrue(TasqueEventLoop::getEventLoopThread()->isShouting());
}

public function testUninstallUninstallsTasqueEventLoop(): void
{
Tasque::install($this->tasquePackageContext, $this->tasquePackage);
TasqueEventLoop::install($this->eventLoopPackageContext, $this->eventLoopPackage);

TasqueEventLoop::uninstall();

static::assertFalse(TasqueEventLoop::isInstalled());
}
}

0 comments on commit f707e72

Please sign in to comment.