Skip to content

Commit 1d54523

Browse files
committed
added option to define init tasks
1 parent 39d8539 commit 1d54523

9 files changed

+243
-1
lines changed

README.md

+18
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,24 @@ $taskmaster->autoDetectWorkers(4, false);
553553
$taskmaster->autoDetectWorkers(4, true, false);
554554
```
555555

556+
### Init tasks
557+
558+
You can define tasks that are executed on every worker instance before the first task is executed.
559+
This is helpful to run some initial setup or (in case of the [`ForkWorker`](src/Environment/Fork/ForkWorker.php))
560+
to clear some variables that are inherited from the parent process, e.g. database connections.
561+
562+
```php
563+
// init tasks are always provided by a task factory
564+
$taskmaster->setDefaultInitTaskFactory(new InitTaskFactory());
565+
566+
// but taskmaster can create task factories automatically by cloning or instancing a task
567+
$taskmaster->setDefaultInitTask(new InitTask());
568+
$taskmaster->setDefaultInitTask(InitTask::class);
569+
570+
// you can also define a task factory for a specific worker
571+
$worker->setInitTaskFactory(new InitTaskFactory());
572+
```
573+
556574
## Running tasks
557575

558576
After writing your tasks, creating them and defining the workers, you can start running the tasks.

src/Task/CloneTaskFactory.php

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace Aternos\Taskmaster\Task;
4+
5+
/**
6+
* Class CloneTaskFactory
7+
*
8+
* The CloneTaskFactory creates clones of a task.
9+
*
10+
* @package Aternos\Taskmaster\Task
11+
*/
12+
class CloneTaskFactory extends TaskFactory
13+
{
14+
protected int $count = 0;
15+
16+
/**
17+
* @param TaskInterface $task
18+
* @param int|null $limit
19+
*/
20+
public function __construct(protected TaskInterface $task, protected ?int $limit = null)
21+
{
22+
}
23+
24+
/**
25+
* @inheritDoc
26+
*/
27+
public function createNextTask(?string $group): ?TaskInterface
28+
{
29+
if ($this->limit !== null && $this->count >= $this->limit) {
30+
return null;
31+
}
32+
$this->count++;
33+
return clone $this->task;
34+
}
35+
}

src/Task/InstanceTaskFactory.php

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Aternos\Taskmaster\Task;
4+
5+
use Aternos\Taskmaster\Task\TaskFactory;
6+
7+
/**
8+
* Class InstanceTaskFactory
9+
*
10+
* The InstanceTaskFactory creates instances of a task class.
11+
*
12+
* @package Aternos\Taskmaster\Task
13+
*/
14+
class InstanceTaskFactory extends TaskFactory
15+
{
16+
protected int $count = 0;
17+
18+
/**
19+
* @param class-string<TaskInterface> $taskClass
20+
* @param int|null $limit
21+
*/
22+
public function __construct(protected string $taskClass, protected ?int $limit = null)
23+
{
24+
}
25+
26+
/**
27+
* @inheritDoc
28+
*/
29+
public function createNextTask(?string $group): ?TaskInterface
30+
{
31+
if ($this->limit !== null && $this->count >= $this->limit) {
32+
return null;
33+
}
34+
$this->count++;
35+
return new $this->taskClass();
36+
}
37+
}

src/Taskmaster.php

+42
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
use Aternos\Taskmaster\Proxy\ProcessProxy;
1313
use Aternos\Taskmaster\Proxy\ProxyInterface;
1414
use Aternos\Taskmaster\Proxy\ProxyStatus;
15+
use Aternos\Taskmaster\Task\CloneTaskFactory;
16+
use Aternos\Taskmaster\Task\InstanceTaskFactory;
1517
use Aternos\Taskmaster\Task\TaskFactoryInterface;
1618
use Aternos\Taskmaster\Task\TaskInterface;
1719
use Aternos\Taskmaster\Worker\SocketWorkerInterface;
@@ -64,6 +66,7 @@ class Taskmaster
6466

6567
protected TaskmasterOptions $options;
6668
protected float $defaultTaskTimeout = 0;
69+
protected ?TaskFactoryInterface $initTaskFactory = null;
6770

6871
/**
6972
* Taskmaster constructor
@@ -389,6 +392,7 @@ public function addWorker(WorkerInterface $worker): static
389392
}
390393

391394
$worker->setOptionsIfNecessary($this->options);
395+
$worker->setInitTaskFactoryIfNecessary($this->initTaskFactory);
392396
$this->workers[] = $worker;
393397
return $this;
394398
}
@@ -561,6 +565,44 @@ public function setDefaultTaskTimeout(float $timeout): static
561565
return $this;
562566
}
563567

568+
/**
569+
* Set the default init task factory for all workers
570+
*
571+
* The init task factory produces tasks that are executed once as first task on every worker instance
572+
* to initialize the worker.
573+
*
574+
* You can also set the init task factory for each worker individually with {@link WorkerInterface::setInitTaskFactory()}.
575+
*
576+
* @param TaskFactoryInterface|null $initTaskFactory
577+
* @return $this
578+
*/
579+
public function setDefaultInitTaskFactory(?TaskFactoryInterface $initTaskFactory): static
580+
{
581+
$this->initTaskFactory = $initTaskFactory;
582+
return $this;
583+
}
584+
585+
/**
586+
* Set the default init task for all workers
587+
*
588+
* The init task is executed once as first task on every worker instance.
589+
* This automatically creates a task factory that produces the init tasks.
590+
*
591+
* If you pass an object, the task will be cloned for each worker instance, if you pass
592+
* a class name, a new instance of the class will be created for each worker instance.
593+
*
594+
* @param TaskInterface|class-string<TaskInterface> $task
595+
* @return void
596+
*/
597+
public function setDefaultInitTask(TaskInterface|string $task): void
598+
{
599+
if (is_string($task)) {
600+
$this->initTaskFactory = new InstanceTaskFactory($task);
601+
} else {
602+
$this->initTaskFactory = new CloneTaskFactory($task);
603+
}
604+
}
605+
564606
/**
565607
* Get the current {@link TaskmasterOptions} instance
566608
*

src/Worker/Worker.php

+32-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Aternos\Taskmaster\Proxy\ProxiedSocket;
66
use Aternos\Taskmaster\Proxy\ProxyInterface;
77
use Aternos\Taskmaster\Proxy\ProxyStatus;
8+
use Aternos\Taskmaster\Task\TaskFactoryInterface;
89
use Aternos\Taskmaster\Task\TaskInterface;
910
use Aternos\Taskmaster\TaskmasterOptions;
1011
use Aternos\Taskmaster\Worker\Instance\ProxyableWorkerInstanceInterface;
@@ -32,6 +33,8 @@ abstract class Worker implements WorkerInterface
3233
protected ?ProxyInterface $proxy = null;
3334
protected bool $instanceStarted = false;
3435
protected ?TaskInterface $queuedTask = null;
36+
protected ?TaskFactoryInterface $initTaskFactory = null;
37+
protected ?TaskInterface $initTask = null;
3538

3639
/**
3740
* @inheritDoc
@@ -53,6 +56,26 @@ public function setOptionsIfNecessary(TaskmasterOptions $options): static
5356
return $this;
5457
}
5558

59+
/**
60+
* @inheritDoc
61+
*/
62+
public function setInitTaskFactory(?TaskFactoryInterface $initTaskFactory): static
63+
{
64+
$this->initTaskFactory = $initTaskFactory;
65+
return $this;
66+
}
67+
68+
/**
69+
* @inheritDoc
70+
*/
71+
public function setInitTaskFactoryIfNecessary(?TaskFactoryInterface $initTaskFactory): static
72+
{
73+
if ($this->initTaskFactory === null) {
74+
$this->setInitTaskFactory($initTaskFactory);
75+
}
76+
return $this;
77+
}
78+
5679
/**
5780
* Get the current instance or create a new one if necessary
5881
*
@@ -86,6 +109,11 @@ protected function startInstance(): void
86109
{
87110
$this->instanceStarted = true;
88111
$instance = $this->getInstance();
112+
113+
if ($this->initTaskFactory) {
114+
$this->initTask = $this->initTaskFactory->createNextTask(null);
115+
}
116+
89117
if (!$this->proxy) {
90118
$instance->init()->start();
91119
return;
@@ -115,7 +143,10 @@ public function update(): static
115143
}
116144
$instance->update();
117145
if ($instance->getStatus() === WorkerInstanceStatus::IDLE) {
118-
if ($this->queuedTask) {
146+
if ($this->initTask) {
147+
$instance->runTask($this->initTask);
148+
$this->initTask = null;
149+
} elseif ($this->queuedTask) {
119150
$instance->runTask($this->queuedTask);
120151
$this->queuedTask = null;
121152
} else {

src/Worker/WorkerInterface.php

+23
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Aternos\Taskmaster\Proxy\ProxyInterface;
66
use Aternos\Taskmaster\Runtime\RuntimeInterface;
7+
use Aternos\Taskmaster\Task\TaskFactoryInterface;
78
use Aternos\Taskmaster\Task\TaskInterface;
89
use Aternos\Taskmaster\Taskmaster;
910
use Aternos\Taskmaster\TaskmasterOptions;
@@ -62,6 +63,28 @@ public function setOptions(TaskmasterOptions $options): static;
6263
*/
6364
public function setOptionsIfNecessary(TaskmasterOptions $options): static;
6465

66+
/**
67+
* Set the init task factory for the worker
68+
*
69+
* Init tasks are executed once as first task on every worker instance to initialize the worker.
70+
*
71+
* @param TaskFactoryInterface|null $initTaskFactory
72+
* @return $this
73+
*/
74+
public function setInitTaskFactory(?TaskFactoryInterface $initTaskFactory): static;
75+
76+
/**
77+
* Set the init task factory for the worker if necessary
78+
*
79+
* If the init task factory is already set, it will not be overwritten.
80+
* This is called by {@link Taskmaster::addWorker()}.
81+
* If you want to set a different init task factory, call this before adding the worker.
82+
*
83+
* @param TaskFactoryInterface|null $initTaskFactory
84+
* @return $this
85+
*/
86+
public function setInitTaskFactoryIfNecessary(?TaskFactoryInterface $initTaskFactory): static;
87+
6588
/**
6689
* Update the worker and its instance
6790
*

test/Integration/AsyncWorkerTestCase.php

+18
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
use Aternos\Taskmaster\Exception\TaskTimeoutException;
77
use Aternos\Taskmaster\Taskmaster;
88
use Aternos\Taskmaster\Test\Util\Task\EmptyTask;
9+
use Aternos\Taskmaster\Test\Util\Task\InitTask;
10+
use Aternos\Taskmaster\Test\Util\Task\InitValidateTask;
911
use Aternos\Taskmaster\Test\Util\Task\InterruptableSleepTask;
1012
use Aternos\Taskmaster\Test\Util\Task\SleepTask;
1113
use Aternos\Taskmaster\Test\Util\Task\SyncTask;
@@ -82,4 +84,20 @@ public function testRecoverAfterTimeout(): void
8284
}
8385
$this->assertEquals(6, $counter);
8486
}
87+
88+
public function testInitTask(): void
89+
{
90+
$this->taskmaster = new Taskmaster();
91+
$this->taskmaster->setDefaultInitTask(InitTask::class);
92+
$this->taskmaster->addWorkers($this->createWorker(), 3);
93+
94+
$this->addTasks(new InitValidateTask(), 3);
95+
$counter = 0;
96+
foreach ($this->taskmaster->waitAndHandleTasks() as $task) {
97+
$this->assertNull($task->getError());
98+
$this->assertTrue($task->getResult());
99+
$counter++;
100+
}
101+
$this->assertEquals(3, $counter);
102+
}
85103
}

test/Util/Task/InitTask.php

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Aternos\Taskmaster\Test\Util\Task;
4+
5+
use Aternos\Taskmaster\Task\OnChild;
6+
use Aternos\Taskmaster\Task\Task;
7+
8+
class InitTask extends Task
9+
{
10+
public static bool $initialized = false;
11+
12+
/**
13+
* @inheritDoc
14+
*/
15+
#[OnChild]
16+
public function run(): void
17+
{
18+
static::$initialized = true;
19+
}
20+
}

test/Util/Task/InitValidateTask.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Aternos\Taskmaster\Test\Util\Task;
4+
5+
use Aternos\Taskmaster\Task\OnChild;
6+
use Aternos\Taskmaster\Task\Task;
7+
8+
class InitValidateTask extends Task
9+
{
10+
/**
11+
* @inheritDoc
12+
*/
13+
#[OnChild]
14+
public function run(): bool
15+
{
16+
return InitTask::$initialized;
17+
}
18+
}

0 commit comments

Comments
 (0)