PHP\'s documentation page for flock() indicates that it\'s not safe to use under IIS. If I can\'t rely on flock under all circumstances, is there another way I
None of these methods are fully atomic.
I have made some tests, confirming this.
The code for T7, using 7 files named by their size in kB:
clearstatcache();
$_DEBUG_ = false;
echo "Lock and flush tester.".time()."
";
$time_constant = 1570787996;
die; // Remove this line when you set time_constant
while ( time()<$time_constant )
{
usleep(500);
}
function test($n, $p, $_DEBUG_){
// $delay_multiplier = $n*2.5;
$sname = "$n"; // source
$tname = "$n.txt";// target
echo "$n at ".time()."
";
for ($i = 0; $i<50; $i++ ){
$start = microtime(true);
clearstatcache(); // needed for filesize and touch
$st = stat("$sname");
$original_size = $st['size'];
if ( $_DEBUG_ )
echo "; 1) prevAccess by ".$st['mtime']." fsize ".$st['size']."; ";
$fsize = filesize($sname);
if ( $original_size <> $fsize )
die("; fsize total FAILTURE; ");
if ($fsize === 0)
echo "! The fsize is 0: stat(): ".$st['size']." ;";
else
{
// READ OPERATION AND LOCK FOR SHARE
$locked = false;
for ($c = 0; !$locked; $c++):
if ( $c > 400)
break;
$fp = fopen($sname, "r");
$locked = flock($fp, LOCK_SH);
if ($locked)
break;
else
{
echo "failed to get LOCK_SH;
";
usleep(5000);
}
endfor;
$s = fread($fp, $fsize );
$success = flock($fp, LOCK_UN);
if ( $success === false )
die("; r flock release failed; ");
$success = fclose($fp);
if ( $success === false )
die("; fclose failed; ");
// 10 - loaded data , $p - broser
if ( $success )
{
$result = touch("$sname",strlen($s),$p);
if ( $_DEBUG_ )
echo "; TOUCH: $result;";
}
else
die("fclose FAIL.");
if ( strlen($s)<60 )
echo "*$s LENGTH:".strlen($s)."
";
}
clearstatcache();
$st = stat("$tname");
if ( $_DEBUG_ )
echo "; 2) prevAccess by ".$st['mtime']." fsize is ".$fsize."; ";
// WRITE OPERATION WITH LOC_EX
$fp = fopen($tname, "w");
$locked = false;
/*
// TOTO NEMÁ VLIV NA ZAMKNUTÍ
for ($c = 0; !$locked; $c++ ):
$c++;
if ( $c > 400)
break;
$locked = flock($fp, LOCK_EX);
if ($locked)
break;
else
{
echo "failed to get LOCK_EX;
";
usleep(5000);
}
endfor;
*/
$locked = flock($fp, LOCK_EX);
if ( $locked ) { // acquire an exclusive lock
$success = fwrite($fp, $s);
if ( $success === false)
echo "; w FAILED;";
else
if ( $_DEBUG_ )
echo " $success B written; ";
$success = fflush($fp);// flush output before releasing the lock
if ( $success === false )
echo "; flush FAILED; ";
$success = flock($fp, LOCK_UN); // release the lock
if ( $success === false )
echo "; release FAILED; ";
$success = fclose($fp);
if ( $success === false )
echo "; fclose FAILED; ";
clearstatcache(); // needed for filesize and touch
$fsize = filesize($tname);
if ($original_size>$fsize)
{
echo "; WRITE FAILED, restoring;";
$original_fname = "$n";
$result = copy($original_fname, $tname);
if ($result == false )
die(" TOTAL FAILTURE: copy failed.");
else
echo " RESTORED;";
}
else
{
if ($fsize === 0)
echo "! THE FILE WAS NOT WRITTEN: data length: ".strlen($s)." fsize: $fsize RESOURCE: $fp
";
if ( $success )
touch("$tname",$fsize,$p);
}
} else {
echo "Couldn't get the lock!";
}
$time_elapsed_secs = microtime(true) - $start;
//usleep( $delay_multiplier + $n*rand(2,6) );
if ( $time_elapsed_secs === 0 )
echo " FAILED ";
echo "time: $time_elapsed_secs s
";
}
}
// headers to identify originator of the request
switch ( $_SERVER['HTTP_USER_AGENT'] ):
// FF 1:
case "Mozilla/5.0 (Windows NT 5.1;) Gecko":
$p = 1; break;
// Chrome:
case "Mozilla/5.0 (Windows NT 5.1) AppleWebKit Chrome Safari":
$p = 2; break;
// OPERA:
case "Mozilla/5.0 (Windows NT 5.1) AppleWebKit Chrome Safari":
$p = 3; break;
endswitch;
copy("523","523.txt");
copy("948","948.txt");
copy("1371","1371.txt");
copy("1913","1913.txt");
copy("2701","2701.txt");
copy("4495","4495.txt");
copy("6758","6758.txt");
test("523",$p,$_DEBUG_);
test("948",$p,$_DEBUG_);
test("1371",$p,$_DEBUG_);
test("1913",$p,$_DEBUG_);
test("2701",$p,$_DEBUG_);
test("4495",$p,$_DEBUG_);
test("6758",$p,$_DEBUG_);
The code for T8 (mkdir lock test):
clearstatcache();
$_DEBUG_ = false;
echo "Atomicity tester.".time()."
";
$time_constant = 1570787996;
die; // Remove this line when you set time_constant
while ( time()<$time_constant )
{
usleep(500);
}
/*
c is counter for optimalization
first call must have c = 0;
*/
function atomicFuse($n, $c, $disableDelay = false){
$start = false;
if ( !file_exists("$n.t") )
$start = mkdir("$n.t");
if ( !$disableDelay ){
if ( $start == false )
{
$n = $n*30;
switch($c): // Delay example increase:
case 0: break; // 0,01569 total
case 1: break; // 0,03138 total
case 2: $n = $n*2; break; // 0,06276 total
case 3: $n = $n*4; break; // 0,12552 total
// case 4: You need at least *6 or *8 to get out of problems with extrem times
case 4: $n = $n*8; break; // 0,25104 t.(upper limit)
// In case of heavy traffic:
case 5: $n = $n*8; break; // 0,36087 total extrem
case 6: $n = $n*10; break; // 0,51777 total extrem
case 7: $n = $n*20; break; // 1,03554 total extrem
default: $n = $n*8; break;
endswitch;
usleep($n);
echo ($n)."
";
}
}
return $start;
}
function test($n, $p, $_DEBUG_){
$fp = null;
$sname = "$n"; // source
$tname = "$n.txt";// target
echo "$n at ".time()."
";
for ($i = 0; $i<50; $i++ ){
$start_time = microtime(true);
{
$start = atomicFuse($n,0);
if (!$start) $start = atomicFuse($n,1);
if (!$start) $start = atomicFuse($n,2);
if (!$start) $start = atomicFuse($n,3);
if (!$start) $start = atomicFuse($n,4);
if (!$start) $start = atomicFuse($n,5);
if (!$start) $start = atomicFuse($n,6);
if (!$start) $start = atomicFuse($n,7);
if (!$start) $start = atomicFuse($n, false);
if (!$start) echo "Atomicity failed. ";
if ( $start )
{
echo "Atomicity OK. ";
/////////////////////////////
// CHECK FILESIZE VALIDITY //
/////////////////////////////
clearstatcache(); // needed for filesize and touch
$st = stat("$sname");
$original_size = $st['size'];
if ( $_DEBUG_ )
echo "; 1) prevAccess by ".$st['mtime']." fsize ".$st['size']."; ";
$fsize = filesize($sname);
if ( $original_size <> $fsize )
die("; fsize total FAILTURE; ");
if ($fsize === 0)
echo "! The fsize is 0: stat(): ".$st['size']." ;";
///////////////////
// OPEN THE FILE //
///////////////////
$fp = fopen($sname, "r");
$s = fread($fp, $fsize );
$success = fclose($fp);
if ( $success === false )
die("; fclose failed; ");
// 10 - loaded data, $p - browser
if ( $success )
{
$result = touch("$sname",strlen($s),$p);
if ( $_DEBUG_ )
echo "; TOUCH: $result;";
}
else
die("fclose FAIL.");
if ( strlen($s)<60 )
echo "*$s LENGTH:".strlen($s)."
";
}
}
if ( $start )
{
clearstatcache();
$st = stat("$tname");
if ( $_DEBUG_ )
echo "; 2) prevAccess by ".$st['mtime']." fsize is ".$fsize."; ";
// WRITE OPERATION WITH LOC_EX
$fp = fopen($tname, "w");
if ( true ) { // acquire an exclusive lock
$success = fwrite($fp, $s);
if ( $success === false)
echo "; w FAILED;";
else
if ( $_DEBUG_ )
echo " $success B written; ";
$success = fflush($fp);// flush output before releasing the lock
if ( $success === false )
echo "; flush FAILED; ";
if ( $success === false )
echo "; release FAILED; ";
$success = fclose($fp);
if ( $success === false )
echo "; fclose FAILED; ";
clearstatcache(); // needed for filesize and touch
$fsize = filesize($tname);
if ($original_size>$fsize)
{
echo "; WRITE FAILED, restoring;";
$original_fname = "$n";
$result = copy($original_fname, $tname);
if ($result == false )
die(" TOTAL FAILTURE: copy failed.");
else
echo " RESTORED;";
}
else
{
if ($fsize === 0)
echo "! THE FILE WAS NOT WRITTEN: data length: ".strlen($s)." fsize: $fsize RESOURCE: $fp
";
if ( $success )
touch("$tname",$fsize,$p);
}
} else {
echo "Couldn't get the lock!";
}
$success = rmdir("$n.t"); // remove atomic fuse
if ( $success )
echo "DIR REMOVED
";
else
echo "DIR NOT REMOVED
";
} // start
else
echo "skipped";
$time_elapsed_secs = microtime(true) - $start_time;
if ( $time_elapsed_secs === 0 )
echo " FAILED ";
echo "time: $time_elapsed_secs s
";
} // for
}
switch ( $_SERVER['HTTP_USER_AGENT'] ):
case "":
$p = 1; break;
case "":
$p = 2; break;
case "":
$p = 3; break;
endswitch;
copy("523","523.txt");
copy("948","948.txt");
copy("1371","1371.txt");
copy("1913","1913.txt");
copy("2701","2701.txt");
copy("4495","4495.txt");
copy("6758","6758.txt");
test("523",$p,$_DEBUG_);
test("948",$p,$_DEBUG_);
test("1371",$p,$_DEBUG_);
test("1913",$p,$_DEBUG_);
test("2701",$p,$_DEBUG_);
test("4495",$p,$_DEBUG_);
test("6758",$p,$_DEBUG_);
Note: T5-T7 - I did not determinated whether the file damages were made by fflush or fwrite, but it was in these tests, where these error occures.
Note: T8 - Specific problem with this test is, that it often waits too long on begin of a testing block (on the begin of the testing function). There even delays like 7 seconds waiting. But I also tried to remove these numbers and the avarage does not change too much, so the curve of T8 would stay the same after this change. The problem here is that using the delay in a loop is not ideal solution of the problem, it makes the probability of failture even higher. Note, that by "failture" I do not really mean file corruption but skipping of the given atomic task because time out.