Skip to content

Commit

Permalink
Merge pull request #6 from hschimpf/1.x-dev
Browse files Browse the repository at this point in the history
ProgressBar with Enhancements for release
  • Loading branch information
hschimpf authored Mar 14, 2023
2 parents 515a2c9 + 5dc81f0 commit 2b54963
Show file tree
Hide file tree
Showing 22 changed files with 1,011 additions and 203 deletions.
87 changes: 79 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,18 @@ composer require hds-solutions/parallel-sdk
```

## Usage
Firstly, you need to set the bootstrap file for parallel. Setting the composer's autoloader is enough. See reference [#1](#references) for more info.
```php
// check if extension is loaded to allow deploying even in envorinments where parallel isn't installed
if (extension_loaded('parallel')) {
// set the path to composer's autoloader
parallel\bootstrap(__DIR__.'/vendor/autoload.php');
}
```

You need to define a `Worker` that will process the tasks. There are two options:
1. Using an anonymous function as a `Worker`.
2. Creating a class that extends from `ParallelWorker` and implements the `processTask()` method.
2. Creating a class that extends from `ParallelWorker` and implements the `process()` method.

Then you can schedule tasks to run in parallel using `Scheduler::runTask()` method.

Expand All @@ -31,7 +40,7 @@ Defining an anonymous function as a `Worker` to process the tasks.
```php
use HDSSolutions\Console\Parallel\Scheduler;

Scheduler::with(static function(int $number): int {
Scheduler::using(static function(int $number): int {
// here you do some work with the received data
// this portion of code will run on a separated thread

Expand All @@ -55,7 +64,7 @@ use HDSSolutions\Console\Parallel\ParallelWorker;

final class ExampleWorker extends ParallelWorker {

protected function processTask(int $number = 0): int {
protected function process(int $number = 0): int {
// example process
$microseconds = random_int(100, 500);
echo sprintf("ExampleWorker >> Hello from task #%u, I'll wait %sms\n", $number, $microseconds);
Expand All @@ -72,7 +81,7 @@ final class ExampleWorker extends ParallelWorker {
use HDSSolutions\Console\Parallel\Scheduler;

$worker = new ExampleWorker();
Scheduler::with($worker);
Scheduler::using($worker);
```

### Schedule tasks
Expand All @@ -87,24 +96,76 @@ foreach (range(1, 100) as $task) {

} catch (Throwable) {
// if no Worker was defined, a RuntimeException will be thrown
// also, Workers have some limitations, see Reference #1 for more info
// also, Workers have some limitations, see Reference #2 for more info
}
}
```

### Get processed tasks result

```php
use HDSSolutions\Console\Parallel\Scheduler;
use HDSSolutions\Console\Parallel\ProcessedTask;

foreach (Scheduler::getProcessedTasks() as $processed_task) {
// you have access to Worker that processed the task
$worker = $processed_task->getWorker();
// you have access to the Worker class that was used to processed the task
$worker = $processed_task->getWorkerClass();
// and the result of the task processed
$result = $processed_task->getResult();
}
```

### ProgressBar

#### Requeriments
- `symfony/console` package
- Enable a ProgressBar for the worker calling the `withProgress()` method.

```php
use HDSSolutions\Console\Parallel\Scheduler;

$tasks = range(1, 10);

$worker = new ExampleWorker();
Scheduler::using($worker)
->withProgress(steps: count($tasks);
```

#### Usage from Worker
Available methods are:
- `setMessage(string $message)`
- `advance(int $steps)`
- `setProgress(int $step)`
- `display()`
- `clear()`

```php
use HDSSolutions\Console\Parallel\ParallelWorker;

final class ExampleWorker extends ParallelWorker {

protected function process(int $number = 0): int {
// example process
$microseconds = random_int(100, 500);
$this->setMessage(sprintf("ExampleWorker >> Hello from task #%u, I'll wait %sms", $number, $microseconds));
usleep($microseconds * 1000);
$this->advance();
// end example process

return $number;
}

}
```

#### Example output
```bash
28 of 52: ExampleWorker >> Hello from task #123, I'll wait 604ms
[===========================================>------------------------------------] 53%
elapsed: 2 secs, remaining: 2 secs, ~13.50 items/s
memory: 562 KiB, threads: 12x ~474 KiB, Σ 5,6 MiB ↑ 5,6 MiB
```

## Graceful close all resources
This method will close all resources used internally by the `Scheduler` instance.
```php
Expand All @@ -114,4 +175,14 @@ Scheduler::disconnect();
```

### References
1. [Parallel\Runtime::run() Task Characteristics](https://www.php.net/manual/en/parallel-runtime.run.php#refsect1-parallel-runtime.run-closure-characteristics)
1. [parallel\bootstrap()](https://www.php.net/manual/en/parallel.bootstrap.php)
2. [Parallel\Runtime::run() Task Characteristics](https://www.php.net/manual/en/parallel-runtime.run.php#refsect1-parallel-runtime.run-closure-characteristics)

# Security Vulnerabilities
If you encounter any security related issue, feel free to raise a ticket on the issue traker.

# Contributors
- [Hermann D. Schimpf](https://hds-solutions.net)

# Licence
GPL-3.0 Please see [License File](LICENSE) for more information.
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
}
],
"suggest": {
"ext-parallel": "Allows to run multi-threaded processes"
"ext-parallel": "Allows to run multi-threaded processes",
"symfony/console": "Allows usage of a shared ProgressBar between the Workers"
},
"require": {
"php": ">=8.0"
Expand All @@ -25,7 +26,8 @@
},
"require-dev": {
"phpunit/phpunit": "^9.6",
"roave/security-advisories": "dev-latest"
"roave/security-advisories": "dev-latest",
"symfony/console": "^6.0"
},
"autoload-dev": {
"psr-4": {
Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="tests/config/bootstrap.php"
bootstrap="vendor/autoload.php"
colors="true">

<testsuites>
Expand Down
101 changes: 101 additions & 0 deletions src/Parallel/Contracts/ParallelWorker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php declare(strict_types=1);

namespace HDSSolutions\Console\Parallel\Contracts;

use HDSSolutions\Console\Parallel\ProcessedTask;

interface ParallelWorker {

/**
* ## Worker has not yet started
*/
public const STATE_New = 0;

/**
* ## Worker is currently running
*/
public const STATE_Running = 1;

/**
* ## Worker has finished execution
*/
public const STATE_Finished = 2;

/**
* ## Available states of the Worker
*/
public const STATES = [
self::STATE_New,
self::STATE_Running,
self::STATE_Finished,
];

/**
* Returns the current state of the Worker
*
* @return int Current Worker state
* @see ParallelWorker::STATES
*/
public function getState(): int;

/**
* Begin execution of this Worker, calling the `process()` method
*
* @param mixed ...$args Task data to pass to the Worker
*/
public function start(...$args): void;

/**
* Associates a text with a named placeholder.
*
* @param string $message The text to associate with the placeholder
* @param string $name The name of the placeholder
*/
public function setMessage(string $message, string $name = 'message'): void;

/**
* Advances the progress output X steps.
*
* @param int $steps Number of steps to advance
*/
public function advance(int $steps = 1): void;

/**
* Moves the progress output to a specific step.
*
* @param int $step Step to move progress to
*/
public function setProgress(int $step): void;

/**
* Outputs the current progress string.
*/
public function display(): void;

/**
* Removes the progress bar from the current line.
*
* This is useful if you wish to write some output
* while a progress bar is running.
* Call display() to show the progress bar again.
*/
public function clear(): void;

/**
* @return ?float Time when Worker started processing the Task, null if Worker didn't start yet
*/
public function getStartedAt(): ?float;

/**
* @return ?float Time when Worker finished processing the Task, null if Worker didn't finish yet
*/
public function getFinishedAt(): ?float;

/**
* Returns the processed task
*
* @return ProcessedTask Processed task
*/
public function getProcessedTask(): ProcessedTask;

}
16 changes: 16 additions & 0 deletions src/Parallel/Internals/Messages/ProgressBarActionMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace HDSSolutions\Console\Parallel\Internals\Messages;

final class ProgressBarActionMessage {

/**
* @param string $action
* @param array $args
*/
public function __construct(
public string $action,
public array $args,
) {}

}
16 changes: 16 additions & 0 deletions src/Parallel/Internals/Messages/ProgressBarRegistrationMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace HDSSolutions\Console\Parallel\Internals\Messages;

final class ProgressBarRegistrationMessage {

/**
* @param string $worker
* @param int $steps
*/
public function __construct(
public string $worker,
public int $steps = 0,
) {}

}
12 changes: 12 additions & 0 deletions src/Parallel/Internals/Messages/StatsReportMessage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php declare(strict_types=1);

namespace HDSSolutions\Console\Parallel\Internals\Messages;

final class StatsReportMessage {

public function __construct(
public string $worker_id,
public int $memory_usage,
) {}

}
30 changes: 30 additions & 0 deletions src/Parallel/Internals/PendingTask.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types=1);

namespace HDSSolutions\Console\Parallel\Internals;

final class PendingTask {

/**
* @param RegisteredWorker $registered_worker Registered Worker that will process this task
* @param mixed $data Data of the Task
*/
public function __construct(
private RegisteredWorker $registered_worker,
private mixed $data = null,
) {}

/**
* @return RegisteredWorker Registered Worker
*/
public function getRegisteredWorker(): RegisteredWorker {
return $this->registered_worker;
}

/**
* @return mixed Data of the Task
*/
public function getData(): mixed {
return $this->data;
}

}
Loading

0 comments on commit 2b54963

Please sign in to comment.