Skip to content

Commit a6502f2

Browse files
committed
Add global timeout
1 parent b3ba0f9 commit a6502f2

File tree

4 files changed

+99
-0
lines changed

4 files changed

+99
-0
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- [#227](https://github.com/steevanb/php-parallel-processes/issues/227) Fix TearDownProcess who never start in some cases
44
- [#226](https://github.com/steevanb/php-parallel-processes/issues/226) Add compatibility with Symfony 6.3
55
- [#228](https://github.com/steevanb/php-parallel-processes/issues/228) Execute phpstan for each Symfony version
6+
- [#224](https://github.com/steevanb/php-parallel-processes/issues/224) Add global timeout with ParallelProcessesApplication::setTimeout()
67

78
### [0.11.0](../../compare/0.10.0...0.11.0) - 2023-04-05
89

src/Console/Application/ParallelProcessesApplication.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ class ParallelProcessesApplication extends SingleCommandApplication implements S
3636

3737
protected bool $canceled = false;
3838

39+
/** Timeout in seconds, null for no timeout */
40+
protected ?int $timeout = null;
41+
42+
protected ?int $startTime = null;
43+
44+
protected ?int $exitCode = null;
45+
3946
public function __construct(string $name = null)
4047
{
4148
parent::__construct($name);
@@ -80,6 +87,18 @@ public function getProcesses(): ProcessInterfaceArray
8087
return $this->processes;
8188
}
8289

90+
public function setTimeout(?int $timeout): static
91+
{
92+
$this->timeout = $timeout;
93+
94+
return $this;
95+
}
96+
97+
public function getTimeout(): ?int
98+
{
99+
return $this->timeout;
100+
}
101+
83102
public function setTheme(ThemeInterface $theme): static
84103
{
85104
$this->theme = $theme;
@@ -177,6 +196,8 @@ protected function isCanceled(): bool
177196

178197
protected function runProcessesInParallel(InputInterface $input, OutputInterface $output): int
179198
{
199+
$this->startTime = time();
200+
180201
$this
181202
->defineThemeFromInput($input)
182203
->defineRefreshIntervalFromInput($input);
@@ -309,6 +330,11 @@ protected function waitProcessesTermination(OutputInterface $output): static
309330
break;
310331
}
311332

333+
if (is_int($this->getTimeout()) && $this->getStartTime() + $this->getTimeout() < time()) {
334+
$this->setExitCode(static::FAILURE);
335+
$this->handleSignal(SIGINT);
336+
}
337+
312338
$terminated = 0;
313339

314340
$this
@@ -365,8 +391,19 @@ protected function startReadyProcesses(): static
365391
return $this;
366392
}
367393

394+
protected function setExitCode(?int $exitCode): static
395+
{
396+
$this->exitCode = $exitCode;
397+
398+
return $this;
399+
}
400+
368401
protected function getExitCode(): int
369402
{
403+
if (is_int($this->exitCode)) {
404+
return $this->exitCode;
405+
}
406+
370407
$return = static::SUCCESS;
371408

372409
foreach ($this->getProcesses()->toArray() as $process) {
@@ -393,4 +430,13 @@ protected function createDefaultOutput(): OutputInterface
393430
{
394431
return new ConsoleBufferedOutput();
395432
}
433+
434+
protected function getStartTime(): int
435+
{
436+
if (is_int($this->startTime) === false) {
437+
throw new ParallelProcessException('Call ' . static::class . '::run() before getStartTime().');
438+
}
439+
440+
return $this->startTime;
441+
}
396442
}

tests/Console/Application/ParallelProcessesApplication/ConstructorTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ public function testSetTheme(): void
2121
static::assertInstanceOf(DefaultTheme::class, $application->getTheme());
2222
static::assertSame(10000, $application->getRefreshInterval());
2323
static::assertNull($application->getMaximumParallelProcesses());
24+
static::assertNull($application->getTimeout());
2425
}
2526
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Steevanb\ParallelProcess\Tests\Console\Application\ParallelProcessesApplication;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Steevanb\ParallelProcess\{
9+
Console\Application\ParallelProcessesApplication,
10+
Process\Process,
11+
};
12+
use Symfony\Component\Console\{
13+
Input\ArrayInput,
14+
Output\NullOutput
15+
};
16+
17+
final class TimeoutTest extends TestCase
18+
{
19+
/**
20+
* @covers \Steevanb\ParallelProcess\Console\Application\ParallelProcessesApplication::setTimeout
21+
* @covers \Steevanb\ParallelProcess\Console\Application\ParallelProcessesApplication::getTimeout
22+
*/
23+
public function testAccessors(): void
24+
{
25+
$application = (new ParallelProcessesApplication())
26+
->setTimeout(10);
27+
28+
static::assertSame(10, $application->getTimeout());
29+
}
30+
31+
/**
32+
* @covers \Steevanb\ParallelProcess\Console\Application\ParallelProcessesApplication::setTimeout
33+
* @covers \Steevanb\ParallelProcess\Console\Application\ParallelProcessesApplication::waitProcessesTermination
34+
*/
35+
public function testTimeout(): void
36+
{
37+
$process = new Process(['sleep', '10'], dirname(__DIR__, 4));
38+
39+
$time = time();
40+
41+
$exitCode = (new ParallelProcessesApplication())
42+
->setTimeout(1)
43+
->setAutoExit(false)
44+
->addProcess($process)
45+
->run(new ArrayInput([]), new NullOutput());
46+
47+
static::assertSame(1, $exitCode);
48+
static::assertTrue(time() >= $time + 1);
49+
static::assertTrue(time() <= $time + 2);
50+
}
51+
}

0 commit comments

Comments
 (0)