swoole-src icon indicating copy to clipboard operation
swoole-src copied to clipboard

How to check if script/context is being executed inside Swoole?

Open ttodua opened this issue 4 years ago • 13 comments

What is correct approach from inside a script to check, if it being executed in Swoole Co\Run context? Basically, I am trying to make a library-function of sleep, which I want to be called by Swoole/non-Swoole scripts:

public function sleep($seconds){
    if (method_exists('\\Swoole\\Coroutine\\System','sleep')  &&  I_AM_BEING_EXECUTED_IN_SWOOLE_COROUTINE () ) 
        \Swoole\Coroutine\System::sleep($seconds); 
    else sleep($seconds);  
}

so, I want a real alternative instead of I_AM_BEING_EXECUTED_IN_SWOOLE_COROUTINE () function.

ttodua avatar Jun 30 '21 04:06 ttodua

Simply use the sleep function provided by php. After Co\run is started, it will automatically replace the synchronously blocked sleep with the asynchronous sleep function of coroutine

matyhtf avatar Jun 30 '21 10:06 matyhtf

thanks ! btw, so just anyway (for other purposes) is there any way to find out if current script is being executed inside coroutine or not? (you can close the issue after answer) . tnx

ttodua avatar Jun 30 '21 10:06 ttodua

code

<?php

use function Swoole\Coroutine\run;
use function Swoole\Coroutine\go;

run(function() {
    echo "sleep begin\n";
    sleep(1);
    echo "sleep end\n";
});

strace

strace php sleep.php
...

epoll_create(512)                       = 3
mmap(NULL, 2101248, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0e20a69000
write(1, "sleep begin\n", 12sleep begin
)           = 12
epoll_wait(3, [], 4096, 1000)           = 0
madvise(0x55b9d84d7000, 4096, MADV_DONTNEED) = 0
brk(0x55b9d84ea000)                     = 0x55b9d84ea000
epoll_wait(3, [], 4096, 1)              = 0
write(1, "sleep end\n", 10sleep end
)             = 10
munmap(0x7f0e20a69000, 2101248)         = 0
close(3)                                = 0
close(0)                                = 0
munmap(0x7f0e20eec000, 916544)          = 0
munmap(0x7f0e214f1000, 392272)          = 0
munmap(0x7f0e20ebc000, 196608)          = 0
munmap(0x7f0e22600000, 2097152)         = 0
munmap(0x7f0e20e6b000, 331776)          = 0
munmap(0x7f0e252f2000, 135168)          = 0
exit_group(0)                           = ?
+++ exited with 0 +++

matyhtf avatar Jun 30 '21 10:06 matyhtf

@ttodua Use Swoole\Coroutine::getCid(), if return -1, that means it is not in coroutine

matyhtf avatar Jun 30 '21 10:06 matyhtf

Yeah,that is !!! thnxx

ttodua avatar Jun 30 '21 10:06 ttodua

However, I have to recommend you to make a critical note with regard to sleep.

The reason I've mentioned the fail-safe 'sleep' function (in the first post), is that is still needs to be determined whether the script is being run within coroutine or not. Do you want to know the main reason, why?

because, the script will lead to unexpected results. For example, when you execute sleep(0.2) it will misbehave , as native sleep only accepts integer (thus, the float number will be rounded to integer). you can try the same code within native php and within coroutine:

echo microtime(true)."\r\n";
sleep(0.99);
echo microtime(true)."\r\n";

and you will get different results (native php will round that to 0 seconds). So, it matters where sleep is executed, to avoid unexpected errors/results.

so, for that reason, i've created this fail-safe function:

public function sleep($seconds){ 
    if ( method_exists('\\Swoole\\Coroutine\\System','sleep') ) 
        \Swoole\Coroutine\System::sleep($seconds); 
	else {
		if ( filter_var($seconds, FILTER_VALIDATE_INT) !== false)
			sleep($seconds);
		else
			usleep($seconds/1000000);
	} 
}

just my 2 cents.

I might advise to create usleep version in swoole to match the well-known behavior of native usleep thus, it will lead people to less errors, instead of relying on sleep, or make some notes on official DOCS, that Swoole's sleep is unline native sleep because it accepts decimals too, not only integer.

ttodua avatar Jun 30 '21 10:06 ttodua

The example you provided looks incorrect for me... sleep(1) is equal to usleep(1 * 1000 * 1000), so your code should be usleep($seconds * 1000000).

And you need not to check it, on Swoole coroutine, usleep() will be replaced by Swoole automatically. It means, when you using usleep in Swoole coroutine, usleep(1000) is equal to Swoole\Coroutine\System::sleep(0.001).

twose avatar Jul 01 '21 07:07 twose

@twose Uh-oh, yes, there should be multiplier, not divisor , of 1000000. my stupid mistake :)

ttodua avatar Jul 01 '21 07:07 ttodua

But I meant about Sleep's misbehavior when called within or outside coroutine, they provide different results.

ttodua avatar Jul 01 '21 07:07 ttodua

In Swoole, we recommand you to put all of your code in coroutine, do not do anything outside of coroutine, Co\run() is like main() in C.

twose avatar Jul 01 '21 08:07 twose

@ttodua Use Swoole\Coroutine::getCid(), if return -1, that means it is not in coroutine

May I ask? It has a bug.

I see that typically, it should work - tested with this funcion.

var_dump("cid (PHP):".\Swoole\Coroutine::getCid());
var_dump("Pcid (PHP):".\Swoole\Coroutine::getPcid());
\Swoole\Coroutine\run( function(){
	var_dump("cid (RUN):".\Swoole\Coroutine::getCid());
	var_dump("Pcid (RUN):".\Swoole\Coroutine::getPcid());
	go( 
		function(){
			var_dump("cid (RUN\GO):".\Swoole\Coroutine::getCid());
			var_dump("Pcid (RUN\GO):".\Swoole\Coroutine::getPcid());
		} 
	);
} );
exit("");

and result was:

"cid  (PHP):-1"
"Pcid (PHP):"
"cid  (RUN):3"
"Pcid (RUN):-1"
"cid  (RUN\GO):4"
"Pcid (RUN\GO):3"

however, something strange happened though. I have function wrapper, exactly like this:

if ( \Swoole\Coroutine::getCid()==-1 ) 
{
  \Swoole\Coroutine\run(function() {
	go( ... );
  });
}
// scheduler started, no need to "run", instead directly go:
else{
   go( ... );
}

However, I still found some errors in logs : PHP Warning: Swoole\Coroutine\Scheduler::start(): eventLoop has already been created. unable to start Swoole\Coroutine\Scheduler in (like mentioned here ). Are you sure that given method is guaranteed way to check that we are in coroutine (scheduler already started) ? Is there any better way to check it?

ttodua avatar Jul 22 '21 19:07 ttodua

My opinion, just use go() everywhere. And main() I usually use if(!debug_backtrace()){ ... } as main wrapper like Python if __name__ == "__main__"

crazywhalecc avatar Aug 11 '21 02:08 crazywhalecc

My opinion, just use go() everywhere. And main() I usually use if(!debug_backtrace()){ ... } as main wrapper like Python if __name__ == "__main__"

Co\run() only can be used at the top of coroutine context, and go() will automatically recognize it. To check if script is being executed inside coroutine, Co::getCid() is a right way.

crazywhalecc avatar Aug 11 '21 07:08 crazywhalecc