Skip to content

Commit a3047aa

Browse files
committed
Add OS info interfaces to readme
1 parent 47da314 commit a3047aa

File tree

9 files changed

+247
-36
lines changed

9 files changed

+247
-36
lines changed

composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131
"boson-php/uri-factory-contracts": "^0.16",
3232
"ffi/env": "^1.0",
3333
"psr/event-dispatcher": "^1.0",
34-
"react/promise": "^3.0",
35-
"revolt/event-loop": "^1.0"
34+
"react/promise": "^3.0"
3635
},
3736
"autoload": {
3837
"psr-4": {

src/Application.php

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
use Boson\Internal\BootHandler\WindowsDetachConsoleBootHandler;
2727
use Boson\Internal\DeferRunner\DeferRunnerInterface;
2828
use Boson\Internal\DeferRunner\NativeShutdownFunctionRunner;
29+
use Boson\Internal\Poller\SaucerPoller;
2930
use Boson\Internal\QuitHandler\PcntlQuitHandler;
3031
use Boson\Internal\QuitHandler\QuitHandlerInterface;
3132
use Boson\Internal\QuitHandler\WindowsQuitHandler;
3233
use Boson\Internal\Saucer\Saucer;
3334
use Boson\Internal\Saucer\SaucerInterface;
3435
use Boson\Internal\ThreadsCountResolver;
36+
use Boson\Poller\PollerInterface;
3537
use Boson\Shared\Marker\BlockingOperation;
3638
use Boson\Shared\Marker\RequiresDealloc;
3739
use Boson\WebView\WebView;
@@ -40,7 +42,6 @@
4042
use Boson\Window\Window;
4143
use FFI\CData;
4244
use Psr\EventDispatcher\EventDispatcherInterface;
43-
use Revolt\EventLoop;
4445

4546
/**
4647
* @template-implements IdentifiableInterface<ApplicationId>
@@ -165,6 +166,11 @@ class Application implements
165166
*/
166167
private readonly EventListener $listener;
167168

169+
/**
170+
* An application poller interface.
171+
*/
172+
public readonly PollerInterface $poller;
173+
168174
/**
169175
* @param EventDispatcherInterface|null $dispatcher an optional event
170176
* dispatcher for handling application events
@@ -208,6 +214,7 @@ public function __construct(
208214
$this->api = $this->createLibSaucer($info->library, $this->os->family, $this->cpu->arch);
209215
$this->id = $this->createApplicationId($this->api, $this->info->name, $this->info->threads);
210216
$this->windows = $this->createWindowManager($this->api, $this, $info, $this->listener);
217+
$this->poller = $this->createApplicationPoller($this->api);
211218

212219
// Initialization of Application's API
213220
$this->dialog = new ApplicationDialog($this->api, $this, $this->listener);
@@ -222,6 +229,14 @@ public function __construct(
222229
$this->boot();
223230
}
224231

232+
/**
233+
* Create an application event-loop.
234+
*/
235+
protected function createApplicationPoller(SaucerInterface $saucer): PollerInterface
236+
{
237+
return new SaucerPoller($this->id, $saucer);
238+
}
239+
225240
/**
226241
* Creates a new instance of {@see SaucerInterface} that provides access to the
227242
* native WebView API library. The returned object allows interacting with
@@ -501,17 +516,9 @@ public function run(): void
501516

502517
$this->listener->dispatch(new ApplicationStarted($this));
503518

504-
EventLoop::repeat(0, function (string $id): void {
505-
if ($this->isRunning === false) {
506-
EventLoop::cancel($id);
507-
508-
return;
509-
}
510-
511-
$this->api->saucer_application_run_once($this->id->ptr);
512-
});
513-
514-
EventLoop::run();
519+
while ($this->isRunning) {
520+
$this->poller->next();
521+
}
515522
}
516523

517524
/**
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Boson\Internal\Poller;
6+
7+
use Boson\ApplicationId;
8+
use Boson\Internal\Saucer\SaucerInterface;
9+
use Boson\Poller\PollerInterface;
10+
use Boson\Shared\IdValueGenerator\IdValueGeneratorInterface;
11+
use Boson\Shared\IdValueGenerator\PlatformDependentIntValueGenerator;
12+
use FFI\CData;
13+
14+
/**
15+
* @api
16+
*
17+
* @internal this is an internal library class, please do not use it in your code
18+
* @psalm-internal Boson
19+
*/
20+
final class SaucerPoller implements PollerInterface
21+
{
22+
/**
23+
* @var array<array-key, \Closure(array-key):void>
24+
*/
25+
private array $queueTasks = [];
26+
27+
/**
28+
* @var array<array-key, \Closure(array-key):void>
29+
*/
30+
private array $periodicTasks = [];
31+
32+
private TaskType $type = TaskType::Internal;
33+
34+
private readonly IdValueGeneratorInterface $ids;
35+
36+
private readonly CData $ptr;
37+
38+
public function __construct(
39+
private readonly ApplicationId $id,
40+
private readonly SaucerInterface $saucer,
41+
) {
42+
$this->ids = new PlatformDependentIntValueGenerator();
43+
$this->ptr = $this->id->ptr;
44+
}
45+
46+
public function next(): void
47+
{
48+
switch ($this->type) {
49+
case TaskType::Internal:
50+
$this->executeInternalTask();
51+
break;
52+
53+
case TaskType::Periodic:
54+
$this->executePeriodicTask();
55+
break;
56+
57+
case TaskType::Queued:
58+
$this->executeQueuedTask();
59+
break;
60+
}
61+
62+
$this->type = $this->type->next();
63+
}
64+
65+
private function executeInternalTask(): void
66+
{
67+
$this->saucer->saucer_application_run_once($this->ptr);
68+
}
69+
70+
private function executePeriodicTask(): void
71+
{
72+
foreach ($this->periodicTasks as $id => $task) {
73+
$task($id);
74+
75+
return;
76+
}
77+
}
78+
79+
private function executeQueuedTask(): void
80+
{
81+
foreach ($this->queueTasks as $id => $task) {
82+
unset($this->queueTasks[$id]);
83+
84+
$task($id);
85+
86+
return;
87+
}
88+
}
89+
90+
public function defer(callable $task): int|string
91+
{
92+
$this->queueTasks[$id = $this->ids->nextId()] = $task(...);
93+
94+
return $id;
95+
}
96+
97+
public function repeat(callable $task): int|string
98+
{
99+
$this->periodicTasks[$id = $this->ids->nextId()] = $task(...);
100+
101+
return $id;
102+
}
103+
104+
public function delay(float $delay, callable $task): int|string
105+
{
106+
$stopsAfter = \microtime(true) + $delay;
107+
108+
return $this->repeat(function (string $taskId) use ($stopsAfter, $task): void {
109+
if (\microtime(true) > $stopsAfter) {
110+
$task($taskId);
111+
112+
$this->cancel($taskId);
113+
}
114+
});
115+
}
116+
117+
public function cancel(int|string $taskId): void
118+
{
119+
unset($this->queueTasks[$taskId]);
120+
}
121+
}

src/Internal/Poller/TaskType.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Boson\Internal\Poller;
6+
7+
enum TaskType
8+
{
9+
case Internal;
10+
case Periodic;
11+
case Queued;
12+
13+
public function next(): self
14+
{
15+
return match ($this) {
16+
self::Internal => self::Periodic,
17+
self::Periodic => self::Queued,
18+
self::Queued => self::Internal,
19+
};
20+
}
21+
}

src/Poller/PollerInterface.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Boson\Poller;
6+
7+
interface PollerInterface
8+
{
9+
/**
10+
* Poll next application loop event.
11+
*/
12+
public function next(): void;
13+
14+
/**
15+
* Defer the execution of a callback.
16+
*
17+
* @template TArgTaskId of array-key
18+
*
19+
* @param callable(TArgTaskId):void $task The callback to defer.
20+
*
21+
* @return TArgTaskId An unique identifier that can be used to cancel
22+
* the callback.
23+
*/
24+
public function defer(callable $task): int|string;
25+
26+
/**
27+
* Repeatedly execute a callback.
28+
*
29+
* @template TArgTaskId of array-key
30+
*
31+
* @param callable(TArgTaskId):void $task The callback to execute.
32+
*
33+
* @return TArgTaskId An unique identifier that can be used to cancel
34+
* the callback.
35+
*/
36+
public function repeat(callable $task): int|string;
37+
38+
/**
39+
* Delay the execution of a callback.
40+
*
41+
* @template TArgTaskId of array-key
42+
*
43+
* @param float $delay The amount of time, in seconds, to delay the execution for.
44+
* @param callable(TArgTaskId):void $task The callback to delay.
45+
*
46+
* @return TArgTaskId A unique identifier that can be used to
47+
* cancel the callback.
48+
*/
49+
public function delay(float $delay, callable $task): int|string;
50+
51+
/**
52+
* Cancel a task.
53+
*
54+
* This will detach the event loop from all resources that are associated
55+
* to the callback. After this operation the callback is permanently
56+
* invalid. Calling this function MUST NOT fail, even if passed an invalid
57+
* identifier.
58+
*
59+
* @param array-key $taskId The callback identifier.
60+
*/
61+
public function cancel(int|string $taskId): void;
62+
}

src/WebView/Api/Data/WebViewData.php

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
use JetBrains\PhpStorm\Language;
2020
use React\Promise\Deferred;
2121
use React\Promise\PromiseInterface;
22-
use Revolt\EventLoop;
2322

2423
use function React\Promise\resolve;
2524

@@ -146,29 +145,34 @@ public function get(#[Language('JavaScript')] string $code, ?float $timeout = nu
146145
throw ApplicationNotRunningException::becauseApplicationNotRunning($code);
147146
}
148147

149-
$suspension = EventLoop::getSuspension();
148+
$isProcessed = false;
149+
$result = null;
150150

151151
$this->defer($code)
152-
->then(static function (mixed $input) use ($suspension): mixed {
153-
$suspension->resume($input);
152+
->then(static function (mixed $input) use (&$isProcessed, &$result): mixed {
153+
$isProcessed = true;
154+
$result = $input;
154155

155156
return $input;
156157
})
157-
->catch(static function (\Throwable $e) use ($suspension): \Throwable {
158-
$suspension->throw($e);
158+
->catch(static function (\Throwable $e) use (&$isProcessed, &$result): \Throwable {
159+
$isProcessed = true;
160+
$result = $e;
159161

160162
return $e;
161163
});
162164

163165
$timeout ??= $this->timeout;
164166

165-
$delayId = EventLoop::delay($timeout, function () use ($code, $timeout): void {
167+
$poller = $this->context->window->app->poller;
168+
169+
$poller->delay($timeout, function () use ($code, $timeout): void {
166170
throw StalledRequestException::becauseRequestIsStalled($code, $timeout);
167171
});
168172

169-
$result = $suspension->suspend();
170-
171-
EventLoop::cancel($delayId);
173+
while ($isProcessed === false) {
174+
$poller->next();
175+
}
172176

173177
return $result;
174178
}

src/WebView/Api/Schemes/WebViewSchemeHandler.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use Boson\WebView\Internal\WebViewSchemeHandler\MimeTypeReader;
1818
use Boson\WebView\WebView;
1919
use FFI\CData;
20-
use Revolt\EventLoop;
2120

2221
final class WebViewSchemeHandler extends WebViewExtension implements SchemesApiInterface
2322
{
@@ -63,7 +62,7 @@ private function onSafeRequest(CData $_, CData $request, CData $executor): void
6362
$code = SaucerSchemeError::SAUCER_REQUEST_ERROR_FAILED;
6463
$this->api->saucer_scheme_executor_reject($executor, $code);
6564

66-
EventLoop::defer(static function () use ($e) {
65+
$this->context->window->app->poller->defer(static function () use ($e) {
6766
throw $e;
6867
});
6968

0 commit comments

Comments
 (0)