Refactored main class, improved a few things here and there. Looks polished enough
This commit is contained in:
parent
75c6062a3e
commit
411898af2a
14 changed files with 351 additions and 300 deletions
7
.idea/runConfigurations/Exception_Test.xml
generated
Normal file
7
.idea/runConfigurations/Exception_Test.xml
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Exception Test" type="PhpLocalRunConfigurationType" factoryName="PHP Console" path="$PROJECT_DIR$/tests/exception_test.php" scriptParameters="--log-level debug">
|
||||||
|
<method v="2">
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Install" run_configuration_type="MAKEFILE_TARGET_RUN_CONFIGURATION" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
5
.idea/runConfigurations/Redis_Server_Test.xml
generated
Normal file
5
.idea/runConfigurations/Redis_Server_Test.xml
generated
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Redis Server Test" type="PhpLocalRunConfigurationType" factoryName="PHP Console" path="$PROJECT_DIR$/tests/redis_server.php" scriptParameters="--log-level debug">
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
18
.idea/runConfigurations/Worker_Test.xml
generated
Normal file
18
.idea/runConfigurations/Worker_Test.xml
generated
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Worker Test" type="PhpLocalRunConfigurationType" factoryName="PHP Console" path="$PROJECT_DIR$/tests/worker.php" scriptParameters="--log-level debug">
|
||||||
|
<CommandLine>
|
||||||
|
<envs>
|
||||||
|
<env name="TAMER_WORKER" value="true" />
|
||||||
|
<env name="TAMER_WORKER_CHANNEL" value="0" />
|
||||||
|
<env name="TAMER_WORKER_DATABASE" value="0" />
|
||||||
|
<env name="TAMER_WORKER_HOST" value="127.0.0.1" />
|
||||||
|
<env name="TAMER_WORKER_ID" value="test" />
|
||||||
|
<env name="TAMER_WORKER_PASSWORD" value="" />
|
||||||
|
<env name="TAMER_WORKER_PORT" value="6379" />
|
||||||
|
</envs>
|
||||||
|
</CommandLine>
|
||||||
|
<method v="2">
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="Install" run_configuration_type="MAKEFILE_TARGET_RUN_CONFIGURATION" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
|
@ -5,10 +5,9 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [1.0.2] - Unreleased
|
## [2.0.0] - Unreleased
|
||||||
|
|
||||||
### Added
|
Major rewrite
|
||||||
- Added non-blocking mode for supervisor
|
|
||||||
|
|
||||||
## [1.0.1] - 2022-02-28
|
## [1.0.1] - 2022-02-28
|
||||||
|
|
||||||
|
|
33
README.md
33
README.md
|
@ -76,7 +76,7 @@ This README will contain documentation on how to effectively use TamerLib in you
|
||||||
* [Implementing a client](#implementing-a-client)
|
* [Implementing a client](#implementing-a-client)
|
||||||
* [Implementing a worker](#implementing-a-worker)
|
* [Implementing a worker](#implementing-a-worker)
|
||||||
* [Implementing a node](#implementing-a-node)
|
* [Implementing a node](#implementing-a-node)
|
||||||
* [Handling Exceptions](#handling-exceptions)
|
* [Job and Error Handling](#job-and-error-handling)
|
||||||
* [Methods](#methods)
|
* [Methods](#methods)
|
||||||
* [Global Methods](#global-methods)
|
* [Global Methods](#global-methods)
|
||||||
* [initialize](#initialize)
|
* [initialize](#initialize)
|
||||||
|
@ -376,6 +376,9 @@ client. In this example, we will implement a worker that implements the followin
|
||||||
A worker does not require to a server configuration, as the server configuration is obtained by the parent process once
|
A worker does not require to a server configuration, as the server configuration is obtained by the parent process once
|
||||||
the file is executed as a worker, see the documentation for the functions used in this example for more information.
|
the file is executed as a worker, see the documentation for the functions used in this example for more information.
|
||||||
|
|
||||||
|
Usually at the `run()` method, if you are running it indefinitely the worker will only exit once the parent process has
|
||||||
|
terminated, if the worker gets closed unexpectedly, the parent process will restart the worker.
|
||||||
|
|
||||||
- [`initialize`](#initialize)
|
- [`initialize`](#initialize)
|
||||||
- [`addFunction`](#addfunction)
|
- [`addFunction`](#addfunction)
|
||||||
- [`run`](#run)
|
- [`run`](#run)
|
||||||
|
@ -405,12 +408,31 @@ functionality should be implemented in the worker script. See [Implementing a wo
|
||||||
details on how to implement a worker script.
|
details on how to implement a worker script.
|
||||||
|
|
||||||
|
|
||||||
## Handling Exceptions
|
## Job and Error Handling
|
||||||
|
|
||||||
When waiting for a Job to complete, it is possible that the job will throw an exception, this exception will be caught
|
When waiting for a Job to complete, it is possible that the job will throw an exception, this exception will be caught
|
||||||
and re-thrown onto the client side of the application. This means that you can catch exceptions thrown by the worker
|
and re-thrown onto the client side of the application. This means that you can catch exceptions thrown by the worker
|
||||||
and handle them accordingly.
|
and handle them accordingly.
|
||||||
|
|
||||||
|
Jobs in TamerLib are managed as follows:
|
||||||
|
|
||||||
|
1. Only a client can delete a job once it has received its results.
|
||||||
|
2. A worker can delete a job if the job has no return channel to go to. This is determined by the client that's
|
||||||
|
listening for job completion when you run wait(), which allows a callback to be triggered each time a job is
|
||||||
|
immediately completed.
|
||||||
|
|
||||||
|
Error handling in TamerLib is achieved through job statuses:
|
||||||
|
|
||||||
|
1. If a return channel is provided, the job's status will be set to either "success" or "failure".
|
||||||
|
2. If the status is "success", the client will unserialize the returned results and return them.
|
||||||
|
3. If the status is "failure", the client will unserialize the exception and return that instead.
|
||||||
|
|
||||||
|
This mechanism ensures that your application has robust, clear information about the status of each job and can handle
|
||||||
|
any exceptions that are thrown during the execution of a job.
|
||||||
|
|
||||||
|
These robust error handling capabilities enable you to build applications with TamerLib that can reliably manage a wide
|
||||||
|
range of tasks in parallel while using the traditional exception handling mechanisms that you are already familiar with.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$job = \TamerLib\tm::do('throw_exception');
|
$job = \TamerLib\tm::do('throw_exception');
|
||||||
try
|
try
|
||||||
|
@ -441,6 +463,13 @@ catch(\SmtpException $e)
|
||||||
> available in the global namespace of the client application or the same packages must be imported in the client
|
> available in the global namespace of the client application or the same packages must be imported in the client
|
||||||
> and worker application. If the exception cannot be unserialized a generic `\Exception` will be thrown instead.
|
> and worker application. If the exception cannot be unserialized a generic `\Exception` will be thrown instead.
|
||||||
|
|
||||||
|
Most of TamerLib's static methods are capable of throwing exceptions but usually it will be a `\RuntimeException` or
|
||||||
|
`\InvalidArgumentException` if the method is called with invalid parameters. Crucial methods such as `initialize()` and
|
||||||
|
`run()` will throw a `\TamerLib\Exceptions\TamerException` if the method if there is an error with the TamerLib system
|
||||||
|
itself.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Methods
|
# Methods
|
||||||
|
|
||||||
TamerLib provides static methods that can be used to interact with the TamerLib system, these methods are often only
|
TamerLib provides static methods that can be used to interact with the TamerLib system, these methods are often only
|
||||||
|
|
|
@ -41,12 +41,6 @@
|
||||||
"source_type": "remote",
|
"source_type": "remote",
|
||||||
"source": "nosial/libs.log=latest@n64"
|
"source": "nosial/libs.log=latest@n64"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "com.opis.closure",
|
|
||||||
"version": "latest",
|
|
||||||
"source_type": "remote",
|
|
||||||
"source": "opis/closure=latest@composer"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "com.symfony.process",
|
"name": "com.symfony.process",
|
||||||
"version": "latest",
|
"version": "latest",
|
||||||
|
|
|
@ -724,6 +724,7 @@
|
||||||
|
|
||||||
if($job_id !== false && $this->claimJob($job_id[1], $worker_id))
|
if($job_id !== false && $this->claimJob($job_id[1], $worker_id))
|
||||||
{
|
{
|
||||||
|
Log::debug(Utilities::getName(), sprintf('Claimed job & received job %s', $job_id[1]));
|
||||||
return new JobPacket($this->getClient()->hGetAll($job_id[1]));
|
return new JobPacket($this->getClient()->hGetAll($job_id[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
<?php /** @noinspection PhpMissingFieldTypeInspection */
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpMissingFieldTypeInspection */
|
||||||
|
|
||||||
namespace TamerLib\Classes;
|
namespace TamerLib\Classes;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnused */
|
||||||
/** @noinspection PhpMissingFieldTypeInspection */
|
/** @noinspection PhpMissingFieldTypeInspection */
|
||||||
|
|
||||||
namespace TamerLib;
|
namespace TamerLib;
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
use TamerLib\Exceptions\JobManagerException;
|
use TamerLib\Exceptions\JobManagerException;
|
||||||
use TamerLib\Exceptions\JobNotFoundException;
|
use TamerLib\Exceptions\JobNotFoundException;
|
||||||
use TamerLib\Exceptions\ServerException;
|
use TamerLib\Exceptions\ServerException;
|
||||||
|
use TamerLib\Exceptions\TamerException;
|
||||||
use TamerLib\Exceptions\TimeoutException;
|
use TamerLib\Exceptions\TimeoutException;
|
||||||
use TamerLib\Exceptions\WorkerFailedException;
|
use TamerLib\Exceptions\WorkerFailedException;
|
||||||
use TamerLib\Objects\JobPacket;
|
use TamerLib\Objects\JobPacket;
|
||||||
|
@ -80,7 +82,7 @@
|
||||||
/**
|
/**
|
||||||
* Appends the job ID to the watch list
|
* Appends the job ID to the watch list
|
||||||
*
|
*
|
||||||
* @param int $job_id
|
* @param int $job_id The job ID to add to the watch list
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private static function addToWatchlist(int $job_id): void
|
private static function addToWatchlist(int $job_id): void
|
||||||
|
@ -94,7 +96,7 @@
|
||||||
/**
|
/**
|
||||||
* Removes the job ID from the watch list
|
* Removes the job ID from the watch list
|
||||||
*
|
*
|
||||||
* @param int $job_id
|
* @param int $job_id The job ID to remove from the watch list
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private static function removeFromWatchlist(int $job_id): void
|
private static function removeFromWatchlist(int $job_id): void
|
||||||
|
@ -105,6 +107,34 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a job and returns the result to the server
|
||||||
|
*
|
||||||
|
* @param JobPacket $job_packet
|
||||||
|
* @return void
|
||||||
|
* @throws ConnectionException
|
||||||
|
* @throws JobManagerException
|
||||||
|
*/
|
||||||
|
private static function executeJob(JobPacket $job_packet): void
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(!isset(self::$function_pointers[$job_packet->getPayload()]))
|
||||||
|
{
|
||||||
|
Log::warning(Utilities::getName(), sprintf('Job %s requested function \'%s\' which does not exist, rejecting job.', $job_packet->getId(), $job_packet->getPayload()));
|
||||||
|
self::$job_manager->rejectJob($job_packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = call_user_func_array(self::$function_pointers[$job_packet->getPayload()], unserialize($job_packet->getParameters(), ['allowed_classes'=>true]));
|
||||||
|
self::$job_manager->returnJob($job_packet, $result);
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
self::$job_manager->returnException($job_packet, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GLOBAL FUNCTIONS
|
* GLOBAL FUNCTIONS
|
||||||
*/
|
*/
|
||||||
|
@ -126,13 +156,13 @@
|
||||||
* if the parent process is a client process, otherwise an exception will be thrown because the worker will
|
* if the parent process is a client process, otherwise an exception will be thrown because the worker will
|
||||||
* have no server to connect to if it is not initialized by a client.
|
* have no server to connect to if it is not initialized by a client.
|
||||||
*
|
*
|
||||||
* @param string|null $mode
|
* @param string $mode The mode to initialize Tamer in, must be one of the TamerMode constants
|
||||||
* @param ServerConfiguration|null $server_config
|
* @param ServerConfiguration|null $server_config Optional. The server configuration to use, if null is provided
|
||||||
|
* then a server will be initialized with default parameters.
|
||||||
|
* @throws ServerException If the server fails to initialize
|
||||||
* @return void
|
* @return void
|
||||||
* @throws ServerException
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
*/
|
||||||
public static function initialize(?string $mode, ?ServerConfiguration $server_config=null): void
|
public static function initialize(string $mode, ?ServerConfiguration $server_config=null): void
|
||||||
{
|
{
|
||||||
if(self::$mode !== null)
|
if(self::$mode !== null)
|
||||||
{
|
{
|
||||||
|
@ -185,8 +215,16 @@
|
||||||
if($mode === TamerMode::CLIENT)
|
if($mode === TamerMode::CLIENT)
|
||||||
{
|
{
|
||||||
self::$supervisor = new WorkerSupervisor(self::$server_configuration);
|
self::$supervisor = new WorkerSupervisor(self::$server_configuration);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
self::$return_channel = sprintf('rch%s', random_int(100000000, 999999999));
|
self::$return_channel = sprintf('rch%s', random_int(100000000, 999999999));
|
||||||
}
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(sprintf('Bad environment, failed to generate random integer. (%s)', $e->getMessage()), $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self::$job_manager = new JobManager(self::$server_configuration);
|
self::$job_manager = new JobManager(self::$server_configuration);
|
||||||
}
|
}
|
||||||
|
@ -195,25 +233,36 @@
|
||||||
* Shuts down all workers
|
* Shuts down all workers
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
* @noinspection PhpUnused
|
|
||||||
*/
|
*/
|
||||||
public static function shutdown(): void
|
public static function shutdown(): void
|
||||||
{
|
{
|
||||||
|
// Do nothing if Tamer is not initialized
|
||||||
if(self::$mode === null)
|
if(self::$mode === null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(self::$mode === TamerMode::CLIENT && self::$supervisor !== null)
|
// Close all subprocesses
|
||||||
{
|
self::$supervisor?->stopAll();
|
||||||
self::$supervisor->stopAll();
|
self::$server?->stop();
|
||||||
}
|
self::$job_manager?->disconnect();
|
||||||
|
|
||||||
|
// Clear all static variables
|
||||||
|
self::$mode = null;
|
||||||
|
self::$server_configuration = null;
|
||||||
|
self::$server = null;
|
||||||
|
self::$watching_jobs = [];
|
||||||
|
self::$supervisor = null;
|
||||||
|
self::$job_manager = null;
|
||||||
|
self::$worker_configuration = null;
|
||||||
|
self::$function_pointers = [];
|
||||||
|
self::$return_channel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current mode Tamer is running in
|
* Returns the current mode Tamer is running in
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string The current mode Tamer is running in
|
||||||
*/
|
*/
|
||||||
public static function getMode(): string
|
public static function getMode(): string
|
||||||
{
|
{
|
||||||
|
@ -223,7 +272,8 @@
|
||||||
/**
|
/**
|
||||||
* Monitors all internal processes
|
* Monitors all internal processes
|
||||||
*
|
*
|
||||||
* @param int $timeout
|
* @param int $timeout The timeout in seconds to monitor for, if -1 is provided then the monitor will run
|
||||||
|
* for one iteration and then return. If 0 is provided then the monitor will run forever.
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function monitor(int $timeout=0): void
|
public static function monitor(int $timeout=0): void
|
||||||
|
@ -271,11 +321,12 @@
|
||||||
* Spawns a worker process by their count, if the path is null then a generic sub process will be spawned
|
* Spawns a worker process by their count, if the path is null then a generic sub process will be spawned
|
||||||
* that will only be capable of executing closures.
|
* that will only be capable of executing closures.
|
||||||
*
|
*
|
||||||
* @param int $count
|
* @param int $count The number of workers to spawn (defaults to 8)
|
||||||
* @param string|null $path
|
* @param string|null $path The path to the worker file to spawn, if null is provided then a generic worker
|
||||||
* @param int $channel
|
* will be spawned that can only execute closures (unimplemented)
|
||||||
|
* @param int $channel The channel to spawn the workers on (defaults to 0)
|
||||||
|
* @throws WorkerFailedException If the worker fails to spawn
|
||||||
* @return void
|
* @return void
|
||||||
* @throws WorkerFailedException
|
|
||||||
*/
|
*/
|
||||||
public static function createWorker(int $count=8, ?string $path=null, int $channel=0): void
|
public static function createWorker(int $count=8, ?string $path=null, int $channel=0): void
|
||||||
{
|
{
|
||||||
|
@ -299,12 +350,12 @@
|
||||||
/**
|
/**
|
||||||
* Preforms a function call against a worker in the background, returns the Job ID to keep track of the job status.
|
* Preforms a function call against a worker in the background, returns the Job ID to keep track of the job status.
|
||||||
*
|
*
|
||||||
* @param string $function
|
* @param string $function The function to call
|
||||||
* @param array $arguments
|
* @param array $arguments The arguments to pass to the function
|
||||||
* @param int $channel
|
* @param int $channel The channel to preform the function call on (defaults to 0)
|
||||||
* @return int
|
* @return int The Job ID of the function call
|
||||||
*/
|
*/
|
||||||
public static function do(string $function, array $arguments, int $channel=0): int
|
public static function do(string $function, array $arguments=[], int $channel=0): int
|
||||||
{
|
{
|
||||||
if(self::$mode !== TamerMode::CLIENT)
|
if(self::$mode !== TamerMode::CLIENT)
|
||||||
{
|
{
|
||||||
|
@ -321,9 +372,13 @@
|
||||||
{
|
{
|
||||||
self::$job_manager->pushJob($job_packet);
|
self::$job_manager->pushJob($job_packet);
|
||||||
}
|
}
|
||||||
catcH(Exception $e)
|
catch(ConnectionException $e)
|
||||||
{
|
{
|
||||||
throw new RuntimeException('do() failed, failed to push job to the server', 0, $e);
|
throw new RuntimeException('do() failed, failed to connect to the server', 0, $e);
|
||||||
|
}
|
||||||
|
catch(JobManagerException $e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException('do() failed, failed to push the job to the server', 0, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::addToWatchlist($job_packet->getId());
|
self::addToWatchlist($job_packet->getId());
|
||||||
|
@ -334,13 +389,12 @@
|
||||||
* Sends a function call to a worker in the background, but once the job is completed it will be forgotten and
|
* Sends a function call to a worker in the background, but once the job is completed it will be forgotten and
|
||||||
* the result will not be returned, this also means that the job will not be added to the watchlist.
|
* the result will not be returned, this also means that the job will not be added to the watchlist.
|
||||||
*
|
*
|
||||||
* @param string $function
|
* @param string $function The function to call
|
||||||
* @param array $arguments
|
* @param array $arguments The arguments to pass to the function
|
||||||
* @param int $channel
|
* @param int $channel The channel to preform the function call on (defaults to 0)
|
||||||
* @return void
|
* @return void
|
||||||
* @noinspection PhpUnused
|
|
||||||
*/
|
*/
|
||||||
public static function dof(string $function, array $arguments, int $channel=0): void
|
public static function dof(string $function, array $arguments=[], int $channel=0): void
|
||||||
{
|
{
|
||||||
if(self::$mode !== TamerMode::CLIENT)
|
if(self::$mode !== TamerMode::CLIENT)
|
||||||
{
|
{
|
||||||
|
@ -360,20 +414,22 @@
|
||||||
{
|
{
|
||||||
throw new RuntimeException('dof() failed, failed to push job to the server', 0, $e);
|
throw new RuntimeException('dof() failed, failed to push job to the server', 0, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::addToWatchlist($job_packet->getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for all the dispatched jobs to complete, this is a blocking function and will not return until all the
|
* Waits for all the dispatched jobs to complete, this is a blocking function and will not return until all the
|
||||||
* jobs have completed. If a timeout is specified, the function will return after the timeout has been reached.
|
* jobs have completed. If a timeout is specified, the function will return after the timeout has been reached.
|
||||||
*
|
*
|
||||||
* @param callable $callback
|
* @param callable $callback A callback function that will be called after each iteration of the wait loop
|
||||||
* @param int $timeout
|
* @param int $timeout The timeout in seconds, if 0 is provided then the function will block until all the jobs
|
||||||
|
* have completed, if -1 is provided then the function run for one iteration and return
|
||||||
* @return void
|
* @return void
|
||||||
* @throws ServerException
|
* @throws JobManagerException If the JobManager throws an exception
|
||||||
* @throws Throwable
|
* @throws TamerException If the Tamer throws an exception
|
||||||
* @throws TimeoutException
|
* @throws TimeoutException If the timeout is reached
|
||||||
|
* @throws ConnectionException If the client fails to connect to the server
|
||||||
|
* @throws Exception If a job fails
|
||||||
|
* @throws Throwable If a job fails
|
||||||
*/
|
*/
|
||||||
public static function wait(callable $callback, int $timeout=0): void
|
public static function wait(callable $callback, int $timeout=0): void
|
||||||
{
|
{
|
||||||
|
@ -388,15 +444,19 @@
|
||||||
self::monitor(-1);
|
self::monitor(-1);
|
||||||
if(count(self::$watching_jobs) === 0)
|
if(count(self::$watching_jobs) === 0)
|
||||||
{
|
{
|
||||||
Log::debug(Utilities::getName(), 'No jobs to wait for, returning');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::debug(Utilities::getName(), 'Waiting for jobs to complete');
|
Log::debug(Utilities::getName(), 'Waiting for jobs to complete');
|
||||||
$job_packet = self::$job_manager->listenReturnChannel(self::$return_channel);
|
$job_packet = self::$job_manager->listenReturnChannel(self::$return_channel);
|
||||||
|
|
||||||
if(in_array($job_packet->getId(), self::$watching_jobs))
|
if(!in_array($job_packet->getId(), self::$watching_jobs))
|
||||||
{
|
{
|
||||||
|
Log::debug(Utilities::getName(), sprintf('Job \'%s\' has returned, but is not in the watchlist', $job_packet->getId()));
|
||||||
|
self::$job_manager->dropJob($job_packet->getId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
self::removeFromWatchlist($job_packet->getId());
|
self::removeFromWatchlist($job_packet->getId());
|
||||||
self::$job_manager->dropJob($job_packet->getId());
|
self::$job_manager->dropJob($job_packet->getId());
|
||||||
|
|
||||||
|
@ -404,19 +464,9 @@
|
||||||
{
|
{
|
||||||
$return_value = $job_packet->getReturnValue();
|
$return_value = $job_packet->getReturnValue();
|
||||||
|
|
||||||
if($return_value === null)
|
if($return_value !== null)
|
||||||
{
|
|
||||||
$return_value = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
$return_value = unserialize($return_value, ['allowed_classes' => true]);
|
$return_value = unserialize($return_value, ['allowed_classes' => true]);
|
||||||
|
|
||||||
if($return_value === false)
|
|
||||||
{
|
|
||||||
Log::error(Utilities::getName(), 'Failed to unserialize return value, return value was dropped');
|
|
||||||
$return_value = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$callback($job_packet->getId(), $return_value);
|
$callback($job_packet->getId(), $return_value);
|
||||||
|
@ -438,18 +488,13 @@
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new ServerException('wait() failed, job returned with an exception');
|
/** @noinspection ThrowRawExceptionInspection */
|
||||||
|
throw new Exception(sprintf('wait() failed, job \'%s\' failed with an unknown exception', $job_packet->getId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log::debug(Utilities::getName(), sprintf('Job \'%s\' returned with an unexpected status of \'%s\'', $job_packet->getId(), $job_packet->getStatus()));
|
throw new TamerException(sprintf('wait() failed, job \'%s\' returned with an unknown status \'%s\'', $job_packet->getId(), $job_packet->getStatus()));
|
||||||
throw new ServerException('wait() failed, job returned with an unexpected status of \'' . $job_packet->getStatus() . '\'');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log::debug(Utilities::getName(), sprintf('Job \'%s\' has returned, but is not in the watchlist', $job_packet->getId()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($timeout < 0)
|
if ($timeout < 0)
|
||||||
|
@ -462,38 +507,36 @@
|
||||||
throw new TimeoutException('wait() timed out');
|
throw new TimeoutException('wait() timed out');
|
||||||
}
|
}
|
||||||
|
|
||||||
usleep(10);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for a job to complete, returns the result of the job.
|
* Waits for a job to complete, returns the result of the job.
|
||||||
*
|
*
|
||||||
* @param JobPacket|int $job_id
|
* @param int $job_id The ID of the job to wait for
|
||||||
* @param int $timeout
|
* @param int $timeout The timeout in seconds, if 0 is provided then the function will block until all the jobs
|
||||||
* @return mixed
|
* have completed, if -1 is provided then the function run for one iteration and return
|
||||||
* @throws JobNotFoundException
|
* @throws ConnectionException If the client fails to connect to the server
|
||||||
* @throws ServerException
|
* @throws JobManagerException If the JobManager throws an exception
|
||||||
* @throws TimeoutException
|
* @throws JobNotFoundException If the job is not found
|
||||||
* @throws Throwable
|
* @throws TimeoutException If the timeout is reached
|
||||||
|
* @throws Throwable If the job fails
|
||||||
|
* @throws Exception If the job fails
|
||||||
|
* @return mixed The return value of the job
|
||||||
*/
|
*/
|
||||||
public static function waitFor(JobPacket|int $job_id, int $timeout=0): mixed
|
public static function waitFor(int $job_id, int $timeout=0): mixed
|
||||||
{
|
{
|
||||||
if(self::$mode !== TamerMode::CLIENT)
|
if(self::$mode !== TamerMode::CLIENT)
|
||||||
{
|
{
|
||||||
throw new RuntimeException(sprintf('Attempting to waitFor() in \'%s\' mode, only clients can preform waitFor().', self::$mode));
|
throw new RuntimeException(sprintf('Attempting to waitFor() in \'%s\' mode, only clients can preform waitFor().', self::$mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
if($job_id instanceof JobPacket)
|
|
||||||
{
|
|
||||||
$job_id = $job_id->getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
$time_start = time();
|
$time_start = time();
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
self::monitor(-1);
|
self::monitor(-1);
|
||||||
|
|
||||||
switch(self::$job_manager->getJobStatus($job_id))
|
switch(self::$job_manager->getJobStatus($job_id))
|
||||||
{
|
{
|
||||||
case JobStatus::FINISHED:
|
case JobStatus::FINISHED:
|
||||||
|
@ -524,18 +567,18 @@
|
||||||
/**
|
/**
|
||||||
* Preforms a do() call on a waitFor() call all in one function.
|
* Preforms a do() call on a waitFor() call all in one function.
|
||||||
*
|
*
|
||||||
* @param string $function
|
* @param string $function The function to call
|
||||||
* @param array $arguments
|
* @param array $arguments The arguments to pass to the function
|
||||||
* @param int $channel
|
* @param int $channel The channel to use
|
||||||
* @param int $timeout
|
* @param int $timeout The timeout in seconds, if 0 is provided then the function will block until all the jobs
|
||||||
* @return mixed
|
* @throws ConnectionException If the client fails to connect to the server
|
||||||
* @throws JobNotFoundException
|
* @throws JobManagerException If the JobManager throws an exception
|
||||||
* @throws ServerException
|
* @throws JobNotFoundException If the job is not found
|
||||||
* @throws Throwable
|
* @throws TimeoutException If the timeout is reached
|
||||||
* @throws TimeoutException
|
* @throws Throwable If the job fails
|
||||||
* @noinspection PhpUnused
|
* @return mixed The return value of the job
|
||||||
*/
|
*/
|
||||||
public static function doWait(string $function, array $arguments, int $channel=0, int $timeout=0): mixed
|
public static function doWait(string $function, array $arguments=[], int $channel=0, int $timeout=0): mixed
|
||||||
{
|
{
|
||||||
return self::waitFor(self::do($function, $arguments, $channel), $timeout);
|
return self::waitFor(self::do($function, $arguments, $channel), $timeout);
|
||||||
}
|
}
|
||||||
|
@ -545,7 +588,6 @@
|
||||||
* Clears the watchlist, this will remove all jobs from the watchlist.
|
* Clears the watchlist, this will remove all jobs from the watchlist.
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
* @noinspection PhpUnused
|
|
||||||
*/
|
*/
|
||||||
public static function clear(): void
|
public static function clear(): void
|
||||||
{
|
{
|
||||||
|
@ -564,7 +606,7 @@
|
||||||
* @param array $arguments
|
* @param array $arguments
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public static function __callStatic(string $name, array $arguments)
|
public static function __callStatic(string $name, array $arguments=[])
|
||||||
{
|
{
|
||||||
return self::do($name, $arguments);
|
return self::do($name, $arguments);
|
||||||
}
|
}
|
||||||
|
@ -574,8 +616,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $function
|
* Registers a new function to be called by the worker, this function will be called when the worker receives
|
||||||
* @param callable $callback
|
* a job with the same name as the function. Internally this function uses call_user_func_array() to call the
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* @param string $function The name of the function to register
|
||||||
|
* @param callable $callback The callback to call when the function is called
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function addFunction(string $function, callable $callback): void
|
public static function addFunction(string $function, callable $callback): void
|
||||||
|
@ -599,9 +645,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $function
|
* Removes a function from the worker, this function will no longer be called when a job with the same name
|
||||||
|
* is received.
|
||||||
|
*
|
||||||
|
* @param string $function The name of the function to remove
|
||||||
* @return void
|
* @return void
|
||||||
* @noinspection PhpUnused
|
|
||||||
*/
|
*/
|
||||||
public static function removeFunction(string $function): void
|
public static function removeFunction(string $function): void
|
||||||
{
|
{
|
||||||
|
@ -610,12 +658,18 @@
|
||||||
throw new RuntimeException(sprintf('Attempting to removeFunction() in \'%s\' mode, only workers can preform removeFunction().', self::$mode));
|
throw new RuntimeException(sprintf('Attempting to removeFunction() in \'%s\' mode, only workers can preform removeFunction().', self::$mode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!isset(self::$function_pointers[$function]))
|
||||||
|
{
|
||||||
|
throw new InvalidArgumentException(sprintf('Attempting to removeFunction() with a function name of \'%s\', this function does not exist.', $function));
|
||||||
|
}
|
||||||
|
|
||||||
unset(self::$function_pointers[$function]);
|
unset(self::$function_pointers[$function]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array
|
* Returns an array of all the registered functions.
|
||||||
* @noinspection PhpUnused
|
*
|
||||||
|
* @return array An array of all the registered functions
|
||||||
*/
|
*/
|
||||||
public static function getFunctions(): array
|
public static function getFunctions(): array
|
||||||
{
|
{
|
||||||
|
@ -628,15 +682,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int|array $channel
|
* Runs the worker, this function will block and listen for incoming jobs, if a job is received then the
|
||||||
* @param int $timeout
|
* job will be processed and optionally returned back to the client via a return channel.
|
||||||
|
*
|
||||||
|
* @param int|array $channel The channel to listen on, if an array is provided then the worker will listen on
|
||||||
|
* @param int $timeout The timeout in seconds, if 0 is provided then the function will block until a job is received
|
||||||
|
* @param bool $ignore_errors If set to true then the worker will not throw exceptions, instead it will log the
|
||||||
|
* error and continue to run.
|
||||||
|
* @throws ConnectionException If the client fails to connect to the server
|
||||||
* @return void
|
* @return void
|
||||||
* @throws ConnectionException
|
|
||||||
* @throws JobManagerException
|
|
||||||
* @throws JobNotFoundException
|
|
||||||
* @throws ServerException
|
|
||||||
*/
|
*/
|
||||||
public static function run(int|array $channel=0, int $timeout=0): void
|
public static function run(int|array $channel=0, int $timeout=0, bool $ignore_errors=false): void
|
||||||
{
|
{
|
||||||
if(self::$mode !== TamerMode::WORKER)
|
if(self::$mode !== TamerMode::WORKER)
|
||||||
{
|
{
|
||||||
|
@ -651,7 +707,7 @@
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$job_packet = self::$job_manager->listenForJob(self::$worker_configuration->getWorkerId(), $channel, $timeout);
|
$job_packet = self::$job_manager->listenForJob(self::$worker_configuration->getWorkerId(), $channel, $timeout);
|
||||||
break;
|
self::executeJob($job_packet);
|
||||||
}
|
}
|
||||||
catch(TimeoutException $e)
|
catch(TimeoutException $e)
|
||||||
{
|
{
|
||||||
|
@ -661,8 +717,7 @@
|
||||||
/** @noinspection PhpRedundantCatchClauseInspection */
|
/** @noinspection PhpRedundantCatchClauseInspection */
|
||||||
catch(RedisException $e)
|
catch(RedisException $e)
|
||||||
{
|
{
|
||||||
// TODO: There has to be a better way to do this.
|
if($ignore_errors === false && strtolower($e->getMessage()) === 'redis server went away')
|
||||||
if(strtolower($e->getMessage()) === 'redis server went away')
|
|
||||||
{
|
{
|
||||||
if($error_time === null)
|
if($error_time === null)
|
||||||
{
|
{
|
||||||
|
@ -670,35 +725,25 @@
|
||||||
}
|
}
|
||||||
else if((time() - $error_time) >= 5)
|
else if((time() - $error_time) >= 5)
|
||||||
{
|
{
|
||||||
throw new ServerException('Redis server went away, and did not come back.');
|
throw new ConnectionException('Redis server went away, and did not come back.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
Log::error(Utilities::getName(), sprintf('Worker %s encountered an error while listening for jobs: %s', self::$worker_configuration->getWorkerId(), $e->getMessage()), $e);
|
||||||
|
unset($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($timeout === -1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if((time() - $start_time) >= $timeout)
|
if((time() - $start_time) >= $timeout)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Log::debug(Utilities::getName(), sprintf('Worker %s received job %s', self::$worker_configuration->getWorkerId(), $job_packet->getId()));
|
|
||||||
|
|
||||||
if(!isset(self::$function_pointers[$job_packet->getPayload()]))
|
|
||||||
{
|
|
||||||
|
|
||||||
Log::warning(Utilities::getName(), sprintf('Job %s requested function \'%s\' which does not exist, rejecting job.', $job_packet->getId(), $job_packet->getPayload()));
|
|
||||||
self::$job_manager->rejectJob($job_packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$result = call_user_func_array(self::$function_pointers[$job_packet->getPayload()], unserialize($job_packet->getParameters(), ['allowed_classes'=>true]));
|
|
||||||
self::$job_manager->returnJob($job_packet, $result);
|
|
||||||
}
|
|
||||||
catch(Exception $e)
|
|
||||||
{
|
|
||||||
self::$job_manager->returnException($job_packet, $e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,57 +3,44 @@
|
||||||
class ExampleClass
|
class ExampleClass
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var array
|
* Sleeps for the given number of seconds, plus a random number of seconds between 0 and 100.
|
||||||
|
*
|
||||||
|
* @param int $seconds
|
||||||
|
* @return int
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private $data;
|
public function sleep(int $seconds=1): int
|
||||||
|
|
||||||
/**
|
|
||||||
* ExampleClass constructor.
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
{
|
||||||
$this->data = [];
|
sleep($seconds);
|
||||||
|
return random_int(0, 100) + $seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a value in the data array
|
* Calculates pi using the Leibniz formula.
|
||||||
|
*
|
||||||
|
* @param int $iterations
|
||||||
|
* @return float
|
||||||
|
*/
|
||||||
|
public function pi(int $iterations): float
|
||||||
|
{
|
||||||
|
$pi = 0;
|
||||||
|
$sign = 1;
|
||||||
|
for ($i = 0; $i < $iterations; $i++)
|
||||||
|
{
|
||||||
|
$pi += $sign / (2 * $i + 1);
|
||||||
|
$sign *= -1;
|
||||||
|
}
|
||||||
|
return $pi * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an exception.
|
||||||
*
|
*
|
||||||
* @param string $key
|
|
||||||
* @param mixed $value
|
|
||||||
* @return void
|
* @return void
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function set(string $key, mixed $value): void
|
public function throwException(): void
|
||||||
{
|
{
|
||||||
$this->data[$key] = $value;
|
throw new Exception('This is an exception.');
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a value from the data array
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function get(string $key): mixed
|
|
||||||
{
|
|
||||||
return $this->data[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a key exists in the data array
|
|
||||||
*
|
|
||||||
* @param string $key
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function exists(string $key): bool
|
|
||||||
{
|
|
||||||
return isset($this->data[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function clear(): void
|
|
||||||
{
|
|
||||||
$this->data = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
14
tests/exception_test.php
Normal file
14
tests/exception_test.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Import everything
|
||||||
|
require 'ncc';
|
||||||
|
require __DIR__ . DIRECTORY_SEPARATOR . 'ExampleClass.php';
|
||||||
|
import('net.nosial.tamerlib');
|
||||||
|
|
||||||
|
// Initialize TamerLib
|
||||||
|
\TamerLib\tm::initialize(\TamerLib\Enums\TamerMode::CLIENT);
|
||||||
|
// Start 8 workers.
|
||||||
|
\TamerLib\tm::createWorker(8, __DIR__ . DIRECTORY_SEPARATOR . 'worker.php');
|
||||||
|
|
||||||
|
// Throw an exception, this be thrown in the client.
|
||||||
|
echo \TamerLib\tm::doWait('throwException');
|
38
tests/jobs_test.php
Normal file
38
tests/jobs_test.php
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Import everything
|
||||||
|
require 'ncc';
|
||||||
|
require __DIR__ . DIRECTORY_SEPARATOR . 'ExampleClass.php';
|
||||||
|
import('net.nosial.tamerlib');
|
||||||
|
|
||||||
|
// Initialize TamerLib
|
||||||
|
\TamerLib\tm::initialize(\TamerLib\Enums\TamerMode::CLIENT);
|
||||||
|
// Start 8 workers.
|
||||||
|
\TamerLib\tm::createWorker(12, __DIR__ . DIRECTORY_SEPARATOR . 'worker.php');
|
||||||
|
|
||||||
|
// For testing purposes
|
||||||
|
$total_sleep = 0;
|
||||||
|
|
||||||
|
// Run 5 sleep jobs
|
||||||
|
for($i = 0; $i < 5; $i++)
|
||||||
|
{
|
||||||
|
$sleep_time = random_int(1, 5);
|
||||||
|
\TamerLib\tm::sleep($sleep_time);
|
||||||
|
$total_sleep += $sleep_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run 30 pi jobs
|
||||||
|
for($i = 0; $i < 30; $i++)
|
||||||
|
{
|
||||||
|
\TamerLib\tm::pi(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all the jobs to complete
|
||||||
|
$start_time = time();
|
||||||
|
\TamerLib\tm::wait(static function($job_id, $return){
|
||||||
|
echo sprintf('Job %s completed with return value %s.', $job_id, $return) . PHP_EOL;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Script ends here once all the jobs are complete.
|
||||||
|
echo sprintf('Total sleep time: %s seconds.', $total_sleep) . PHP_EOL;
|
||||||
|
echo sprintf('Total execution time: %s seconds.', time() - $start_time) . PHP_EOL;
|
|
@ -1,62 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use LogLib\Log;
|
|
||||||
use TamerLib\Enums\TamerMode;
|
|
||||||
use TamerLib\Objects\ServerConfiguration;
|
|
||||||
use TamerLib\tm;
|
|
||||||
|
|
||||||
require 'ncc';
|
|
||||||
import('net.nosial.tamerlib');
|
|
||||||
|
|
||||||
// Start as client mode, if no configuration is passed on then
|
|
||||||
// Tamer will spawn its own Redis server and use it.
|
|
||||||
tm::initialize(TamerMode::CLIENT);
|
|
||||||
tm::createWorker(20, __DIR__ . DIRECTORY_SEPARATOR . 'worker.php');
|
|
||||||
|
|
||||||
$total_sleep = 0;
|
|
||||||
$start_time = time();
|
|
||||||
$jobs = [];
|
|
||||||
|
|
||||||
// Start doing programming!
|
|
||||||
// Loop 2 times, each time we will do a sleep job
|
|
||||||
for($i = 0; $i <= 2; $i++)
|
|
||||||
{
|
|
||||||
$sleep_time = random_int(5, 10);
|
|
||||||
$total_sleep += $sleep_time;
|
|
||||||
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
|
||||||
$job_id = tm::sleep($sleep_time);
|
|
||||||
|
|
||||||
// Log the result
|
|
||||||
print(sprintf('Created task %s', $job_id) . PHP_EOL);
|
|
||||||
$jobs[$job_id] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop 200 times, each time we will do a Pi calculation job
|
|
||||||
for($i = 0; $i < 200; $i++)
|
|
||||||
{
|
|
||||||
$iterations = random_int(100000, 1000000);
|
|
||||||
|
|
||||||
/** @noinspection PhpUndefinedMethodInspection */
|
|
||||||
$job_id = tm::calculate_pi($iterations);
|
|
||||||
|
|
||||||
// Log the result
|
|
||||||
print(sprintf('Created task %s', $job_id) . PHP_EOL);
|
|
||||||
$jobs[$job_id] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
print('Waiting for jobs to finish...' . PHP_EOL);
|
|
||||||
|
|
||||||
// Wait for all jobs to finish
|
|
||||||
tm::wait(static function ($job_id, $result) use (&$jobs) {
|
|
||||||
print(sprintf('Job %s finished with result %s', $job_id, $result) . PHP_EOL);
|
|
||||||
$jobs[$job_id] = $result;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Finally do some fancy calling
|
|
||||||
|
|
||||||
|
|
||||||
var_dump($jobs);
|
|
||||||
|
|
||||||
print(sprintf('Total sleep time: %s', $total_sleep) . PHP_EOL);
|
|
||||||
print(sprintf('Total execution time: %s', time() - $start_time) . PHP_EOL);
|
|
|
@ -1,44 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// Import everything
|
||||||
require 'ncc';
|
require 'ncc';
|
||||||
require __DIR__ . DIRECTORY_SEPARATOR . 'ExampleClass.php';
|
require __DIR__ . DIRECTORY_SEPARATOR . 'ExampleClass.php';
|
||||||
import('net.nosial.tamerlib');
|
import('net.nosial.tamerlib');
|
||||||
|
|
||||||
// Initialize as a worker, will fail if the process is executed directly
|
// Initialize TamerLib
|
||||||
\TamerLib\tm::initialize(\TamerLib\Enums\TamerMode::WORKER);
|
\TamerLib\tm::initialize(\TamerLib\Enums\TamerMode::WORKER);
|
||||||
|
|
||||||
// Callback Examples
|
// Register the functions
|
||||||
\TamerLib\tm::addFunction('sleep', function($sleep_time){
|
|
||||||
sleep($sleep_time);
|
|
||||||
return $sleep_time;
|
|
||||||
});
|
|
||||||
\TamerLib\tm::addFunction('calculate_pi', function($iterations){
|
|
||||||
$pi = 0;
|
|
||||||
$sign = 1;
|
|
||||||
for($i = 0; $i < $iterations; $i++)
|
|
||||||
{
|
|
||||||
$pi += $sign * (1 / (2 * $i + 1));
|
|
||||||
$sign *= -1;
|
|
||||||
}
|
|
||||||
return $pi * 4;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Function pointer examples
|
|
||||||
$example_class = new ExampleClass();
|
$example_class = new ExampleClass();
|
||||||
\TamerLib\tm::addFunction('getValue', [$example_class, 'get']);
|
\TamerLib\tm::addFunction('sleep', [$example_class, 'sleep']);
|
||||||
\TamerLib\tm::addFunction('setValue', [$example_class, 'set']);
|
\TamerLib\tm::addFunction('pi', [$example_class, 'pi']);
|
||||||
\TamerLib\tm::addFunction('valueExists', [$example_class, 'exists']);
|
\TamerLib\tm::addFunction('throwException', [$example_class, 'throwException']);
|
||||||
\TamerLib\tm::addFunction('clearValues', [$example_class, 'clear']);
|
|
||||||
|
|
||||||
// Run forest, run!
|
// Run the worker
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
\TamerLib\tm::run();
|
\TamerLib\tm::run();
|
||||||
}
|
|
||||||
catch(Exception $e)
|
|
||||||
{
|
|
||||||
print($e->getMessage() . PHP_EOL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue