Function With Possible Race Condition

    Date: 10/03/05 (PHP Community)    Keywords: php

    Hi, everyone. I've written a library function that opens a file (for reading or writing) safely, including file locking and error reporting. It also retries a locked file a few times on the theory that any lock is likely to be released after only a second or two, and it's better to delay output for a couple of seconds (but then give the user something useful) than to just try once and then give up with an error message.

    Since this is a very low-level routine that I'll be relying on heavily in a variety of other projects later on, I want it to be really robust. Completely bulletproof would be nice, actually. And the function already works fine in the few low-traffic situations I've deployed it in; I'm not having any of the usual "why can't I get this to work?" problems. Instead, I'm concerned that there may be a possible race condition lurking in the space between the fopen() and flock() calls, and I'd like to get some other people's input about it. Here's the function code itself, with minimal comments to show the calling syntax and point out the area I'm concerned about:

    function safeopen ($filename, &$errmsg, $mode  = 'r', $lock = LOCK_EX) {
    
    # Required arguments are: file to be opened, and a variable that 
    # error messages will be placed in. Optional arguments are:
    # whether to open read-only or read/write, and the locking mode.
    
    # Will return a filehandle on success; on failure, returns false and
    # places an error string in the $errmsg variable.
    
    $retry_msecs = 250;
    $timeout_secs = 5;
    
    global $php_errormsg;
    
    $elapsed = 0;
    while (! $fh) {
    	# This will always fail the first time around.
    	if ($elapsed >= $timeout_secs * 1000) {
    		if (strlen($errmsg)) {
    			$errmsg = "Couldn't open file: ".$errmsg;
    		} else {
    			$errmsg = "Couldn't open file; no other error data available.";
    		}
    		return false;
    	}
    	$fh = @fopen($filename, $mode);
    	if (! $fh) {
    		$errmsg = $php_errormsg;
    		usleep($retry_msecs * 1000);
    		$elapsed += $retry_msecs;
    	}
    }
    
    # Note that there's a gap between the fopen() call in the previous
    # loop, and the flock() call coming up. This is what I'm worried about.
    
    while (! @flock($fh, $lock+LOCK_NB)) {
    	$errmsg = $php_errormsg;
    	if ($elapsed >= $timeout_secs * 1000) {
    		if (strlen($errmsg)) {
    			$errmsg = "Couldn't acquire lock on file: ".$errmsg;
    		} else {
    			$errmsg = "Couldn't acquire lock on file.";
    		}
    		return false;
    	}
    	usleep($retry_msecs * 1000);
    	$elapsed += $retry_msecs;
    }
    
    return $fh;
    
    }
    As you can see, I'm worried about the gap in execution between the fopen() and flock() calls. More specifically: since these two operations aren't being handled atomically, there seems to be a chance that one instance of safeopen() could open a file, and then before it managed to flock() it, a second instance might also open the same file.

    In that case, I'm unsure what happens when each instance attempts to lock the file. Does one of them succeed in locking, and then the other one fails? What happens if the first one only needs a shared lock, and then the second one wants an exclusive lock? Does it fail because the first one already has the shared lock?

    Note that in the last paragraph, "the first one" and "the second one" simply mean "whichever one first manages to acquire a lock" — I think there's no way to guarantee that the safeopen() instance that first started execution will also be the first one to acquire a lock. And I'm okay with that.

    Anyway, I think part of my worry here is that I'm somewhat new to file locking, and don't completely understand exactly how it plays out in marginal situations like this. So if someone can fill me in on the nitty-gritty details, I'd be most appreciative.

    Source: http://www.livejournal.com/community/php/348860.html

« Introduction and question || onsubmit? »


antivirus | apache | asp | blogging | browser | bugtracking | cms | crm | css | database | ebay | ecommerce | google | hosting | html | java | jsp | linux | microsoft | mysql | offshore | offshoring | oscommerce | php | postgresql | programming | rss | security | seo | shopping | software | spam | spyware | sql | technology | templates | tracker | virus | web | xml | yahoo | home