1

I'm trying to test a race condition in PHP. I'd like to have N PHP processes get ready to do something, then block. When I say "go", they should all execute the action at the same time. Hopefully this will demonstrate the race.

In Java, I would use Object.wait() and Object.notifyAll(). What can I use in PHP?

(Either Windows or linux native answers are acceptable)

tshepang
  • 12,111
  • 21
  • 91
  • 136
Rich
  • 15,048
  • 2
  • 66
  • 119
  • I'd use file, and check `file_exists`. Here it really deppends what "the same time" means. – Tomáš Zato Apr 17 '13 at 14:46
  • "the same time" means in parallel and all starting as close as possible to the exact same moment. When you say "check file_exists", do you mean with a spin-lock? That might work. – Rich Apr 17 '13 at 16:02
  • Yeah, something with [`usleep`](http://cz2.php.net/manual/en/function.usleep.php) function. – Tomáš Zato Apr 17 '13 at 16:21
  • If you used "usleep", that wouldn't be a [spinlock](http://en.wikipedia.org/wiki/Spinlock) – Rich Apr 17 '13 at 17:20
  • I've written your comment up as an answer, as it's the best answer I have yet. Feel free to write your own version and I'll delete mine in favour of yours. – Rich Apr 17 '13 at 17:25
  • I'm going to hazard a guess that you are using processes because you think it is the only option. http://php.net/pthreads ... you will find yourself in familiar territory, hopefully ... http://php.net/thread.wait, http://php.net/thread.notify (implicitly all) kinda making this a non-question, which is the reason I haven't said this in an answer ... – Joe Watkins Apr 19 '13 at 13:11
  • My specific problem is a race condition in a standard LAMP webapp, so multi-process is the most realistic way to repro the bug, but `pthreads` looks like it does have wait/notify, so that might also work. – Rich Apr 19 '13 at 13:47
  • Incidentally, I didn't end up using this approach to repro the bug, but used JMeter + lots of concurrent web requests. – Rich Apr 19 '13 at 13:49

3 Answers3

2
  • Create a file "wait.txt"
  • Start N processes, each with the code shown below
  • Delete the "wait.txt" file.

...

<?php
while (file_exists('wait.txt')) {}
runRaceTest();
Rich
  • 15,048
  • 2
  • 66
  • 119
1

Usually with PHP file lock approach is used. One create a RUN_LOCK or similar file and asks for file_exists("RUN_LOCK"). This system is also used to secure potential endless loops in recursive threads.

I decided to require the file for the execution. Other approach may be, that existence of the file invokes the blocking algorithm. That depends on your situation. Always the safer situation should be the easier to achieve.

Wait code:

/*prepare the program*/
   /* ... */
/*Block until its time to go*/
define("LOCK_FILE", "RUN_UNLOCK");    //I'd define this in some config.php
while(!file_exists(LOCK_FILE)) {
    usleep(1); //No sleep will eat lots of CPU
}
/*Execute the main code*/
   /* ... */
/*Delete the "run" file, so that no further executions should be allowed*/
usleep(1);  //Just for sure - we want other processes to start execution phase too
if(file_exists(LOCK_FILE))
    unlink(LOCK_FILE);

I guess it would be nice to have a blocking function for that, like this one:

function wait_for_file($filename, $timeout = -1) {
    if($timeout>=0) {
        $start = microtime(true)*1000;    //Remember the start time
    }    
    while(!file_exists($filename)) {      //Check the file existence
        if($timeout>=0) {                 //Only calculate when timeout is set
           if((microtime(true)*1000-$start)>$timeout) //Compare current time with start time (current always will be > than start)
             return false;          //Return failure
        }   
        usleep(1);         //Save some CPU
    }
    return true;           //Return success
}

It implements timeout. You don't need them but maybe someone else will.
Usage:

header("Content-Type: text/plain; charset=utf-8"); 
ob_implicit_flush(true);while (@ob_end_clean());   //Flush buffers so the output will be live stream
define("LOCK_FILE","RUN_FOREST_RUN");  //Define lock file name again
echo "Starting the blocking algorithm. Waiting for file: ".LOCK_FILE."\n"; 
if(wait_for_file(LOCK_FILE, 10000)) {  //Wait for 10 secconds
    echo "File found and deleted!\n";
    if(file_exists(LOCK_FILE))   //May have been deleted by other proceses
        unlink(LOCK_FILE);
}
else {
    echo "Wait failed!\n";
}

This will output:

Starting the blocking algorithm. Waiting for file: RUN_FOREST_RUN
Wait failed!

~or~

Starting the blocking algorithm. Waiting for file: RUN_FOREST_RUN
File found and deleted!
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • Thanks. I think that adding "usleep" to the wait loop will cause the threads to be less tightly synchronised, so I prefer the version without it. This file_exists approach is inelegant but effective, much like the rest of PHP ;-) – Rich Apr 19 '13 at 10:17
  • One microsecond is really short time interval. What exactly are those threads supposed to do? – Tomáš Zato Apr 19 '13 at 11:13
  • In my case, they are meant to participate in a fairly standard read-then-write race condition, like the example shown [on wikipedia](http://en.wikipedia.org/wiki/Race_condition#Example). For setting up a test case, we don't care about burning some CPU in the test processes but we do care about getting the processes all in the "run" state at the same instant. Calling "usleep" here marks the process as interruptible to the O/S and is distracting in the code, so I think it's incorrect to use it here. – Rich Apr 19 '13 at 13:52
  • Of course, testing the situation is way more different - and you are right with sacrificing some CPU to have a precise results. For other users, the required level of synchronisation may be lower. – Tomáš Zato Apr 19 '13 at 17:43
-1

PHP doesn't have multithreading. And its not planned to be implemented either. You can try hacks with sockets though or 0MQ to communicate between multiple processes

See Why does PHP not support multithreading? Php multithread

Community
  • 1
  • 1
Artjom Kurapov
  • 6,115
  • 4
  • 32
  • 42