php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #31363 Non-blocking flock() broken
Submitted: 2004-12-31 06:41 UTC Modified: 2005-04-06 15:59 UTC
Votes:3
Avg. Score:5.0 ± 0.0
Reproduced:3 of 3 (100.0%)
Same Version:2 (66.7%)
Same OS:2 (66.7%)
From: ian at snork dot net Assigned: ilia
Status: Closed Package: Filesystem function related
PHP Version: 5CVS-2005-03-09 OS: Debian woody (i386)
Private report: No CVE-ID:
 [2004-12-31 06:41 UTC] ian at snork dot net
Description:
------------
This code works in PHP 4.3.0, but does not in PHP 5.0.3.    
        
When invoked, it will try to lock /tmp/test1, /tmp/test2      
or /tmp/test3 for exclusive use, whichever is free. If      
none are, it will wait a second before cycling through      
with another attempt.   
   
When running concurrently, the first invocation correctly   
picks up /tmp/test1; the second and subsequent ones just   
cycle forever.   
      
For some reason, the non-blocking flock() only sets $block  
the first time.  

Reproduce code:
---------------
while (!sleep(1))
  foreach (array("/tmp/test1", "/tmp/test2", "/tmp/test3") as $path)
    if (flock($handle = fopen($path, "w"), LOCK_EX | LOCK_NB, $block) and !$block)
    {
      echo "Got $path\n"; 
      sleep(10); 
      echo "Releasing $path\n"; 
      exit; 
    }


Expected result:
----------------
$ php5 flock_test.php &  
Got /tmp/test1   
$ php5 flock_test.php &  
Got /tmp/test2   
$ php5 flock_test.php &  
Got /tmp/test3   
$ php5 flock_test.php &  
Releasing /tmp/test1  
Got /tmp/test1  
$ 
    

Actual result:
--------------
$ php5 flock_test.php & 
Got /tmp/test1 
$ php5 flock_test.php & 
$ php5 flock_test.php & 
$ php5 flock_test.php & 
$ 

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2005-02-28 21:06 UTC] sniper@php.net
Please try using this CVS snapshot:

  http://snaps.php.net/php5-latest.tar.gz
 
For Windows:
 
  http://snaps.php.net/win32/php5-win32-latest.zip


 [2005-03-09 17:29 UTC] ian at snork dot net
flock() in today's PHP CVS snapshot (http://snaps.php.net/php5-latest.tar.bz2) behaves slightly differently, but still doesn't quite work correctly in non-blocking mode.

Expected result:
--------------
$ php5 flock_test.php &  
Got /tmp/test1   
$ php5 flock_test.php &  
Got /tmp/test2   
$ php5 flock_test.php &  
Got /tmp/test3   
$ php5 flock_test.php &  
$
Releasing /tmp/test1
Got /tmp/test1
Releasing /tmp/test2
Releasing /tmp/test3


Actual result:
--------------
$ php5 flock_test.php &  
Got /tmp/test1   
$ php5 flock_test.php &  
Got /tmp/test2   
$ php5 flock_test.php &  
Got /tmp/test3   
$ php5 flock_test.php &  
$
Releasing /tmp/test1
Releasing /tmp/test2
Releasing /tmp/test3
 [2005-04-01 14:42 UTC] maka3d at yahoo dot com dot br
I expected it to be fixed until 5.0.4.
On freeBSD it doesn't work too :(
 [2005-04-05 01:55 UTC] maurice at gitaar dot net
I had the same problem. It seems that errno is not reset to zero in the flock implementation. Therefore the last errno will be used. In case no other errno-modifying call is made before retrying the flock() call, the call will conclude in an EWOULDBLOCK situation (my conclusion after a very quick scan of the code).

Looking with truss on Solaris to the process shows that the first fcntl call (during which I have already locked the file) returns an EAGAIN error, meaning that the file was already locked. The second fnctl call (on the now unlocked file) does not return an error. So the call is successfull. But the call from the php code does set the $wouldblock parameter to a true value.

For me a workaround for now is something like this (no error checking on flock and a bit of pseudo-coded, but the workaround is the important part):

for (;;)
{
    flock($fd, LOCK_EX|LOCK_NB, $wouldblock);
    if (! $wouldblock) break;
    fopen("/this/is/no/real/file", "r");
}

The fopen() call sets errno to the file not found error, which makes the flock() call act like the first time it is called. This is not great coding, but it does work for me till a real fix is implemented in PHP.
 [2005-04-05 17:53 UTC] ian at snork dot net
The cause looks to be two separate bugs; here's a patch to 
fix both. 
 
*** php5-200504050030.orig/ext/standard/file.c	Sun Mar 27 
16:30:05 2005 
--- php5-200504050030/ext/standard/file.c	Tue Apr  5 
15:26:08 2005 
*************** 
*** 339,351 **** 
   
  	/* flock_values contains all possible actions if 
(operation & 4) we won't block on the lock */ 
  	act = flock_values[act - 1] | (operation & 4 ? 
LOCK_NB : 0); 
! 	if (!php_stream_lock(stream, act)) { 
  		if (operation && errno == EWOULDBLOCK && 
arg3 && PZVAL_IS_REF(arg3)) { 
  			Z_LVAL_P(arg3) = 1; 
  		} 
! 		RETURN_TRUE; 
  	} 
! 	RETURN_FALSE; 
  } 
   
  /* }}} */ 
--- 339,351 ---- 
   
  	/* flock_values contains all possible actions if 
(operation & 4) we won't block on the lock */ 
  	act = flock_values[act - 1] | (operation & 4 ? 
LOCK_NB : 0); 
! 	if (php_stream_lock(stream, act)) { 
  		if (operation && errno == EWOULDBLOCK && 
arg3 && PZVAL_IS_REF(arg3)) { 
  			Z_LVAL_P(arg3) = 1; 
  		} 
! 		RETURN_FALSE; 
  	} 
! 	RETURN_TRUE; 
  } 
   
  /* }}} */ 
*** php5-200504050030.orig/main/streams/plain_wrapper.c	
Thu Oct 28 06:35:19 2004 
--- php5-200504050030/main/streams/plain_wrapper.c	
Tue Apr  5 15:45:51 2005 
*************** 
*** 578,584 **** 
  				return 0; 
  			} 
   
! 			if (!flock(fd, value) || (errno == 
EWOULDBLOCK && value & LOCK_NB)) { 
  				data->lock_flag = value; 
  				return 0; 
  			} else { 
--- 578,584 ---- 
  				return 0; 
  			} 
   
! 			if (!flock(fd, value)) { 
  				data->lock_flag = value; 
  				return 0; 
  			} else {
 [2005-04-05 17:57 UTC] tony2001@php.net
Please, generate diffs with `diff -upd` and put them somewhere in the Net.
Thanks you.
 [2005-04-05 17:59 UTC] ian at snork dot net
Aw, shucks. It's been word-wrapped. 
 
Oh well -- send mail if you need an unmolested copy of the 
patch. :)
 [2005-04-05 18:01 UTC] tony2001@php.net
The URL to the patch would be much better.
 [2005-04-05 18:03 UTC] ian at snork dot net
My apologies -- I posted just before your message from 
bugs.php.net arrived. :) 
 
http://www.snork.net/flock.diff
 [2005-04-06 15:59 UTC] iliaa@php.net
This bug has been fixed in CVS.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.


 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 16 04:02:11 2014 UTC