PHP - Interrupting or suspending pthread's execution - php

How to interrupt an execution of a thread from the main context?
In the snippet below - how would one go about stopping the action that the thread does without destroying it?
class ReadFileThread extends Thread
{
public function __construct($file, $chunk = 1024)
{
$this->file = $file;
$this->chunk = $chunk;
}
public function run()
{
if(is_file($this->file) && is_readable($this->file))
{
$fh = fopen($this->file, 'rb');
while(!feof($fh))
{
$content = fread($fh, $this->chunk);
}
fclose($fh);
}
}
}
$num = 10;
$threads = [];
for($i = 0; $i < $num; $i++)
{
$thread = new ReadFileThread('/path/to/10gig_file.txt', 1024);
$threads[] = $thread;
// I start the thread, now it's detached from the main context and is reading the file asynchronously
$thread->start();
}
// The interesting part - I want to random 1 thread whose operation of file reading I want to interrupt
$to_interrupt = $threads[rand(0, $num)];
// How to interrupt the thread without destroying it? I want its context preserved

RandomSeeds answer is close, but open to race conditions.
<?php
class FileReader extends Thread {
public $file;
public $pause;
public function __construct($file) {
$this->file = $file;
$this->pause = false;
}
public function run() {
if (($handle = fopen($this->file, "rb"))) {
$len = 0;
do {
$this->synchronized(function(){
if ($this->paused) {
printf(
"\npausing %lu ...\n", $this->getThreadId());
$this->wait();
}
});
$data = fread($handle, 1024);
$len += strlen($data);
if (($len % 2) == 0) {
printf(
"\r\rread %lu", $len);
}
} while (!feof($handle));
fclose($handle);
}
}
public function pause() {
return $this->synchronized(function(){
return ($this->paused = true);
});
}
public function unpause() {
return $this->synchronized(function(){
$this->paused = false;
if ($this->isWaiting()) {
return $this->notify();
}
});
}
}
function do_something($time) {
$start = time();
while (($now = time()) < ($start + $time)) {
usleep(100);
if (($now % 2) == 0) {
echo ".";
}
}
echo "\n";
}
$reader = new FileReader("/path/to/big/file.ext");
$reader->start();
sleep(2);
$reader->pause();
do_something(rand(2, 4));
$reader->unpause();
sleep(2);
$reader->pause();
do_something(rand(2, 4));
$reader->unpause();
sleep(2);
$reader->pause();
do_something(rand(2, 4));
$reader->unpause();
?>
It is important that variables used for purposes of synchronization are only ever access in synchronized blocks, I have omitted the implementation of a stop/quit function but it's logic is much the same, as RandomSeeds example shows.
Race conditions lurk within:
public function mine($data) {
/* anyone can set doSynchronization at any time */
if ($this->doSynchronization) {
$this->synchronize(function(){
/* checking the predicate in here is safer */
$this->wait();
});
}
}
Good:
public function mine($data) {
$this->synchronize(function(){
if ($this->doSynchronization) {
$this->wait();
}
});
}
Extreme:
public function mine($data) {
$this->synchronize(function(){
while ($this->doSynchronization) {
$this->wait();
}
});
}
The posix standard would always have you write it the extreme way, I'm not so fussed, whatever works for you. The reason for this extreme code is, allowances must be made for a thread to receive a signal other than the one it is waiting on, many low level signals may result in a thread waking from a call to pthread_cond_wait; checking the predicate in a loop like that guards against what the specification calls spurious wakeups ... but such extreme measures can also lead to ill side effects; the reason those threads receive the low level signal is because it is necessary for them to take some action of some kind, ignoring that can easily cause a different part of the stack to deadlock because it expected your thread to die (or do something else, die is an example) when it was signalled ...

AFAIK you can't arbitrarily pause a concurrent thread, but you can send a notification to it. The other thread must cooperate and willingly pause itself when it receives the notification.
Example:
<?php
class MyThread extends Thread {
private $pauseRequested = false;
private $stopRequested = false;
public function pause() {
$this->synchronized(function($thread){
$thread->pauseRequested = true;
}, $this);
}
public function resume() {
$this->synchronized(function($thread){
$thread->pauseRequested = false;
$thread->notify();
}, $this);
}
public function stop() {
$this->synchronized(function($thread){
$thread->stopRequested = true;
}, $this);
}
public function run() {
echo 'Thread started!' . PHP_EOL;
while (!$this->stopRequested) {
// do the actual work
echo 'Working...';
sleep(1);
// check if we have been requested to pause
$this->synchronized(function($thread){
if ($this->pauseRequested) {
echo 'Paused...';
$thread->wait(); // this is where the magic happens
}
}, $this);
}
if ($this->stopRequested) {
echo PHP_EOL . 'Stopped!' . PHP_EOL;
}
}
}
$t = new MyThread();
$t->start();
sleep(5);
$t->pause();
sleep(2);
$t->resume();
sleep(5);
$t->stop();
// wait for $t to complete
$t->join();
?>

Never used pthreads, tbh, but have you tried making a public boolean flag inside the thread class ?

Related

How to exit from a specific function in PHP?

I have multiple nested methods inside a PHP class. What I want to do is, based on certain circumstances, I want to exit from NOT JUST the current method, but 2 above it, then the leftover code should continue running. Now the issue with die(), exit() is that they end the full script and I don't want that. I simply want to go a few methods up and continue the script.
Of course, there's the old school method of returning a value in each method and check if it's false for example. But that way I'll have to write tons of additional code if I have like 50 nested methods. Here's what I have right now - it's a very basic usage here, I'm using it in a lot more complicated scenarios (using PHP 7.2.4):
class Sites
{
public function __construct()
{
$this->fn1();
}
public function fn1()
{
$fn2 = $this->fn2();
echo 'I want this to be displayed no matter what!';
}
public function fn2()
{
$fn3 = $this->fn3();
if ($fn3)
{
return true;
}
}
public function fn3()
{
$fn4 = $this->fn4();
if ($fn4)
{
return true;
}
}
public function fn4()
{
$random = rand(1, 100);
if ($random > 50)
{
return true;
}
else
{
// I want to exit/break the scirpt to continue running after
// the $fn2 = $this->fn2() call in the $this->fn1() function.
exit();
echo "This shouldn't be displayed.";
}
}
}
Just as mentioned in the code comments, I want to break the script - if the random number is below 50 and go back to fn1() but continue executing the echo function there.
Is this possible somehow? Please let me know if you need more information and I'll provide.
You can use Exceptions to do this, not particularly elegant, but this should do what your after, replace these methods...
public function fn1()
{
try {
$fn2 = $this->fn2();
}
catch ( Exception $e ) {
}
echo 'I want this to be displayed no matter what!';
}
public function fn4()
{
$random = rand(1, 100);
if ($random > 50)
{
return true;
}
else
{
// I want to exit/break the scirpt to continue running after
// the $fn2 = $this->fn2() call in the $this->fn1() function.
//exit();
throw new Exception();
echo "This shouldn't be displayed.";
}
}
How about regular function call with a flag?
class Sites
{
protected $flag = false;
public function __construct()
{
$this->fn1();
}
public function fn1()
{
if ($this->flag) {
$this->flag = true;
} else {
echo 'I want this to be displayed no matter what!';
$fn2 = $this->fn2();
}
}
public function fn2()
{
$fn3 = $this->fn3();
if ($fn3)
{
return true;
}
}
public function fn3()
{
$fn4 = $this->fn4();
if ($fn4)
{
return true;
}
}
public function fn4()
{
$random = rand(1, 100);
if ($random > 50)
{
return true;
}
else
{
// I want to exit/break the scirpt to continue running after
// the $fn2 = $this->fn2() call in the $this->fn1() function.
//exit();
$this->flag = true;
$this->fn1();
exit();
echo "This shouldn't be displayed.";
}
}
}
$sites = new Sites;
I hope this helps!

PHP - PTHREAD Worker Threads not executing concurrently

I am working on improving a process in php so I resorted into using multithreading using Worker to manage my threads. But the threads in the worker executes one after the other not concurrently.
This is my sample code
namespace App\Engine\Threads\ThreadClass;
class StatePaperAttendancePDFThread extends \Threaded
{
private $write_folder_name;
private $center_code;
private $paper_id;
private $title;
public function __construct($write_folder_name, $center_code, $paper_id, $title)
{
$this->write_folder_name = $write_folder_name;
$this->center_code = $center_code;
$this->paper_id = $paper_id;
$this->title = $title;
}
public function run(){
echo " Retrieving paper attendance ".$this->center_code." ".$this->paper_id." ".\Thread::getCurrentThreadId()." ".date("H:i:s:u").PHP_EOL;
$artisan = 'C:/xampp/htdocs/attendance/artisan';
$cmd = "php $artisan attendancereport:center $this->write_folder_name $this->center_code $this->paper_id $this->title";
$return = exec($cmd, $output, $return_var);
echo $return;
}
}
foreach ($centers as $i=>$center){
$center_code = $center->getCenterCode();
$thread = new StatePaperAttendancePDFThread($folder_name, $center_code, $paper_id, $title);
$worker->stack($thread);
}
$worker->start(PTHREADS_INHERIT_ALL ^ PTHREADS_INHERIT_CLASSES);
$worker->shutdown();
but when I monitor it from the CLI using the time been printed I can see that none of the threads starts together. They all starts with some seconds interval
Please what am I missing
I was able to solve my problem by creating a customized Thread Pool Executor as below. Please I am open to suggestions and Improvement
class ThreadPoolExecutor{
private $poolSize;
private $threadPool;
private $done = false;
private $workingThreads;
public function __construct($poolSize, array $threadPool)
{
$this->poolSize = $poolSize;
$this->threadPool = $threadPool;
}
public function execute()
{
$this->parametersOk();
try {
while (!empty($this->threadPool)) {
$this->extractThreads();
foreach ($this->workingThreads as $thread) {
$thread->start(PTHREADS_INHERIT_ALL ^ PTHREADS_INHERIT_CLASSES);
}
foreach ($this->workingThreads as $thread) {
$thread->join();
}
}
$this->done = true;
} catch (\Exception $ex) {
var_dump($ex->getMessage());
}
}
private function parametersOk()
{
if (!is_array($this->threadPool))
throw new \RuntimeException("threadPool expected to be an array of threads");
if (count($this->threadPool) <= 0)
throw new \RuntimeException("expected at least an element in threadPool");
foreach ($this->threadPool as $thread) {
if (!is_subclass_of($thread, \Thread::class, false)) {
throw new \RuntimeException(" an element of threadPool does not extend class \\Thread");
}
}
if ($this->poolSize > count($this->threadPool)) {
throw new \RuntimeException("The number of threads set to execute can not be greater than the threadPool");
}
return true;
}
private function extractThreads()
{
$this->workingThreads = [];
$this->workingThreads = array_slice($this->threadPool, 0, $this->poolSize);
for ($i = 0; $i < count($this->workingThreads); $i++) {
array_shift($this->threadPool);
}
}
public function isDone()
{
return $this->done;
}
}
I will appreciate any addition or correction to this.

Does flock() only apply to the current method?

Does the flock() function only work if it is used within the same method that the code is executed?
For example, in the following code, the lock is successful:
public function run()
{
$filePointerResource = fopen('/tmp/lock.txt', 'w');
if (flock($filePointerResource, LOCK_EX)) {
sleep(10);
} else {
exit('Could not get lock!');
}
}
However, in the following code, the lock is unsuccessful:
public function run()
{
if ($this->lockFile()) {
sleep(10);
} else {
exit('Could not get lock!');
}
}
private function lockFile()
{
$filePointerResource = fopen('/tmp/lock.txt', 'w');
return flock($filePointerResource, LOCK_EX);
}
I haven't seen any documentation on this, so I am puzzled by this behavior. I am using php version 5.5.35.
I think the issue with your class based attempt is that when the lockFile method finishes the $filePointerResource goes out of scope and that is probably what is releasing the lock
This works which sort of supports that theory
<?php
class test {
public function run()
{
$fp = fopen('lock.txt', 'w');
if ($this->lockFile($fp)) {
echo 'got a lock'.PHP_EOL;
sleep(5);
}
/*
* Not going to do anything as the attempt to lock EX will
* block until a lock can be gained
else {
exit('Could not get lock!'.PHP_EOL);
}
*/
}
private function lockFile($fp)
{
return flock($fp, LOCK_EX);
}
}
$t = new test();
$t->run();
So if you want to lock the file over more than one call to a class method it might be better to keep the filehandle as a class property, then it will remain in scope as long as the class is instantiated and in scope.
<?php
class test {
private $fp;
public function run()
{
$this->fp = fopen('lock.txt', 'w');
if ($this->lockFile()) {
echo 'got a lock'.PHP_EOL;
sleep(5);
}
/*
* Not going to do anything as the attempt to lock EX will
* block until a lock can be gained
else {
exit('Could not get lock!'.PHP_EOL);
}
*/
}
private function lockFile()
{
return flock($this->fp, LOCK_EX);
}
}
$t = new test();
$t->run();

How do I set the procss blocked for waiting for a signal in PHP

I`m writing a Timer script,using a signal.Every 1 second send a SIGALRM to the process,then catch the signal and check whether the there is a task to do.
I use pcntl_alarm(1) to send the signal,but the process excute-and-quit,before receive the signal.
I think whether there is a method to set the prcess blocked waiting for the signal.I have used base_event,but its much heavy.
So anybody can help me.Thanks a lot.
That`s my script(Sorry for the chinesse ^_^):
<?php
/**
*定时器
*/
class Timer
{
//保存所有定时任务
public static $task = array();
//定时间隔
public static $time = 1;
/**
*开启服务
*#param $time int
*/
public static function run($time = null)
{
if($time)
{
self::$time = $time;
}
self::installHandler();
pcntl_alarm(1);
do{ sleep(2); }while(true);
}
/**
*注册信号处理函数
*/
public static function installHandler()
{
echo "installHandler\n";
pcntl_signal(SIGALRM, array('Timer','signalHandler'),false);
pcntl_alarm(1);
}
/**
*信号处理函数
*/
public static function signalHandler()
{
self::task();
}
/**
*执行回调
*/
public function task()
{
if(empty(self::$task))
{
return ;
}
foreach(self::$task as $time => $arr)
{
$current = time();
$func = $arr['func'];
$argv = $arr['argv'];
$interval = $arr['interval'];
$persist = $arr['persist'];
if($current == $time)
{
call_user_func_array($func, $argv);
}
if($persist)
{
self::$task[$current+$interval] = $arr;
}
unset(self::$task[$time]);
}
pcntl_alarm(self::$time);
}
/**
*添加任务
public static function add($interval, $func, $argv,$persist = false)
{
if(is_null($interval))
{
return;
}
$time = time()+$interval;
self::$task[$time] = array('func'=>$func, 'argv'=>$argv, 'interval'=>$interval, 'persist'=>$persist);
}
/**
*删除所有定时器任务
*/
public function dellAll()
{
self::$task = array();
}
}
Now, I know the answer even though so many times has passed away for I had other jobs to finish...sorry for the late.So my answer is very simple.
sleep
is right for blocking a process, then the process receives a signal,and what I have missed is never capture it...so use
pcntl_signal_dispatch
function in a loop is exactly the way.

nested lock/free calls in shared memory synchronization using semaphores

[EDIT] Complete rewrite with added Background (original question below)
In an application running in PHP I used shared memory to store values temporarily for performance reasons (database has too much overhead and files are too slow).
I built a really simple shared memory class that gives scripts access to variables stored in shared memory and has the ability to synchronize calls using semaphores. The code is here (no error handling as of yet):
class SHM {
private static $defaultSize = 10000;
private static function getIdentifier ($identFile, $projId) {
return ftok($identFile, $projId);
}
private $sem = NULL;
private $shm = NULL;
private $identFile;
private $projId;
private $size;
public function __construct($identFile, $projId, $size=NULL) {
if ($size === NULL) $size = self::$defaultSize;
$this->identFile = $identFile;
$this->projId = $projId;
$this->size = $size;
}
public function __destruct() {
if ($this->sem) {
$this->lock();
if ($this->shm) {
shm_detach($this->shm);
}
$this->free();
}
}
public function exists ($key) {
return shm_has_var($this->getShm(), $key);
}
public function get ($key, $lock=true) {
if ($this->exists ($key)) {
if ($lock) $this->lock();
$var = shm_get_var($this->getShm(), $key);
if ($lock) $this->free();
return $var;
} else return NULL;
}
public function set ($key, $var, $lock=true) {
if ($lock) $this->lock();
shm_put_var($this->getShm(), $key, $var);
if ($lock) $this->free();
}
public function remove ($key, $lock=true) {
if ($this->exists ($key)) {
if ($lock) $this->lock();
$result = shm_remove_var($this->getShm(), $key);
if ($lock) $this->free();
return $result;
} else return NULL;
}
public function clean () {
$this->lock();
shm_remove($this->shm);
$this->free();
sem_remove($this->sem);
$this->shm = NULL;
$this->sem = NULL;
}
private function getSem () {
if ($this->sem === NULL) {
$this->sem = sem_get(self::getIdentifier($this->identFile, $this->projId));
}
return $this->sem;
}
private function lock () {
return sem_acquire($this->getSem());
}
private function free () {
return sem_release($this->getSem());
}
private function getShm () {
if ($this->shm === NULL) {
$this->shm = shm_attach(self::getIdentifier($this->identFile, $this->projId), $this->size);
}
return $this->shm;
}
}
I now have another class that uses this shared memory class and needs to perform a "get, modify and write" operation on one variable. Basically, this:
function getModifyWrite () {
$var = $mySHM->get('var');
$var += 42;
$mySHM->set('var', $var);
}
The way it is now, this would lock the semaphore, free it, lock it again, and free it. I would love to have the code executed with the semaphore locke dthe whole time.
Previously, I had the code surrounded by one sem_acquire and sem_release pair. Unfortunately it turned out (thanks #Ben) that System V binary semaphores block on additional lock calls by the same process.
There are no monitors in PHP either (that would actually solve it), and I'm not too keen on implementing them on my own using some shared-memory varialbes (also I guess I could do this...) plus the traditional semaphores.
I need exclusive access, so non-binary semaphores aren't an option either.
Any sugestions on hwo to do this, without violating DRY principles?
original question
Just a quick question on how System V semaphores work and how PHP uses them:
If I lock (sem_acquire) one semaphore in one process multiple times, is the semaphore value actually increased with every call (so I need to free (sem_release) it as often as I locked it), or do additional calls of sem_acquire just continues without counting up if the process already owns the semaphore (so the first free always unlocks the semaphore)?
When in doubt, a hint on how to reasonably test this would be enough ^^
Example:
$sem = sem_get(ftok('/some/file', 'a'));
function doSomething1 () {
sem_acquire($sem);
doSomething2();
// do something else
sem_release($sem);
}
function doSomething2 () {
sem_acquire($sem);
// do stuff
sem_release($sem);
}
In the code above, If I call doSomething1, would the sem_release inside doSomething2 already free the semaphore for other processes, or was the semaphore counter actually set to "2" (even though it only has a capacity of one, as nothing else was specified in sem_get) and the semaphore stays locked until released the second time?
I need it to stay locked until doSOmething1 has finished its work, obviously. I could, of course, ujst copy the contets of doSomething2, but this violates DRY principles and I want to avoid it. I coulkd, of course, also pack the work inside doSOmething2 inside a private function and call that one from both others, but that is additional, potntially unnecessary overhead, too - so I'm aksing first before doing it. And, of course ³, the real thing isn't that simple.
I DO know how semaphores in general work, but as there are multiple implementation strategies, I want to make sure System V semaphores work the way I'd expect them to work (that is, increasing the counter and requireng as many calls to freeas they received lock calls).
My own solution (for now - still waiting for other suggestions, so if you got a better/different soltution, go ahead!):
1) I modified the lockand unlock methods to count lock/unlock calls and only access the semaphore if it hasn't been locked yet / has been unlocked as often as locked.
2) I added a modify method to my SHM class that takes the key of a variable to modify and a callback. It then locks, gets the variable using the getter (no additional locking because of 1) ), calls the callback and passes the variable to it, afterwards it uses the setter (again: no additional locking) to write the new value back, then it frees the semaphore.
The modify method can work in two modes: either the callback can take the variable as reference and modify it (default), or you can tell modify() to instead assign the return value of the callback to the function. This provides maximum flexibility.
The modified SHM class is below (still no error-handling, but modified free and lock behave nicely so it can be added):
<?php namespace Utilities;
//TODO: ERROR HANDLING
class SHM {
private static function getIdentifier ($identFile, $projId) {
return ftok($identFile, $projId);
}
private static $defaultSize = 10000;
private $sem = NULL;
private $shm = NULL;
private $identFile;
private $projId;
private $size;
private $locked=0;
public function __construct($identFile, $projId, $size=NULL) {
if ($size === NULL) $size = self::$defaultSize;
$this->identFile = $identFile;
$this->projId = $projId;
$this->size = $size;
}
public function __destruct() {
if ($this->sem) {
$this->lock();
if ($this->shm) {
shm_detach($this->shm);
}
$this->free();
}
}
public function clean () {
$this->lock();
shm_remove($this->shm);
$this->free();
sem_remove($this->sem);
$this->shm = NULL;
$this->sem = NULL;
}
public function __isset($key) {
return $this->exists($key);
}
public function __get($key) {
return $this->get($key);
}
public function __set($key, $val) {
return $this->set($key, $val);
}
public function __unset($key) {
return $this->remove($key);
}
public function exists ($key) {
return shm_has_var($this->getShm(), $key);
}
public function get ($key, $lock=true) {
if ($this->exists ($key)) {
if ($lock) $this->lock();
$var = shm_get_var($this->getShm(), $key);
if ($lock) $this->free();
return $var;
} else return NULL;
}
public function set ($key, $var, $lock=true) {
if ($lock) $this->lock();
shm_put_var($this->getShm(), $key, $var);
if ($lock) $this->free();
}
public function modify ($key, $action, $useReturn = false) {
$var = $this->get($key);
$result = $action($var);
if ($useReturn) {
$var = $result;
}
$this->set($key, $var);
}
public function remove ($key, $lock=true) {
if ($this->exists ($key)) {
if ($lock) $this->lock();
$result = shm_remove_var($this->getShm(), $key);
if ($lock) $this->free();
return $result;
} else return NULL;
}
private function getSem () {
if ($this->sem === NULL) {
$this->sem = sem_get(self::getIdentifier($this->identFile, $this->projId));
}
return $this->sem;
}
private function getShm () {
if ($this->shm === NULL) {
$this->shm = shm_attach(self::getIdentifier($this->identFile, $this->projId), $this->size);
}
return $this->shm;
}
private function lock () {
if ($this->locked == 0) {
$result = sem_acquire($this->getSem());
if (!$result) return 0;
}
return ++$this->locked;
}
private function free () {
if ($this->locked == 1) {
$result = sem_release($this->getSem());
if (!$result) return 0;
}
return --$this->locked;
}
}

Categories