Update README.md, consistent use of spaces instead of tabs, better examples
This commit is contained in:
		
							parent
							
								
									92bc0ab407
								
							
						
					
					
						commit
						db081158d7
					
				
							
								
								
									
										193
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								README.md
									
									
									
									
									
								
							| @ -4,7 +4,7 @@ Welcome to Toalett, a humble initiative based around the idea that all software | |||||||
| Toalett is the Norwegian word for toilet. It feels fancier than plain "toilet". | Toalett is the Norwegian word for toilet. It feels fancier than plain "toilet". | ||||||
| 
 | 
 | ||||||
| ## Why `toalett/multiprocessing`? | ## Why `toalett/multiprocessing`? | ||||||
| [Multiprocessing](https://nl.wikipedia.org/wiki/Multiprocessing) is a technique that is often used in PHP (CLI-)applications to execute tasks asynchronously. | [Multiprocessing](https://nl.wikipedia.org/wiki/Multiprocessing) is a technique that is often used in PHP (cli) applications to execute tasks asynchronously. | ||||||
| Due to the lack of native [multithreading](https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)) in PHP, developers have to rely on | Due to the lack of native [multithreading](https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)) in PHP, developers have to rely on | ||||||
| good old multiprocessing to do this.   | good old multiprocessing to do this.   | ||||||
| 
 | 
 | ||||||
| @ -22,6 +22,56 @@ Workers are a representation of child processes that are working on a task. | |||||||
| The Context uses a [ReactPHP EventLoop](https://reactphp.org/event-loop/) internally  | The Context uses a [ReactPHP EventLoop](https://reactphp.org/event-loop/) internally  | ||||||
| and emits events using the simple (but elegant) [Evenement](https://github.com/igorw/Evenement) library. | and emits events using the simple (but elegant) [Evenement](https://github.com/igorw/Evenement) library. | ||||||
| 
 | 
 | ||||||
|  | ## Events | ||||||
|  | 
 | ||||||
|  | The context emits events when something of interest happens. | ||||||
|  | You can react to these events by calling:   | ||||||
|  | `$context->on('name_of_event', fn() => ...);`. | ||||||
|  | 
 | ||||||
|  | These are the events emitted by the context: | ||||||
|  | 
 | ||||||
|  | 1. `booted` | ||||||
|  | 2. `worker_started` | ||||||
|  | 3. `worker_stopped` | ||||||
|  | 4. `congestion` | ||||||
|  | 5. `congestion_relieved` | ||||||
|  | 6. `no_workers_remaining` | ||||||
|  | 7. `stopped` | ||||||
|  | 
 | ||||||
|  | #### 1. `booted` | ||||||
|  | This event is emitted after `$context->run()` is called. | ||||||
|  | This is the very first event dispatched by the context.  | ||||||
|  | It is dispatched as soon as the event loop has started. | ||||||
|  | 
 | ||||||
|  | #### 2. `worker_started` | ||||||
|  | This event is emitted when a worker has been started (the process has been forked). | ||||||
|  | The PID of the child process is supplied as an argument to a listener. | ||||||
|  | 
 | ||||||
|  | #### 3. `worker_stopped` | ||||||
|  | This event is emitted when a worker has been stopped (child process has stopped). | ||||||
|  | The PID of the child process is supplied as an argument to a listener. | ||||||
|  | 
 | ||||||
|  | #### 4. `congestion` | ||||||
|  | This event is emitted when the imposed concurrency limit is reached. | ||||||
|  | This happens when (for example) the concurrency is set to at most 2 child processes,  | ||||||
|  | and a third task gets submitted while 2 tasks are already running.  | ||||||
|  | The system naively waits for a child to stop before starting another worker. | ||||||
|  | 
 | ||||||
|  | #### 5. `congestion_relieved` | ||||||
|  | This event is emitted when congestion is relieved.  | ||||||
|  | This means that a child has stopped, allowing for the execution of a new task. | ||||||
|  | 
 | ||||||
|  | #### 6. `no_workers_remaining` | ||||||
|  | This event is emitted when there are no workers left running.  | ||||||
|  | This usually means there is no more work to do.  | ||||||
|  | It's possible to automatically stop the context when this event occurs.  | ||||||
|  | This is shown in the first and last example. | ||||||
|  | 
 | ||||||
|  | #### 7. `stopped` | ||||||
|  | The context can be stopped by calling `$context->stop()`. | ||||||
|  | When the workers and the event loop are succesfully stopped, the context | ||||||
|  | emits a `stopped` event. | ||||||
|  | 
 | ||||||
| ## Examples | ## Examples | ||||||
| For most developers, the quickest way to learn something is by looking at examples.  | For most developers, the quickest way to learn something is by looking at examples.  | ||||||
| Three examples are provided. | Three examples are provided. | ||||||
| @ -30,123 +80,87 @@ There is a simple example, which demonstrates event emission with the creation o | |||||||
| A counter is incremented every time a job stops. | A counter is incremented every time a job stops. | ||||||
| When all jobs are done, the context is stopped. | When all jobs are done, the context is stopped. | ||||||
| 
 | 
 | ||||||
| ### [Simple example](bin/simple_example.php) | The cleanup interval is the interval at which the context checks for dead  | ||||||
|  | worker processes and reads their exit codes. | ||||||
|  | It defaults to 5 seconds and is in some examples explicitely set to a low  | ||||||
|  | value to improve example responsiveness. | ||||||
|  | 
 | ||||||
|  | ### [Counting stopped workers using events](bin/counting_stopped_workers.php) | ||||||
| ```php | ```php | ||||||
| <?php | <?php | ||||||
| 
 | 
 | ||||||
| use Toalett\Multiprocessing\ContextBuilder; | use Toalett\Multiprocessing\ContextBuilder; | ||||||
| use Toalett\Multiprocessing\Task\Interval; | use Toalett\Multiprocessing\Task\Interval; | ||||||
| 
 | 
 | ||||||
| require_once '/path/to/autoload.php'; |  | ||||||
| 
 |  | ||||||
| // We will run 50 jobs |  | ||||||
| const NUM_JOBS = 50; | const NUM_JOBS = 50; | ||||||
| 
 | 
 | ||||||
| $counter = new class { |  | ||||||
| 	public int $value = 0; |  | ||||||
| 
 |  | ||||||
| 	public function increment(): void |  | ||||||
| 	{ |  | ||||||
| 		$this->value++; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // Create a context (defaults to unlimited child processes). |  | ||||||
| // The cleanup interval is the interval at dead processes |  | ||||||
| // will be read. For this example it's kept low.  |  | ||||||
| // The default value is 5 seconds. |  | ||||||
| $context = ContextBuilder::create() | $context = ContextBuilder::create() | ||||||
|     ->withCleanupInterval(Interval::seconds(0.5)) |     ->withCleanupInterval(Interval::seconds(0.5)) | ||||||
|     ->build(); |     ->build(); | ||||||
| 
 | 
 | ||||||
|  | $counter = new Counter(); | ||||||
| $context->on('worker_stopped', [$counter, 'increment']); | $context->on('worker_stopped', [$counter, 'increment']); | ||||||
| $context->on('no_workers_remaining', [$context, 'stop']); | $context->on('no_workers_remaining', [$context, 'stop']); | ||||||
| $context->on('stopped', fn() => printf("\nJobs completed: %d\n", $counter->value)); | $context->on('stopped', fn() => printf(" %d\n", $counter->value)); | ||||||
| 
 | 
 | ||||||
| // You can submit jobs before the context is running. They will be executed |  | ||||||
| // in the order in which they are submitted to the context.  |  | ||||||
| // Each job (thus child process) will be sleeping for 3 seconds. |  | ||||||
| for ($i = 0; $i < NUM_JOBS; $i++) { | for ($i = 0; $i < NUM_JOBS; $i++) { | ||||||
| 	$context->submit(fn() => sleep(3)); |     $context->submit(fn() => sleep(2)); | ||||||
|     print('.'); |     print('.'); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| $context->run(); | $context->run(); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ### [More elaborate example](bin/more_elaborate_example.php) | ### [Triggering congestion with 4 workers](bin/triggering_congestion.php) | ||||||
| This example is a bit more elaborate than the previous one. | This example is a bit more elaborate than the previous one. | ||||||
| It serves to demonstrate congestion and how it is handled by the context:  | It serves to demonstrate congestion and how it is handled by the context:  | ||||||
| the context simply blocks all execution until a worker stops and a spot becomes available. | the context simply blocks all execution until a worker stops and a spot becomes available. | ||||||
| 
 | 
 | ||||||
| This example shows the usage of events. | Watch for the occurence of 'C' in the output.  | ||||||
|  | This denotes congestion: a worker could not be started. | ||||||
| ```php | ```php | ||||||
| <?php | <?php | ||||||
| 
 | 
 | ||||||
|  | use React\EventLoop\Factory; | ||||||
| use Toalett\Multiprocessing\ContextBuilder; | use Toalett\Multiprocessing\ContextBuilder; | ||||||
| use Toalett\Multiprocessing\ConcurrencyLimit; | use Toalett\Multiprocessing\Concurrency; | ||||||
| use React\EventLoop\Factory as EventLoopFactory; |  | ||||||
| 
 | 
 | ||||||
| require_once '/path/to/autoload.php'; | $loop = Factory::create(); | ||||||
| 
 |  | ||||||
| // Create our own EventLoop and limit and supply them to the builder |  | ||||||
| $loop = EventLoopFactory::create(); |  | ||||||
| $context = ContextBuilder::create() | $context = ContextBuilder::create() | ||||||
|     ->withEventLoop($loop) |     ->withEventLoop($loop) | ||||||
| 	->withLimit(ConcurrencyLimit::atMost(4)) |     ->withConcurrency(Concurrency::atMost(4)) | ||||||
|     ->build(); |     ->build(); | ||||||
| 
 | 
 | ||||||
| $context->on('booted', fn() => print("🚽 Toalett Multiprocessing Context\n")); | $context->on('booted', fn() => print("🚽 toalett context booted\n")); | ||||||
| $context->on('congestion', fn() => print('C')); | $context->on('congestion', fn() => print('C')); | ||||||
| $context->on('congestion_relieved', fn() => print('R')); | $context->on('congestion_relieved', fn() => print('R')); | ||||||
| $context->on('worker_started', fn() => print('+')); | $context->on('worker_started', fn() => print('+')); | ||||||
| $context->on('worker_stopped', fn() => print('-')); | $context->on('worker_stopped', fn() => print('-')); | ||||||
| 
 | 
 | ||||||
| // Submit a fake job every second | // A job is submitted to the context every second. | ||||||
|  | // The job sleeps for a random amount of seconds (0 - 10). | ||||||
| $loop->addPeriodicTimer(1, fn() => $context->submit(fn(int $s) => sleep($s), random_int(0, 10))); | $loop->addPeriodicTimer(1, fn() => $context->submit(fn(int $s) => sleep($s), random_int(0, 10))); | ||||||
| 
 | 
 | ||||||
| print("Press CTRL+C to stop.\n"); | print("Press CTRL+C to stop.\n"); | ||||||
| $context->run(); | $context->run(); | ||||||
| 
 |  | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ### [Example with a Job class](bin/example_with_job_class.php) | ### [Single worker with a Job class](bin/single_worker_with_job_class.php) | ||||||
| Since the task is defined by a `callable` supplied with arguments, it's also possible to  | Since a task is really just a `Closure`, it's also possible to submit an object  | ||||||
| define a class that implements the magic `__invoke()` method and submit objects of this  | with an implementation of the `__invoke()` magic method.  | ||||||
| class to the Context. Objects implementing the `__invoke()` method can be treated as  | 
 | ||||||
| closures. They may accept zero or more arguments.  | In this example, execution is limited to a single worker, and jobs are  | ||||||
|  | instances of the `Job` class. | ||||||
| 
 | 
 | ||||||
| This idea is demonstrated here, while execution is limited to a single worker. |  | ||||||
| ```php | ```php | ||||||
| <?php | <?php | ||||||
| 
 | 
 | ||||||
| use Toalett\Multiprocessing\ConcurrencyLimit; | use Toalett\Multiprocessing\Concurrency; | ||||||
| use Toalett\Multiprocessing\ContextBuilder; | use Toalett\Multiprocessing\ContextBuilder; | ||||||
| use Toalett\Multiprocessing\Task\Interval; | use Toalett\Multiprocessing\Task\Interval; | ||||||
| 
 | 
 | ||||||
| require_once '/path/to/vendor/autoload.php'; |  | ||||||
| 
 |  | ||||||
| class Job |  | ||||||
| { |  | ||||||
| 	private string $title; |  | ||||||
| 
 |  | ||||||
| 	public function __construct(string $title) |  | ||||||
| 	{ |  | ||||||
| 		$this->title = $title; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public function __invoke() |  | ||||||
| 	{ |  | ||||||
| 		cli_set_process_title("php {$this->title}"); |  | ||||||
| 		print("+ {$this->title}"); |  | ||||||
| 		sleep(1); |  | ||||||
| 		print("\r {$this->title}\n"); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| $limit = ConcurrencyLimit::singleWorker(); |  | ||||||
| $context = ContextBuilder::create() | $context = ContextBuilder::create() | ||||||
| 	->withLimit(ConcurrencyLimit::singleWorker()) |     ->withConcurrency(Concurrency::singleWorker()) | ||||||
|     ->withCleanupInterval(Interval::seconds(0.2)) |     ->withCleanupInterval(Interval::seconds(0.2)) | ||||||
|     ->build(); |     ->build(); | ||||||
| 
 | 
 | ||||||
| @ -159,52 +173,5 @@ $context->on('no_workers_remaining', [$context, 'stop']); | |||||||
| $context->run(); | $context->run(); | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Events | ## Tests | ||||||
| 
 | Tests can be found in the [src/Tests/](src/Tests) directory. | ||||||
| 1. `booted` |  | ||||||
| 1. `worker_started` |  | ||||||
| 1. `worker_stopped` |  | ||||||
| 1. `congestion` |  | ||||||
| 1. `congestion_relieved` |  | ||||||
| 1. `no_workers_remaining` |  | ||||||
| 1. `stopped` |  | ||||||
| 
 |  | ||||||
| These events are emitted by the context.  |  | ||||||
| They can be subscribed to by calling `$context->on('...', fn() => ...);`. |  | ||||||
| 
 |  | ||||||
| #### `booted` |  | ||||||
| This event is emitted when `$context->run()` is called. |  | ||||||
| This is the very first event dispatched by the context. |  | ||||||
| 
 |  | ||||||
| #### `worker_started` |  | ||||||
| This event is emitted when a worker has been started (the process has been forked). |  | ||||||
| The PID of the child process is supplied as an argument to a listener. |  | ||||||
| 
 |  | ||||||
| #### `worker_stopped` |  | ||||||
| This event is emitted when a worker has been stopped (child process has stopped). |  | ||||||
| The PID of the child process is supplied as an argument to a listener. |  | ||||||
| 
 |  | ||||||
| #### `congestion` |  | ||||||
| This event is emitted when the imposed concurrency limit is reached, for example,  |  | ||||||
| when the limit is set to at most 2 child processes, and a third task gets submitted |  | ||||||
| while there are already two tasks running.  |  | ||||||
| The system naively waits for a child to stop before starting another worker. |  | ||||||
| 
 |  | ||||||
| #### `congestion_relieved` |  | ||||||
| This event is emitted in case the congestion explained above is relieved.  |  | ||||||
| This means that a child has stopped, allowing for the execution of a new task. |  | ||||||
| 
 |  | ||||||
| #### `no_workers_remaining` |  | ||||||
| This event is emitted when there are no workers left running.  |  | ||||||
| This usually means there is no more work to do.  |  | ||||||
| It's possible to automatically stop the context when this event occurs.  |  | ||||||
| This is shown in the first and last example. |  | ||||||
| 
 |  | ||||||
| #### `stopped` |  | ||||||
| This event is emitted when `$context->stop()` is called and the eventloop has |  | ||||||
| succesfully been stopped. |  | ||||||
| 
 |  | ||||||
| ## Why no shared memory? |  | ||||||
| Shared memory in PHP is hard to manage and quickly becomes a mess. Don't ask. |  | ||||||
| 
 |  | ||||||
| Feel free to add it yourself though. 😉 |  | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								bin/classes/Counter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								bin/classes/Counter.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | class Counter | ||||||
|  | { | ||||||
|  |     public int $value = 0; | ||||||
|  | 
 | ||||||
|  |     public function increment(): void | ||||||
|  |     { | ||||||
|  |         $this->value++; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								bin/classes/Job.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								bin/classes/Job.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | class Job | ||||||
|  | { | ||||||
|  |     private string $title; | ||||||
|  | 
 | ||||||
|  |     public function __construct(string $title) | ||||||
|  |     { | ||||||
|  |         $this->title = $title; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function __invoke() | ||||||
|  |     { | ||||||
|  |         cli_set_process_title("php {$this->title}"); | ||||||
|  |         print("* {$this->title}"); | ||||||
|  |         sleep(1); | ||||||
|  |         print("\r  {$this->title}\n"); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -4,28 +4,20 @@ use Toalett\Multiprocessing\ContextBuilder; | |||||||
| use Toalett\Multiprocessing\Task\Interval; | use Toalett\Multiprocessing\Task\Interval; | ||||||
| 
 | 
 | ||||||
| require_once __DIR__ . '/../vendor/autoload.php'; | require_once __DIR__ . '/../vendor/autoload.php'; | ||||||
| 
 | require_once __DIR__ . '/classes/Counter.php'; | ||||||
| const NUM_JOBS = 50; | const NUM_JOBS = 50; | ||||||
| 
 | 
 | ||||||
| $counter = new class { |  | ||||||
| 	public int $value = 0; |  | ||||||
| 
 |  | ||||||
| 	public function increment(): void |  | ||||||
| 	{ |  | ||||||
| 		$this->value++; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| $context = ContextBuilder::create() | $context = ContextBuilder::create() | ||||||
|     ->withCleanupInterval(Interval::seconds(0.5)) |     ->withCleanupInterval(Interval::seconds(0.5)) | ||||||
|     ->build(); |     ->build(); | ||||||
| 
 | 
 | ||||||
|  | $counter = new Counter(); | ||||||
| $context->on('worker_stopped', [$counter, 'increment']); | $context->on('worker_stopped', [$counter, 'increment']); | ||||||
| $context->on('no_workers_remaining', [$context, 'stop']); | $context->on('no_workers_remaining', [$context, 'stop']); | ||||||
| $context->on('stopped', fn() => printf("\nJobs completed: %d\n", $counter->value)); | $context->on('stopped', fn() => printf(" %d\n", $counter->value)); | ||||||
| 
 | 
 | ||||||
| for ($i = 0; $i < NUM_JOBS; $i++) { | for ($i = 0; $i < NUM_JOBS; $i++) { | ||||||
| 	$context->submit(fn() => sleep(3)); |     $context->submit(fn() => sleep(2)); | ||||||
|     print('.'); |     print('.'); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -1,39 +0,0 @@ | |||||||
| <?php |  | ||||||
| 
 |  | ||||||
| use Toalett\Multiprocessing\ConcurrencyLimit; |  | ||||||
| use Toalett\Multiprocessing\ContextBuilder; |  | ||||||
| use Toalett\Multiprocessing\Task\Interval; |  | ||||||
| 
 |  | ||||||
| require_once __DIR__ . '/../vendor/autoload.php'; |  | ||||||
| 
 |  | ||||||
| class Job |  | ||||||
| { |  | ||||||
| 	private string $title; |  | ||||||
| 
 |  | ||||||
| 	public function __construct(string $title) |  | ||||||
| 	{ |  | ||||||
| 		$this->title = $title; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public function __invoke() |  | ||||||
| 	{ |  | ||||||
| 		cli_set_process_title("php {$this->title}"); |  | ||||||
| 		print("+ {$this->title}"); |  | ||||||
| 		sleep(1); |  | ||||||
| 		print("\r {$this->title}\n"); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| $limit = ConcurrencyLimit::singleWorker(); |  | ||||||
| $context = ContextBuilder::create() |  | ||||||
| 	->withLimit(ConcurrencyLimit::singleWorker()) |  | ||||||
| 	->withCleanupInterval(Interval::seconds(0.2)) |  | ||||||
| 	->build(); |  | ||||||
| 
 |  | ||||||
| for ($i = 0; $i < 3; $i++) { |  | ||||||
| 	$title = md5(mt_rand()); |  | ||||||
| 	$context->submit(new Job($title)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| $context->on('no_workers_remaining', [$context, 'stop']); |  | ||||||
| $context->run(); |  | ||||||
							
								
								
									
										21
									
								
								bin/single_worker_with_job_class.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								bin/single_worker_with_job_class.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | use Toalett\Multiprocessing\Concurrency; | ||||||
|  | use Toalett\Multiprocessing\ContextBuilder; | ||||||
|  | use Toalett\Multiprocessing\Task\Interval; | ||||||
|  | 
 | ||||||
|  | require_once __DIR__ . '/../vendor/autoload.php'; | ||||||
|  | require_once __DIR__ . '/classes/Job.php'; | ||||||
|  | 
 | ||||||
|  | $context = ContextBuilder::create() | ||||||
|  |     ->withConcurrency(Concurrency::singleWorker()) | ||||||
|  |     ->withCleanupInterval(Interval::seconds(0.2)) | ||||||
|  |     ->build(); | ||||||
|  | 
 | ||||||
|  | for ($i = 0; $i < 3; $i++) { | ||||||
|  |     $title = md5(mt_rand()); | ||||||
|  |     $context->submit(new Job($title)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | $context->on('no_workers_remaining', [$context, 'stop']); | ||||||
|  | $context->run(); | ||||||
| @ -1,15 +1,15 @@ | |||||||
| <?php | <?php | ||||||
| 
 | 
 | ||||||
|  | use React\EventLoop\Factory; | ||||||
|  | use Toalett\Multiprocessing\Concurrency; | ||||||
| use Toalett\Multiprocessing\ContextBuilder; | use Toalett\Multiprocessing\ContextBuilder; | ||||||
| use Toalett\Multiprocessing\ConcurrencyLimit; |  | ||||||
| use React\EventLoop\Factory as EventLoopFactory; |  | ||||||
| 
 | 
 | ||||||
| require_once __DIR__ . '/../vendor/autoload.php'; | require_once __DIR__ . '/../vendor/autoload.php'; | ||||||
| 
 | 
 | ||||||
| $loop = EventLoopFactory::create(); | $loop = Factory::create(); | ||||||
| $context = ContextBuilder::create() | $context = ContextBuilder::create() | ||||||
|     ->withEventLoop($loop) |     ->withEventLoop($loop) | ||||||
| 	->withLimit(ConcurrencyLimit::atMost(4)) |     ->withConcurrency(Concurrency::atMost(4)) | ||||||
|     ->build(); |     ->build(); | ||||||
| 
 | 
 | ||||||
| $context->on('booted', fn() => print("🚽 Toalett Multiprocessing Context\n")); | $context->on('booted', fn() => print("🚽 Toalett Multiprocessing Context\n")); | ||||||
| @ -4,7 +4,7 @@ namespace Toalett\Multiprocessing; | |||||||
| 
 | 
 | ||||||
| use Toalett\Multiprocessing\Exception\InvalidArgumentException; | use Toalett\Multiprocessing\Exception\InvalidArgumentException; | ||||||
| 
 | 
 | ||||||
| class ConcurrencyLimit | class Concurrency | ||||||
| { | { | ||||||
|     private const VALUE_UNLIMITED = -1; |     private const VALUE_UNLIMITED = -1; | ||||||
|     private int $limit; |     private int $limit; | ||||||
| @ -16,23 +16,22 @@ class Context implements EventEmitterInterface | |||||||
|     use EventEmitterTrait; |     use EventEmitterTrait; | ||||||
| 
 | 
 | ||||||
|     private LoopInterface $eventLoop; |     private LoopInterface $eventLoop; | ||||||
| 	private ConcurrencyLimit $limit; |     private Concurrency $concurrency; | ||||||
|     private Workers $workers; |     private Workers $workers; | ||||||
|     private Tasks $maintenanceTasks; |     private Tasks $maintenanceTasks; | ||||||
| 
 | 
 | ||||||
|     public function __construct( |     public function __construct( | ||||||
|         LoopInterface $eventLoop, |         LoopInterface $eventLoop, | ||||||
| 		ConcurrencyLimit $limit, |         Concurrency $concurrency, | ||||||
|         ?Workers $workers = null, |         ?Workers $workers = null, | ||||||
| 		?Interval $cleanupInterval = null, |         ?Interval $cleanupInterval = null | ||||||
| 		?Interval $garbageCollectionInterval = null |  | ||||||
|     ) |     ) | ||||||
|     { |     { | ||||||
|         $this->eventLoop = $eventLoop; |         $this->eventLoop = $eventLoop; | ||||||
| 		$this->limit = $limit; |         $this->concurrency = $concurrency; | ||||||
|         $this->workers = $workers ?? new Workers(); |         $this->workers = $workers ?? new Workers(); | ||||||
|         $this->setupWorkerEventForwarding(); |         $this->setupWorkerEventForwarding(); | ||||||
| 		$this->setupMaintenanceTasks($cleanupInterval, $garbageCollectionInterval); |         $this->setupMaintenanceTasks($cleanupInterval); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function run(): void |     public function run(): void | ||||||
| @ -46,7 +45,7 @@ class Context implements EventEmitterInterface | |||||||
|     public function submit(callable $task, ...$args): void |     public function submit(callable $task, ...$args): void | ||||||
|     { |     { | ||||||
|         $this->eventLoop->futureTick(function () use ($task, $args) { |         $this->eventLoop->futureTick(function () use ($task, $args) { | ||||||
| 			if ($this->limit->isReachedBy(count($this->workers))) { |             if ($this->concurrency->isReachedBy(count($this->workers))) { | ||||||
|                 $this->emit('congestion'); |                 $this->emit('congestion'); | ||||||
|                 $this->workers->awaitCongestionRelief(); |                 $this->workers->awaitCongestionRelief(); | ||||||
|                 $this->emit('congestion_relieved'); |                 $this->emit('congestion_relieved'); | ||||||
| @ -69,17 +68,13 @@ class Context implements EventEmitterInterface | |||||||
|         $this->workers->on('no_workers_remaining', fn() => $this->emit('no_workers_remaining')); |         $this->workers->on('no_workers_remaining', fn() => $this->emit('no_workers_remaining')); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 	private function setupMaintenanceTasks(?Interval $cleanupInterval, ?Interval $garbageCollectionInterval): void |     private function setupMaintenanceTasks(?Interval $cleanupInterval): void | ||||||
|     { |     { | ||||||
|  |         $cleanupInterval = $cleanupInterval ?? Interval::seconds(self::INTERVAL_CLEANUP); | ||||||
|  |         $gcInterval = Interval::seconds(self::INTERVAL_GC); | ||||||
|         $this->maintenanceTasks = new Tasks( |         $this->maintenanceTasks = new Tasks( | ||||||
| 			new RepeatedTask( |             new RepeatedTask($cleanupInterval, [$this->workers, 'cleanup']), | ||||||
| 				$cleanupInterval ?? Interval::seconds(self::INTERVAL_CLEANUP), |             new RepeatedTask($gcInterval, 'gc_collect_cycles') | ||||||
| 				fn() => $this->workers->cleanup() |  | ||||||
| 			), |  | ||||||
| 			new RepeatedTask( |  | ||||||
| 				$garbageCollectionInterval ?? Interval::seconds(self::INTERVAL_GC), |  | ||||||
| 				fn() => gc_collect_cycles() |  | ||||||
| 			) |  | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,9 +9,8 @@ use Toalett\Multiprocessing\Task\Interval; | |||||||
| class ContextBuilder | class ContextBuilder | ||||||
| { | { | ||||||
|     private ?LoopInterface $loop = null; |     private ?LoopInterface $loop = null; | ||||||
| 	private ?ConcurrencyLimit $limit = null; |     private ?Concurrency $concurrency = null; | ||||||
|     private ?Workers $workers = null; |     private ?Workers $workers = null; | ||||||
| 	private ?Interval $garbageCollectionInterval = null; |  | ||||||
|     private ?Interval $cleanupInterval = null; |     private ?Interval $cleanupInterval = null; | ||||||
| 
 | 
 | ||||||
|     public static function create(): self |     public static function create(): self | ||||||
| @ -26,10 +25,10 @@ class ContextBuilder | |||||||
|         return $instance; |         return $instance; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 	public function withLimit(ConcurrencyLimit $limit): self |     public function withConcurrency(Concurrency $concurrency): self | ||||||
|     { |     { | ||||||
|         $instance = clone $this; |         $instance = clone $this; | ||||||
| 		$instance->limit = $limit; |         $instance->concurrency = $concurrency; | ||||||
|         return $instance; |         return $instance; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -40,13 +39,6 @@ class ContextBuilder | |||||||
|         return $instance; |         return $instance; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 	public function withGarbageCollectionInterval(Interval $interval): self |  | ||||||
| 	{ |  | ||||||
| 		$instance = clone $this; |  | ||||||
| 		$instance->garbageCollectionInterval = $interval; |  | ||||||
| 		return $instance; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
|     public function withCleanupInterval(Interval $interval): self |     public function withCleanupInterval(Interval $interval): self | ||||||
|     { |     { | ||||||
|         $instance = clone $this; |         $instance = clone $this; | ||||||
| @ -58,10 +50,9 @@ class ContextBuilder | |||||||
|     { |     { | ||||||
|         return new Context( |         return new Context( | ||||||
|             $this->loop ?? Factory::create(), |             $this->loop ?? Factory::create(), | ||||||
| 			$this->limit ?? ConcurrencyLimit::unlimited(), |             $this->concurrency ?? Concurrency::unlimited(), | ||||||
|             $this->workers, |             $this->workers, | ||||||
| 			$this->cleanupInterval, |             $this->cleanupInterval | ||||||
| 			$this->garbageCollectionInterval |  | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,88 +0,0 @@ | |||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace Toalett\Multiprocessing\Tests; |  | ||||||
| 
 |  | ||||||
| use PHPUnit\Framework\TestCase; |  | ||||||
| use Toalett\Multiprocessing\ConcurrencyLimit; |  | ||||||
| use Toalett\Multiprocessing\Exception\InvalidArgumentException; |  | ||||||
| use Toalett\Multiprocessing\Tests\Tools\PropertyInspector; |  | ||||||
| 
 |  | ||||||
| class ConcurrencyLimitTest extends TestCase |  | ||||||
| { |  | ||||||
| 	use PropertyInspector; |  | ||||||
| 
 |  | ||||||
| 	public function testItDoesNotAcceptZero(): void |  | ||||||
| 	{ |  | ||||||
| 		$this->expectException(InvalidArgumentException::class); |  | ||||||
| 		$this->expectExceptionMessage('Expected -1 or positive integer, got \'0\''); |  | ||||||
| 
 |  | ||||||
| 		ConcurrencyLimit::atMost(0); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public function testItAcceptsNegativeOneAsUnlimited(): void |  | ||||||
| 	{ |  | ||||||
| 		$limit = ConcurrencyLimit::atMost(-1); |  | ||||||
| 
 |  | ||||||
| 		self::assertTrue($limit->isUnlimited()); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * @param int $negativeNumber |  | ||||||
| 	 * @dataProvider negativeValueProvider |  | ||||||
| 	 */ |  | ||||||
| 	public function testItDoesNotAllowAnyOtherNegativeValue(int $negativeNumber): void |  | ||||||
| 	{ |  | ||||||
| 		$this->expectException(InvalidArgumentException::class); |  | ||||||
| 		$this->expectExceptionMessage(sprintf('Expected -1 or positive integer, got \'%s\'', $negativeNumber)); |  | ||||||
| 
 |  | ||||||
| 		ConcurrencyLimit::atMost($negativeNumber); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public function testTheLimitMayBeUnlimited(): void |  | ||||||
| 	{ |  | ||||||
| 		$limit = ConcurrencyLimit::unlimited(); |  | ||||||
| 
 |  | ||||||
| 		self::assertTrue($limit->isUnlimited()); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public function testTheLimitMayBeASingleWorker(): void |  | ||||||
| 	{ |  | ||||||
| 		$limit = ConcurrencyLimit::singleWorker(); |  | ||||||
| 
 |  | ||||||
| 		self::assertFalse($limit->isUnlimited()); |  | ||||||
| 		self::assertEquals(1, $this->getProperty($limit, 'limit')); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public function testAnUnlimitedLimitCanNeverBeReached(): void |  | ||||||
| 	{ |  | ||||||
| 		$limit = ConcurrencyLimit::unlimited(); |  | ||||||
| 
 |  | ||||||
| 		self::assertFalse($limit->isReachedBy(PHP_INT_MIN)); |  | ||||||
| 		self::assertFalse($limit->isReachedBy(0)); |  | ||||||
| 		self::assertFalse($limit->isReachedBy(PHP_INT_MAX)); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public function testABoundLimitCanBeReached(): void |  | ||||||
| 	{ |  | ||||||
| 		$three = ConcurrencyLimit::atMost(3); |  | ||||||
| 		$seven = ConcurrencyLimit::atMost(7); |  | ||||||
| 
 |  | ||||||
| 		self::assertTrue($three->isReachedBy(3)); |  | ||||||
| 		self::assertFalse($three->isReachedBy(2)); |  | ||||||
| 		self::assertFalse($three->isReachedBy(1)); |  | ||||||
| 
 |  | ||||||
| 		self::assertTrue($seven->isReachedBy(7)); |  | ||||||
| 		self::assertTrue($seven->isReachedBy(120)); |  | ||||||
| 		self::assertFalse($seven->isReachedBy(-2)); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	public function negativeValueProvider(): array |  | ||||||
| 	{ |  | ||||||
| 		return [ |  | ||||||
| 			'-2'          => [-2], |  | ||||||
| 			'-3'          => [-3], |  | ||||||
| 			'-10000'      => [-10000], |  | ||||||
| 			'PHP_INT_MIN' => [PHP_INT_MIN], |  | ||||||
| 		]; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										87
									
								
								src/Tests/ConcurrencyTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/Tests/ConcurrencyTest.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Toalett\Multiprocessing\Tests; | ||||||
|  | 
 | ||||||
|  | use PHPUnit\Framework\TestCase; | ||||||
|  | use Toalett\Multiprocessing\Concurrency; | ||||||
|  | use Toalett\Multiprocessing\Exception\InvalidArgumentException; | ||||||
|  | use Toalett\Multiprocessing\Tests\Tools\PropertyInspector; | ||||||
|  | 
 | ||||||
|  | class ConcurrencyTest extends TestCase | ||||||
|  | { | ||||||
|  |     use PropertyInspector; | ||||||
|  | 
 | ||||||
|  |     public function testItAcceptsNegativeOneAsUnlimited(): void | ||||||
|  |     { | ||||||
|  |         $concurrency = Concurrency::atMost(-1); | ||||||
|  | 
 | ||||||
|  |         self::assertTrue($concurrency->isUnlimited()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testItDoesNotAcceptZero(): void | ||||||
|  |     { | ||||||
|  |         $this->expectException(InvalidArgumentException::class); | ||||||
|  |         $this->expectExceptionMessage('Expected -1 or positive integer, got \'0\''); | ||||||
|  | 
 | ||||||
|  |         Concurrency::atMost(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @param int $negativeNumber | ||||||
|  |      * @dataProvider negativeValueProvider | ||||||
|  |      */ | ||||||
|  |     public function testItDoesNotAllowAnyOtherNegativeValue(int $negativeNumber): void | ||||||
|  |     { | ||||||
|  |         $this->expectException(InvalidArgumentException::class); | ||||||
|  |         $this->expectExceptionMessage(sprintf('Expected -1 or positive integer, got \'%s\'', $negativeNumber)); | ||||||
|  | 
 | ||||||
|  |         Concurrency::atMost($negativeNumber); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testTheLimitMayBeUnlimited(): void | ||||||
|  |     { | ||||||
|  |         $concurrency = Concurrency::unlimited(); | ||||||
|  |         self::assertTrue($concurrency->isUnlimited()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testTheLimitMayBeASingleWorker(): void | ||||||
|  |     { | ||||||
|  |         $concurrency = Concurrency::singleWorker(); | ||||||
|  | 
 | ||||||
|  |         self::assertFalse($concurrency->isUnlimited()); | ||||||
|  |         self::assertEquals(1, $this->getProperty($concurrency, 'limit')); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testAnUnlimitedLimitCanNeverBeReached(): void | ||||||
|  |     { | ||||||
|  |         $concurrency = Concurrency::unlimited(); | ||||||
|  | 
 | ||||||
|  |         self::assertFalse($concurrency->isReachedBy(PHP_INT_MIN)); | ||||||
|  |         self::assertFalse($concurrency->isReachedBy(0)); | ||||||
|  |         self::assertFalse($concurrency->isReachedBy(PHP_INT_MAX)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function testABoundLimitCanBeReached(): void | ||||||
|  |     { | ||||||
|  |         $three = Concurrency::atMost(3); | ||||||
|  |         $seven = Concurrency::atMost(7); | ||||||
|  | 
 | ||||||
|  |         self::assertTrue($three->isReachedBy(3)); | ||||||
|  |         self::assertFalse($three->isReachedBy(2)); | ||||||
|  |         self::assertFalse($three->isReachedBy(1)); | ||||||
|  | 
 | ||||||
|  |         self::assertTrue($seven->isReachedBy(7)); | ||||||
|  |         self::assertTrue($seven->isReachedBy(120)); | ||||||
|  |         self::assertFalse($seven->isReachedBy(-2)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function negativeValueProvider(): array | ||||||
|  |     { | ||||||
|  |         return [ | ||||||
|  |             '-2'          => [-2], | ||||||
|  |             '-3'          => [-3], | ||||||
|  |             '-10000'      => [-10000], | ||||||
|  |             'PHP_INT_MIN' => [PHP_INT_MIN], | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -4,7 +4,7 @@ namespace Toalett\Multiprocessing\Tests; | |||||||
| 
 | 
 | ||||||
| use PHPUnit\Framework\TestCase; | use PHPUnit\Framework\TestCase; | ||||||
| use React\EventLoop\LoopInterface; | use React\EventLoop\LoopInterface; | ||||||
| use Toalett\Multiprocessing\ConcurrencyLimit; | use Toalett\Multiprocessing\Concurrency; | ||||||
| use Toalett\Multiprocessing\ContextBuilder; | use Toalett\Multiprocessing\ContextBuilder; | ||||||
| use Toalett\Multiprocessing\Tests\Tools\PropertyInspector; | use Toalett\Multiprocessing\Tests\Tools\PropertyInspector; | ||||||
| use Toalett\Multiprocessing\Workers; | use Toalett\Multiprocessing\Workers; | ||||||
| @ -17,10 +17,10 @@ class ContextBuilderTest extends TestCase | |||||||
|     { |     { | ||||||
|         $builder = ContextBuilder::create(); |         $builder = ContextBuilder::create(); | ||||||
|         $eventLoop = $this->createMock(LoopInterface::class); |         $eventLoop = $this->createMock(LoopInterface::class); | ||||||
| 		$limit = $this->createMock(ConcurrencyLimit::class); |         $concurrency = $this->createMock(Concurrency::class); | ||||||
| 
 | 
 | ||||||
|         self::assertNotSame($builder->withEventLoop($eventLoop), $builder); |         self::assertNotSame($builder->withEventLoop($eventLoop), $builder); | ||||||
| 		self::assertNotSame($builder->withLimit($limit), $builder); |         self::assertNotSame($builder->withConcurrency($concurrency), $builder); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testItBuildsANewContextEveryTime(): void |     public function testItBuildsANewContextEveryTime(): void | ||||||
| @ -30,7 +30,7 @@ class ContextBuilderTest extends TestCase | |||||||
|         self::assertNotSame($builder->build(), $builder->build()); |         self::assertNotSame($builder->build(), $builder->build()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 	public function testTheDefaultConcurrencyLimitIsUnlimited(): void |     public function testTheDefaultConcurrencyIsUnlimited(): void | ||||||
|     { |     { | ||||||
|         $builder = ContextBuilder::create(); |         $builder = ContextBuilder::create(); | ||||||
| 
 | 
 | ||||||
| @ -38,11 +38,11 @@ class ContextBuilderTest extends TestCase | |||||||
|         self::assertIsObject($context); |         self::assertIsObject($context); | ||||||
|         self::assertInstanceOf(LoopInterface::class, $this->getProperty($context, 'eventLoop')); |         self::assertInstanceOf(LoopInterface::class, $this->getProperty($context, 'eventLoop')); | ||||||
| 
 | 
 | ||||||
| 		/** @var ConcurrencyLimit|null $limit */ |         /** @var Concurrency|null $concurrency */ | ||||||
| 		$limit = $this->getProperty($context, 'limit'); |         $concurrency = $this->getProperty($context, 'concurrency'); | ||||||
| 		self::assertIsObject($limit); |         self::assertIsObject($concurrency); | ||||||
| 		self::assertInstanceOf(ConcurrencyLimit::class, $limit); |         self::assertInstanceOf(Concurrency::class, $concurrency); | ||||||
| 		self::assertTrue($limit->isUnlimited()); |         self::assertTrue($concurrency->isUnlimited()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testWhenGivenAnEventLoopItUsesThatLoop(): void |     public function testWhenGivenAnEventLoopItUsesThatLoop(): void | ||||||
| @ -56,15 +56,15 @@ class ContextBuilderTest extends TestCase | |||||||
|         self::assertSame($eventLoop, $usedEventLoop); |         self::assertSame($eventLoop, $usedEventLoop); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 	public function testWhenGivenAConcurrencyLimitItUsesThatLimit(): void |     public function testWhenGivenAConcurrencyItUsesThatConcurrency(): void | ||||||
|     { |     { | ||||||
|         $builder = ContextBuilder::create(); |         $builder = ContextBuilder::create(); | ||||||
| 		$limit = $this->createMock(ConcurrencyLimit::class); |         $concurrency = $this->createMock(Concurrency::class); | ||||||
| 
 | 
 | ||||||
| 		$context = $builder->withLimit($limit)->build(); |         $context = $builder->withConcurrency($concurrency)->build(); | ||||||
| 		$usedLimit = $this->getProperty($context, 'limit'); |         $usedConcurrency = $this->getProperty($context, 'concurrency'); | ||||||
| 
 | 
 | ||||||
| 		self::assertSame($limit, $usedLimit); |         self::assertSame($concurrency, $usedConcurrency); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testWhenGivenWorkersItUsesThatWorkers(): void |     public function testWhenGivenWorkersItUsesThatWorkers(): void | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ use PHPUnit\Framework\TestCase; | |||||||
| use React\EventLoop\Factory; | use React\EventLoop\Factory; | ||||||
| use React\EventLoop\LoopInterface; | use React\EventLoop\LoopInterface; | ||||||
| use React\EventLoop\Timer\Timer; | use React\EventLoop\Timer\Timer; | ||||||
| use Toalett\Multiprocessing\ConcurrencyLimit; | use Toalett\Multiprocessing\Concurrency; | ||||||
| use Toalett\Multiprocessing\Context; | use Toalett\Multiprocessing\Context; | ||||||
| use Toalett\Multiprocessing\Workers; | use Toalett\Multiprocessing\Workers; | ||||||
| 
 | 
 | ||||||
| @ -14,9 +14,9 @@ class ContextTest extends TestCase | |||||||
| { | { | ||||||
|     public function testItEmitsAnEventWhenBooted(): void |     public function testItEmitsAnEventWhenBooted(): void | ||||||
|     { |     { | ||||||
| 		$limit = $this->createMock(ConcurrencyLimit::class); |         $concurrency = $this->createMock(Concurrency::class); | ||||||
|         $loop = Factory::create(); |         $loop = Factory::create(); | ||||||
| 		$context = new Context($loop, $limit); |         $context = new Context($loop, $concurrency); | ||||||
| 
 | 
 | ||||||
|         $loop->futureTick(fn() => $context->stop()); |         $loop->futureTick(fn() => $context->stop()); | ||||||
| 
 | 
 | ||||||
| @ -33,10 +33,10 @@ class ContextTest extends TestCase | |||||||
|     public function testItEmitsEventsWhenCongestionOccursAndIsRelieved(): void |     public function testItEmitsEventsWhenCongestionOccursAndIsRelieved(): void | ||||||
|     { |     { | ||||||
|         $loop = Factory::create(); |         $loop = Factory::create(); | ||||||
| 		$limit = $this->createMock(ConcurrencyLimit::class); |         $concurrency = $this->createMock(Concurrency::class); | ||||||
| 		$context = new Context($loop, $limit); |         $context = new Context($loop, $concurrency); | ||||||
| 
 | 
 | ||||||
| 		$limit->method('isReachedBy')->willReturn(true); // trigger congestion
 |         $concurrency->method('isReachedBy')->willReturn(true); // trigger congestion
 | ||||||
| 
 | 
 | ||||||
|         $congestionEventHasTakenPlace = false; |         $congestionEventHasTakenPlace = false; | ||||||
|         $context->on('congestion', function () use (&$congestionEventHasTakenPlace) { |         $context->on('congestion', function () use (&$congestionEventHasTakenPlace) { | ||||||
| @ -61,11 +61,11 @@ class ContextTest extends TestCase | |||||||
| 
 | 
 | ||||||
|     public function testItCreatesAWorkerForASubmittedTask(): void |     public function testItCreatesAWorkerForASubmittedTask(): void | ||||||
|     { |     { | ||||||
| 		$limit = $this->createMock(ConcurrencyLimit::class); |         $concurrency = $this->createMock(Concurrency::class); | ||||||
|         $loop = $this->createMock(LoopInterface::class); |         $loop = $this->createMock(LoopInterface::class); | ||||||
| 		$context = new Context($loop, $limit); |         $context = new Context($loop, $concurrency); | ||||||
| 
 | 
 | ||||||
| 		$limit->method('isReachedBy')->willReturn(false); |         $concurrency->method('isReachedBy')->willReturn(false); | ||||||
|         $loop->expects(self::once()) |         $loop->expects(self::once()) | ||||||
|             ->method('futureTick') |             ->method('futureTick') | ||||||
|             ->withConsecutive([ |             ->withConsecutive([ | ||||||
| @ -78,7 +78,7 @@ class ContextTest extends TestCase | |||||||
|     public function testItRegistersMaintenanceTasksOnTheEventLoop(): void |     public function testItRegistersMaintenanceTasksOnTheEventLoop(): void | ||||||
|     { |     { | ||||||
|         $loop = $this->createMock(LoopInterface::class); |         $loop = $this->createMock(LoopInterface::class); | ||||||
| 		$limit = $this->createMock(ConcurrencyLimit::class); |         $concurrency = $this->createMock(Concurrency::class); | ||||||
| 
 | 
 | ||||||
|         $loop->expects(self::exactly(2)) |         $loop->expects(self::exactly(2)) | ||||||
|             ->method('addPeriodicTimer') |             ->method('addPeriodicTimer') | ||||||
| @ -90,14 +90,14 @@ class ContextTest extends TestCase | |||||||
|                 new Timer(Context::INTERVAL_GC, static fn() => null), |                 new Timer(Context::INTERVAL_GC, static fn() => null), | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
| 		$context = new Context($loop, $limit); |         $context = new Context($loop, $concurrency); | ||||||
|         $context->run(); |         $context->run(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function testItForwardsWorkersEventsToSelf(): void |     public function testItForwardsWorkersEventsToSelf(): void | ||||||
|     { |     { | ||||||
|         $loop = $this->createMock(LoopInterface::class); |         $loop = $this->createMock(LoopInterface::class); | ||||||
| 		$limit = $this->createMock(ConcurrencyLimit::class); |         $concurrency = $this->createMock(Concurrency::class); | ||||||
|         $workers = $this->createMock(Workers::class); |         $workers = $this->createMock(Workers::class); | ||||||
| 
 | 
 | ||||||
|         $workers->expects(self::exactly(3)) |         $workers->expects(self::exactly(3)) | ||||||
| @ -108,6 +108,6 @@ class ContextTest extends TestCase | |||||||
|                 ['no_workers_remaining', static fn() => null] |                 ['no_workers_remaining', static fn() => null] | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
| 		new Context($loop, $limit, $workers); |         new Context($loop, $concurrency, $workers); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,10 +3,10 @@ | |||||||
| namespace Toalett\Multiprocessing\Tests\Task; | namespace Toalett\Multiprocessing\Tests\Task; | ||||||
| 
 | 
 | ||||||
| use PHPUnit\Framework\MockObject\MockObject; | use PHPUnit\Framework\MockObject\MockObject; | ||||||
|  | use PHPUnit\Framework\TestCase; | ||||||
| use React\EventLoop\LoopInterface; | use React\EventLoop\LoopInterface; | ||||||
| use Toalett\Multiprocessing\Task\Task; | use Toalett\Multiprocessing\Task\Task; | ||||||
| use Toalett\Multiprocessing\Task\Tasks; | use Toalett\Multiprocessing\Task\Tasks; | ||||||
| use PHPUnit\Framework\TestCase; |  | ||||||
| use Toalett\Multiprocessing\Tests\Tools\PropertyInspector; | use Toalett\Multiprocessing\Tests\Tools\PropertyInspector; | ||||||
| 
 | 
 | ||||||
| class TasksTest extends TestCase | class TasksTest extends TestCase | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user