Skip to content

Commit f6e4e68

Browse files
committed
update readme and stubs, tests, added signal example, and rename other
- update `uv_spawn` callback to just close all handles - renamed function, allow to return results - update readme about integration - updated/added channnel class method to send kill to subprocess - keep `libuv` doc/stubs in sync with [WIP] PR amphp/ext-uv#77
1 parent b7a181f commit f6e4e68

File tree

10 files changed

+199
-41
lines changed

10 files changed

+199
-41
lines changed

README.md

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,10 @@ $process = spawn(function (ChanneledInterface $channel) {
106106
echo $channel->read(); // same as echo fgets(STDIN);
107107
echo $channel->read();
108108

109-
// This is needed otherwise last output will be mixed in with the encoded return data.
109+
// The `return_in` is needed otherwise last output will be mixed in with the encoded return data.
110110
// Or some other processing could be done instead to make this unnecessary.
111-
returning(); // same as echo '___uv_spawn___'; usleep(50);
112-
113-
// all returned `data/results` are encoded, then decode by the parent.
114-
return 'return whatever';
111+
// All returned `data/results` are encoded, then decode by the parent.
112+
return \return_in(50, 'return whatever'); // same as echo '___uv_spawn___'; usleep(50); return 'return whatever';
115113
}, 0, $ipc)
116114
->progress(function ($type, $data) use ($ipc) {
117115
if ('ping' === $data) {
@@ -151,7 +149,7 @@ $process = Spawn::create(function () {
151149

152150
/////////////////////////////////////////////////////////////
153151
// This following statement is needed or some other processing performed before returning data.
154-
returning($delay); // will print '___uv_spawn___' and sleep for 50 microseconds;
152+
\return_in($delay, $with); // will print '___uv_spawn___' and sleep for 50 microseconds with data;
155153
/////////////////////////////////////////////////////////////
156154

157155
return `result`; // `result` will be encoded, then decoded by parent.
@@ -203,6 +201,8 @@ There's also `->done`, part of `->then()` extended callback method.
203201
->run();
204202
```
205203

204+
## How to integrate into another project
205+
206206
```php
207207
/**
208208
* Setup for third party integration.
@@ -216,9 +216,64 @@ There's also `->done`, part of `->then()` extended callback method.
216216
* @param bool $useUv - Turn **on/off** `uv_spawn` for child subprocess operations, will use **libuv** features,
217217
* if not **true** will use `proc_open` of **symfony/process**.
218218
*/
219-
spawn_setup($loop, $isYield, $bypass, $useUv)
219+
\spawn_setup($loop, $isYield, $bypass, $useUv)
220220
// Or
221221
Spawn::setup($loop = null, $isYield = true, $bypass = true, $useUv = true);
222+
223+
// For checking and acting on each subprocess status use:
224+
225+
/**
226+
* Check if the process has timeout (max. runtime).
227+
*/
228+
->isTimedOut();
229+
230+
/**
231+
* Call the timeout callbacks.
232+
*/
233+
->triggerTimeout();
234+
235+
/**
236+
* Checks if the process received a signal.
237+
*/
238+
->isSignaled();
239+
240+
/**
241+
* Call the signal callbacks.
242+
*/
243+
->triggerSignal($signal);
244+
245+
/**
246+
* Checks if the process is currently running.
247+
*/
248+
->isRunning();
249+
250+
/**
251+
* Call the progress callbacks on the child subprocess output in real time.
252+
*/
253+
->triggerProgress($type, $buffer);
254+
255+
/**
256+
* Checks if the process ended successfully.
257+
*/
258+
->isSuccessful();
259+
260+
/**
261+
* Call the success callbacks.
262+
* @return mixed
263+
*/
264+
->triggerSuccess();
265+
266+
/**
267+
* Checks if the process is terminated.
268+
*/
269+
->isTerminated();
270+
271+
/**
272+
* Call the error callbacks.
273+
* @throws \Exception if error callback array is empty
274+
*/
275+
->triggerError();
276+
222277
```
223278

224279
## Error handling

Spawn/Channeled.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,11 @@ public function send($message): ChanneledInterface
8989
throw new \RuntimeException(\sprintf('%s is closed', static::class));
9090
}
9191

92-
if ($this->channel !== null && $this->channel->getProcess() instanceof \UVProcess) {
92+
if (
93+
\is_object($this->channel)
94+
&& \method_exists($this->channel, 'getProcess')
95+
&& $this->channel->getProcess() instanceof \UVProcess
96+
) {
9397
\uv_write($this->channel->getPipeInput(), self::validateInput(__METHOD__, $message), function () {
9498
});
9599
} else {
@@ -99,9 +103,21 @@ public function send($message): ChanneledInterface
99103
return $this;
100104
}
101105

106+
/**
107+
* @codeCoverageIgnore
108+
*/
109+
public function kill(): void
110+
{
111+
if (\is_object($this->channel) && \method_exists($this->channel, 'stop')) {
112+
$this->channel->stop();
113+
}
114+
}
115+
102116
public function receive()
103117
{
104-
return $this->channel->getLast();
118+
if (\is_object($this->channel) && \method_exists($this->channel, 'getLast')) {
119+
return $this->channel->getLast();
120+
}
105121
}
106122

107123
/**

Spawn/ChanneledInterface.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ interface ChanneledInterface extends \IteratorAggregate
1515
/**
1616
* Setup the `parent` IPC handle.
1717
*
18-
* @param Object|Launcher $handle Use by `send()` and `receive()`
18+
* @param Object|Launcher $handle Use by `send()`, `receive()`, and `kill()`
1919
*
2020
* @return ChanneledInterface
2121
*/
@@ -57,6 +57,13 @@ public function isClosed(): bool;
5757
*/
5858
public function send($message): ChanneledInterface;
5959

60+
/**
61+
* Stop/kill the channel **child/subprocess** with `SIGKILL` signal.
62+
*
63+
* @return void
64+
*/
65+
public function kill(): void;
66+
6067
/**
6168
* Receive the last message from the IPC channel.
6269
*/

Spawn/Core.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ function is_base64($input): ?bool
288288
*
289289
* @return void
290290
*
291+
* @deprecated 1.1.3
292+
*
291293
* @codeCoverageIgnore
292294
*/
293295
function returning(int $microsecond = 50)
@@ -297,6 +299,41 @@ function returning(int $microsecond = 50)
297299
\usleep($microsecond);
298300
}
299301

302+
/**
303+
* For use when/before calling the actual `return` keyword, will write `___uv_spawn___` to standard
304+
* output and flush, then sleep for `microsecond`.
305+
*
306+
* The `___uv_spawn___` will be extracted from the encoded `return` data/result before being
307+
* processed by other internal routines/methods.
308+
*
309+
* - For use with subprocess `ipc` interaction.
310+
*
311+
* - This function is intended to overcome an issue when **`return`ing** the `encode` data/results
312+
* from an child subprocess operation.
313+
*
314+
* - The problem is the fact the last output is being mixed in with the `return` encode
315+
* data/results.
316+
*
317+
* - The parent is given no time to read data stream before the `return`, there was no
318+
* delay or processing preformed between child last output and the `return` statement.
319+
*
320+
* @param int $microsecond - `50` when using `uv_spawn`, otherwise `1500` or so higher with `proc_open`.
321+
*
322+
* @param mixed $withData to return to parent process.
323+
*
324+
* @return void|mixed
325+
*/
326+
function return_in(int $microsecond = 50, $withData = null)
327+
{
328+
\fwrite(\STDOUT, '___uv_spawn___');
329+
\fflush(\STDOUT);
330+
\usleep($microsecond);
331+
\fflush(\STDOUT);
332+
333+
if (!\is_null($withData))
334+
return $withData;
335+
}
336+
300337
/**
301338
* Setup for third party integration.
302339
*

Spawn/Launcher.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,7 @@ public static function add(
160160

161161
foreach ([$in, $out, $err, $process] as $handle) {
162162
if ($handle instanceof \UV) {
163-
\uv_close($handle, function () {
164-
});
163+
\uv_close($handle);
165164
}
166165
}
167166

Spawn/UVFunctions.php

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,8 @@ function uv_queue_work(UVLoop $loop, callable $callback, callable $after_callbac
665665
}
666666

667667
/**
668-
* Initialize the handle.
668+
* Initialize the `UVIdle` handle watcher.
669+
* Idle watchers get invoked every loop iteration.
669670
*
670671
* @param UVLoop $loop uv_loop handle.
671672
*
@@ -676,7 +677,21 @@ function uv_idle_init(UVLoop $loop = null)
676677
}
677678

678679
/**
679-
* Start the handle with the given callback.
680+
* Start the Idle handle with the given callback.
681+
*
682+
* The callbacks of idle handles are invoked once per event loop.
683+
*
684+
* The idle callback can be used to perform some very low priority activity.
685+
* For example, you could dispatch a summary of the daily application performance to the
686+
* developers for analysis during periods of idleness, or use the application’s CPU time
687+
* to perform SETI calculations :)
688+
*
689+
* An idle watcher is also useful in a GUI application.
690+
*
691+
* Say you are using an event loop for a file download. If the TCP socket is still being established
692+
* and no other events are present your event loop will pause (block), which means your progress bar
693+
* will freeze and the user will face an unresponsive application. In such a case queue up and idle
694+
* watcher to keep the UI operational.
680695
*
681696
* @param UVIdle $idle uv_idle handle.
682697
* @param callable $callback expects (UVIdle $handle)
@@ -686,7 +701,7 @@ function uv_idle_start(UVIdle $idle, callable $callback)
686701
}
687702

688703
/**
689-
* Stop the handle, the callback will no longer be called.
704+
* Stop the Idle handle, the callback will no longer be called.
690705
*
691706
* @param UVIdle $idle uv_idle handle.
692707
*/
@@ -695,7 +710,12 @@ function uv_idle_stop(UVIdle $idle)
695710
}
696711

697712
/**
698-
* Initialize the handle.
713+
* Initialize the `UVPrepare` handle watcher.
714+
* Prepare watchers get invoked before polling for I/O events.
715+
*
716+
* Their main purpose is to integrate other event mechanisms into `libuv` and their
717+
* use is somewhat advanced. They could be used, for example, to track variable changes,
718+
* implement your own watchers.
699719
*
700720
* @param UVLoop $loop uv loop handle.
701721
*
@@ -706,7 +726,7 @@ function uv_prepare_init(UVLoop $loop = null)
706726
}
707727

708728
/**
709-
* Start the handle with the given callback.
729+
* Start the Prepare handle with the given callback.
710730
*
711731
* @param UVPrepare $handle UV handle (prepare)
712732
* @param callable $callback expects (UVPrepare $prepare, int $status).
@@ -716,7 +736,7 @@ function uv_prepare_start(UVPrepare $handle, callable $callback)
716736
}
717737

718738
/**
719-
* Stop the handle, the callback will no longer be called.
739+
* Stop the Prepare handle, the callback will no longer be called.
720740
*
721741
* @param UVPrepare $handle UV handle (prepare).
722742
*/
@@ -725,7 +745,12 @@ function uv_prepare_stop(UVPrepare $handle)
725745
}
726746

727747
/**
728-
* Initialize the handle.
748+
* Initialize the `UVCheck` handle watcher.
749+
* Check watchers get invoked after polling for I/O events.
750+
*
751+
* Their main purpose is to integrate other event mechanisms into `libuv` and their
752+
* use is somewhat advanced. They could be used, for example, to track variable changes,
753+
* implement your own watchers.
729754
*
730755
* @param UVLoop $loop uv loop handle
731756
*
@@ -736,21 +761,7 @@ function uv_check_init(UVLoop $loop = null)
736761
}
737762

738763
/**
739-
* Start the handle with the given callback.
740-
*
741-
* The callbacks of idle handles are invoked once per event loop.
742-
*
743-
* The idle callback can be used to perform some very low priority activity.
744-
* For example, you could dispatch a summary of the daily application performance to the
745-
* developers for analysis during periods of idleness, or use the application’s CPU time
746-
* to perform SETI calculations :)
747-
*
748-
* An idle watcher is also useful in a GUI application.
749-
*
750-
* Say you are using an event loop for a file download. If the TCP socket is still being established
751-
* and no other events are present your event loop will pause (block), which means your progress bar
752-
* will freeze and the user will face an unresponsive application. In such a case queue up and idle
753-
* watcher to keep the UI operational.
764+
* Start the Check handle with the given callback.
754765
*
755766
* @param UVCheck $handle UV handle (check).
756767
* @param callable $callback expects (UVCheck $check, int $status).
@@ -760,7 +771,7 @@ function uv_check_start(UVCheck $handle, callable $callback)
760771
}
761772

762773
/**
763-
* Stop the handle, the callback will no longer be called.
774+
* Stop the Check handle, the callback will no longer be called.
764775
*
765776
* @param UVCheck $handle UV handle (check).
766777
*/
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ function (ChanneledInterface $channel) {
1414
$channel->write('ping');
1515
echo $channel->read();
1616
echo $channel->read();
17-
returning();
18-
return 'The game!';
17+
return \return_in(50, 'The game!');
1918
}
2019
)->progress(function ($type, $data) use ($ipc) {
2120
if ('ping' === $data) {

examples/signal.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
include 'vendor/autoload.php';
4+
5+
use Async\Spawn\Channeled;
6+
use Async\Spawn\ChanneledInterface;
7+
8+
$ipc = new Channeled();
9+
10+
echo "Let's play, ";
11+
12+
$process = \spawn(
13+
function (ChanneledInterface $channel) {
14+
$channel->write('ping');
15+
echo $channel->read();
16+
echo $channel->read();
17+
return \return_in(50, 'The game!');
18+
}
19+
)->signal(\SIGKILL, function ($signal) {
20+
echo "the process has been terminated with 'SIGKILL - " . $signal . "' signal!" . \PHP_EOL;
21+
})->progress(function ($type, $data) use ($ipc) {
22+
if ('ping' === $data) {
23+
$ipc->send('pang' . \PHP_EOL);
24+
$ipc->kill();
25+
} elseif (!$ipc->isClosed()) {
26+
$ipc->send('pong. ' . \PHP_EOL)
27+
->close();
28+
}
29+
});
30+
31+
$ipc->setHandle($process);
32+
$result = \spawn_run($process);
33+
echo \spawn_output($process) . \PHP_EOL;
34+
echo \spawn_result($process) . \PHP_EOL;
35+
echo $result . \PHP_EOL;

tests/ChanneledFallbackTest.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ public function testSimpleChanneled()
2222
$channel->write('ping');
2323
echo $channel->read();
2424
echo $channel->read();
25-
returning(1500);
26-
return 9;
25+
return \return_in(1500, 9);
2726
}, 10, $ipc)
2827
->progress(
2928
function ($type, $data) use ($ipc) {

0 commit comments

Comments
 (0)