note 62638 deleted from function.flock by danbrown

From: Date: Mon, 18 May 2009 00:38:44 +0000
Subject: note 62638 deleted from function.flock by danbrown
References: 1  Groups: php.notes 
Request: Send a blank email to php-notes+get-155402@lists.php.net to get a copy of this message
Note Submitter: dranger at export dash japan dot com ---- Re: Niels Jaeckel The code posted unfortunately does NOT work in race conditions, because even though it is improbable, there is still a chance the 'mutex' will fail and you will encounter a situation where two programs have a 'lock' for the same file at the same time. It is improbable, but in a busy system with perhaps many processes trying to open one file or perhaps two processes opening the file several times, the probability rises and you will most certianly get strange bugs at strange times. Even though it looks quite improbable, there are two problems: 1) The extra ifs do not add any extra precautions. You might as well usleep for a few microseconds because only the last one matters. 2) Even though it looks like a single atomic command, if you look at the PHP source code, touch is relatively complicated. It gets the time from the computer, checks the base directory, etc.,etc. This means that there will be times that this lock fails, no matter how improbable. What makes a race condition so nasty is that it happens only once every so often, making it nearly impossible to debug and really annoying. *** The only safe way to implement locks is with flock or some other locking mechanism *outside* of PHP. *** (caveat: there may be an alternate locking mechanism in PHP i don't know about, but you cannot make your own) To put this to rest, make a PHP script using the functions described in the previous post. Then, add this code to the file: <?php /*** test.php ***/ // lock and unlock function defs go here while(1) { if(lock("test")) { $f=fopen("importantfile", "w") or die; $pid=getmypid(); $string="Important Information! From $pid"; fwrite($f, $string); fclose($f); $check=file_get_contents("importantfile"); if($check != $string) { echo "THIS LOCK FAILED!\\n"; } unlock("test"); } } ?> Then run this script from the command line - it will loop forever happily. Then, *while that script is running*, run the same script again while the first one is still going. In a UNIX environment you can do this by typing: > php test.php & > php test.php & You will probably see this: Warning: unlink(test.lock): No such file or directory in lock.php on line 29 THIS LOCK FAILED! Warning: touch(): Utime failed: No such file or directory in lock.php on line 19 a lot of times. This means the lock has failed :) In fact, if you ever think you have invented a clever way to lock a file, test it first in this while loop. Just replace lock and unlock with your function and rearrange the code so it makes sense. Then run it and see if it fails. Note that flock() passes this test beautifully.

« previous php.notes (#155402) next »