note 62638 added to function.flock
| From: | drangeratexportdashjapandotcom at osu1 dot php dot net | Date: | Mon, 06 Mar 2006 05:45:12 +0000 |
| Subject: | note 62638 added to function.flock | ||
| Groups: | php.notes | ||
| Request: | Send a blank email to php-notes+get-104879@lists.php.net to get a copy of this message | ||
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.
----
Server IP: 210.138.109.182
Probable Submitter: 61.195.156.129
----
X-Spam-Status: No, hits=3.1 required=5.0 tests=DATE_MISSING,FROM_NO_LOWER
autolearn=no version=2.64
----
Manual Page -- http://www.php.net/manual/en/function.flock.php
Edit -- http://master.php.net/note/edit/62638
Del: integrated -- http://master.php.net/note/delete/62638/integrated
Del: useless -- http://master.php.net/note/delete/62638/useless
Del: bad code -- http://master.php.net/note/delete/62638/bad+code
Del: spam -- http://master.php.net/note/delete/62638/spam
Del: non-english -- http://master.php.net/note/delete/62638/non-english
Del: in docs -- http://master.php.net/note/delete/62638/in+docs
Del: other reasons-- http://master.php.net/note/delete/62638
Reject -- http://master.php.net/note/reject/62638
Search -- http://master.php.net/manage/user-notes.php