5

I am trying to simulate a file writing on a busy site. I have written a following code which eventually end up freezing computer.

$loop = 10000;
$sleep = 500000;
$i =0;

while($i < $loop) {

    $mtime = microtime();
    $mtime = explode(" ",$mtime);
    $mtime = $mtime[1] + $mtime[0];
    $starttime = $mtime; 

    $handler = fopen($file,"a+");
    if($handler) {
    if (flock($handler, LOCK_EX)) {
        $mtime = microtime();
        $mtime = explode(" ",$mtime);
        $mtime = $mtime[1] + $mtime[0];
        $endtime = $mtime;
        $totaltime = ($endtime - $starttime); 

        fwrite($handler,"Script 1 took $totaltime secs\n");
    }

    flock($handler, LOCK_UN);
    fclose($handler);
}
$i++;
usleep($sleep);
}

I can't use LOCK_NB because it will not work on windows. The code works fine if there are less than 13 processes execute the above code at the same. How do I cope with this deadlock situation?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Maximus
  • 2,906
  • 4
  • 35
  • 55
  • 2
    A deadlock between a few PHP processes shouldn't freeze your entire system. Are you sure it's a deadlock? – Matti Virkkunen Apr 24 '11 at 17:10
  • Yes, tried two times and end up rebooting system. I have created 40 php processes. – Maximus Apr 24 '11 at 17:12
  • 1
    LOCK_NB seems to be working on Windows, despite what the docs say: http://bugs.php.net/bug.php?id=54129 – magma Apr 24 '11 at 17:20
  • 1
    I agree, that there should be no deadlock, but some waiting for the processes. If your system freezes, it could be something else. Why append to a file, and not use a database? – Erik Apr 24 '11 at 17:28
  • I am running the same code on CentOS and there is no issue with that, so it could be windows incompatibility with flock. – Maximus Apr 24 '11 at 18:00
  • 1
    If you do `$starttime = microtime(TRUE)`, you'll get the time value as a simple float, saving you the explode/addition step. – Marc B Apr 24 '11 at 19:11
  • Jason, did you ever figure out what was causing your apparent deadlock? – Peter Jun 14 '13 at 19:38

4 Answers4

2

Reading your code, I'm thinking you should move flock($handler, LOCK_UN); inside the if (flock($handler, LOCK_EX)) {} conditional block.

To figure out exactly what's getting stuck and where, I would add datestamped (and flushed, so nothing gets stuck in the output buffer) debug output both before and after each call to flock(), fopen(), fwrite(), and fclose(), and redirect the output from each script instance to its own file.

Then, after a freeze + restart you can review the end of each file and see what each script was doing when you restarted. By comparing the datestamps you should be able to see which script(s) "froze" first.

Philipp Ludwig
  • 3,758
  • 3
  • 30
  • 48
Peter
  • 2,526
  • 1
  • 23
  • 32
  • Moving the `LOCK_UN` into the `if` prevents exactly the dead lock. Being outside it might happen that processes waste the `LOCK_UN` for non existent locks and therefore stacks blocked processes. – Markus Malkusch Nov 20 '14 at 13:28
1

Hy

Try with file_put_contents():

<?php

$file = 'file.txt';

$str = "some text\n";

file_put_contents($file, $str, FILE_APPEND | LOCK_EX);

?>

CoursesWeb
  • 4,179
  • 3
  • 21
  • 27
1

Before you write some more php code I recommend you use the application AB (apache benchmark) that comes with apache and simulate a high load in your local host EX>

ab -n 1000 -c 200 http://localhost/your.php

With this you can simulate 200 concurrent users and 1000 requests.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
Yuri
  • 11
  • 1
0

Try to "guard" the LOCK_EX with an additional mkdir() lock-mechanism as described here: https://stackoverflow.com/a/7927642/318765

Example:

<?php
$file = 'deadlock.txt';
$loop = 10000;
$sleep = 500000;
$i = 0;
while ($i < $loop) {
    $starttime = microtime(true);
    $handler = fopen($file, 'a+');
    if ($handler) {
        if (!file_exists($file . '_lock')) {
            if (mkdir($file . '_lock')) {
                if (flock($handler, LOCK_EX)) {
                    $endtime = microtime(true);
                    $totaltime = ($endtime - $starttime);
                    $totaltime = number_format($endtime - $starttime, 10);
                    if ($totaltime > 1) {
                        break;
                    }
                }
                flock($handler, LOCK_UN);
                fclose($handler);
                rmdir($file . '_lock');
            }
        }
    }
    $i++;
    usleep($sleep);
}
?>

As you can see I added a break; to avoid the deadlock. If the scripts stops you can view into the log file.

In Linux I'm using this mkdir() trick without flock() as it is atomic. I don't know if this works in Windows, too.

Community
  • 1
  • 1
mgutt
  • 5,867
  • 2
  • 50
  • 77