flock

Description

flock() allows you to perform a simple reader/writer
model which can be used on virtually every platform (including most Unix
derivatives and even Windows).

On versions of PHP before 5.3.2, the lock is released also by
fclose() (which is also called automatically when script
finished).

PHP supports a portable way of locking complete files in an advisory way
(which means all accessing programs have to use the same way of locking
or it will not work). By default, this function will block until the
requested lock is acquired; this may be controlled (on non-Windows
platforms) with the LOCK_NB option documented below.

Notes

Note:

flock() uses mandatory locking instead of advisory
locking on Windows. Mandatory locking is also supported on Linux and
System V based operating systems via the usual mechanism supported by the
fcntl() system call: that is, if the file in question has the setgid
permission bit set and the group execution bit cleared. On Linux, the file
system will also need to be mounted with the mand option for this to work.

Note:

Because flock() requires a file pointer, you may have
to use a special lock file to protect access to a file that you intend
to truncate by opening it in write mode (with a "w" or "w+" argument to
fopen()).

Assigning another value to handle argument in
subsequent code will release the lock.

Warning

On some operating systems flock() is implemented at
the process level. When using a multithreaded server API like ISAPI you
may not be able to rely on flock() to protect files
against other PHP scripts running in parallel threads of the same server
instance!

flock() is not supported on antiquated filesystems like
FAT and its derivates and will therefore always
return FALSE under this environments (this is especially true for
Windows 98 users).

User Contributed Notes 33 notes

The supplied documentation is vague, ambiguous and lacking, and the user comments contain erroneous information! The flock function follows the semantics of the Unix system call bearing the same name. Flock utilizes ADVISORY locking only; that is, other processes may ignore the lock completely; it only affects those that call the flock call.

LOCK_SH means SHARED LOCK. Any number of processes MAY HAVE A SHARED LOCK simultaneously. It is commonly called a reader lock.

LOCK_EX means EXCLUSIVE LOCK. Only a single process may possess an exclusive lock to a given file at a time.

If the file has been LOCKED with LOCK_SH in another process, flock with LOCK_SH will SUCCEED. flock with LOCK_EX will BLOCK UNTIL ALL READER LOCKS HAVE BEEN RELEASED.

If the file has been locked with LOCK_EX in another process, the CALL WILL BLOCK UNTIL ALL OTHER LOCKS have been released.

If however, you call flock on a file on which you possess the lock, it will try to change it. So: flock(LOCK_EX) followed by flock(LOCK_SH) will get you a SHARED lock, not "read-write" lock.

I just want to add a note about making atomic lock on NFS, there is only twoways:

- 1 (the most robust but the most complicate) - It's to use link() to create a hard link to a file you want to lock (on the same FS of course). (On most NFS implementations, Link() is atomic)

Once you created a hard link (not a symbolic link), with a unique randomlygenerated name, call stat() on it and count the number of link (nlink), if thereis only 2 then the file is locked.

If there is more than two you have to unlink() the link you just created andcreate a new one with a new unique name (else NFS will use its cache and statwill return wrong data) then call stat() on the new link and test the number oflinks again, repeat this operation until you get the lock.

You have to use usleep() between the link() attempts with a fixed + randomsleep value to avoid dead lock situations (link() and unlink() may be atomicbut not instantaneous)

Also note than when you unlink a file through NFS, if NFS think that the fileis still in use, it will create a .nfs link to this file until it realizes thefile is no longer in use... A wrong timing could generate thousands of thosefiles and a deadlock situation. Because of this when a deadlock situationoccurs or if your stat() command returns a very high number of links, you haveto look for .nfs file in the same directory you created your links and unlinkall the .nfs file you find (sometimes NFS take its time to remove them)

- 2 (the simplest) - the second method is to use a lock server and lock daemons on each client that will forward lock request to the server... (this is moredangerous than the first method because the daemons may be killed...)

Here is for reference the function I created to make atomic locks through NFS(this function is in production since at least 4 years now), it's just forreference because it uses many external functions to do its job but you can seethe principle:

I have found that if you open a currently locked file with 'w' or 'w+' ("file pointer at the beginning of the file and truncate the file to zero length") then it will not truncate the file when the lock is released and the file available.

As you can see, b.php does not truncate the file as the w+ would suggest if the file were instantly available. But only moves the pointer to the begining of the file. If b.php was loaded after a.php finished then there would be no "a ..." lines in the file, since it would be truncated.

Here's a handy class to allow retrying a write with flock a set number of times. If it can't flock, it will sleep for a brief random interval and try again. If you have a lot of concurrent writes going on, you can use it to avoid data corruption.

The problem is, if you have a busy site and a lots of locking, the while loop may not acquire the lock for some time. Locking without LOCK_NB is much more persistent and it will wait for the lock for as long as it takes. It is almose guaranteed that the file will be locked, unless the script times out or something.

Consider these two scripts: 1st one is ran, and the second one is ran 5 seconds after the first.

If you run 1st and then the 2nd script,the 2nd script will wait untill the 1st has finished. As soon as the first script finishes, the second one will acquire the lock and finish the execution. If you use flock($file_handle, LOCK_EX | LOCK_NB) in the 2nd script while the 1st script is running, it would finish execution immediately and you would not get the lock.

Note that Example #1 contains a bug: ftruncate() does *not* re-set the file pointer to the beginning of the file. You need to execute a call to rewind() afterward. I realize that the ftruncate page does mention this, but if anybody copies the example above (as I did), their program will not work correctly unless they fix this.

I've been having trouble getting Flock to work when I read a file, delete it, and then output slightly changed information back to the same location. When deleting with Unlink, there's a very brief period of time where no file exists. But, if you do an fopen using the "w" mode, it keeps the file in existence, but deletes all of its data when you go to write to it. That way, the file never actually disappears, and another script accessing the same file with flock won't get a "file doesn't exist" error.

flock on Solaris is slightly strange: it will fail if you try to get an exclusive lock on a file not opened for writing. That is, for reading files, you MUST use a shared lock. From the Solaris man page for flock:

"Read permission is required on a file to obtain a shared lock, and write permission is required to obtain an exclusive lock."

now the file will have the size of $data without opening the file in w mode but with a lock on the file.

to the previous writers jpriebe and mallory:of course the lock is lost in this case, but thats simply because the file is closed by PHP. and closing the file means unlocking it (same as when you use fclose() yourself).

Just a comment about the last method to lock files using filemtime().What if filemtime($fp[1]) == $fp[3] because somebody modified the file less than 1s after the value of $fp[3] was picked up?Then this modification will be lost...?

This system to lock files is made to prevent problems when two modifications are so close that they can interfere, so the case "less than 1s" will often happen?

I've been testing a few custom file access functions but I always liked the simplicity of file_get_contents(). Of course it doesn't seem to respect any file locks created with flock(). I created the function below to wrap around file_get_contents() so it can support locked files. It's an odd way of doing it but it works for me.

Indeed, flock() will not work reliably when the underlying filesystem is NFS. The proper way to perform file locking, in this case, would be to use PHP's link() function. From the Linux man page of open():

O_EXCL When used with O_CREAT, if the file already exists it is an error and the open will fail. In this context, a symbolic link exists, regardless of where its points to. O_EXCL is broken on NFS file systems, programs which rely on it for performing lock- ing tasks will contain a race condition. The solution for per- forming atomic file locking using a lockfile is to create a unique file on the same fs (e.g., incorporating hostname and pid), use link(2) to make a link to the lockfile. If link() returns 0, the lock is successful. Otherwise, use stat(2) on the unique file to check if its link count has increased to 2, in which case the lock is also successful.

just wanted to say that you will most likely fail if you use a separate lock file together with register_shutdown_function.

my script did some different actions... resizing pictures, rotating them and this stuff. it needed a "database" file to get the correct file locations. this database file also stored some flags. and of course the script had to save that file when it was done.

because of my script exited on many different points depending on the action i used register_shutdown_function to save the file. it wanted to use a locking system to be sure the script doesn't overwrite the data another process had written into it some microseconds before. i was running on windows 2000 and apache2 on my developing machine, and flock always returned true for some reason... so i used a separate lock file. the script looked for it at the beginning and exited if it was found. otherwise it created it. but this file had to be deleted at the end. i put the unlink command into the registered shutdown-function but it never deleted the file. i tried clearstatcache and some other stuff but it didn't help.

Like a user already noted, most Linux kernels (at least the Redhat ones) will return false, even if you locked the file. This is because the lock is only ADVISORY (you can check that in /proc/locks). What you have to do there is to evalute the 3rd parameter of flock(), $eWouldBlock. See for an example below. Note however that if youlock the file in non blocking mode, flock() will work as expected (and blocks the script).

I'm thinking that a good way to ensure that no data is lost would be to create a buffer directory that could store the instructions for what is to be written to a file, then whenever the file is decidedly unlocked, a single execution could loop through every file in that directory and apply the indicated changes to the file.

I'm working on writing this for a flat-file based database. The way it works is, whenever a command is issued (addline, removeline, editline), the command is stored in a flat file stored in a folder named a shortened version of the filename to be edited and named by the time and a random number. In that file is a standardized set of commands that define what is to be done to what file (the likes of "file: SecuraLog/index_uid" new line "editline: 14").

Each execution will check every folder in that directory for files and a certain amount of time (I don't know how long, maybe 1-2 seconds) is spent making pending changes to unlocked files. This way no changes will be lost (i.e. person 1 makes a change at the same time as person 2, and person 1 loses the race by just enough to have their changed version of the file overwritten by person 2's version) and there will be no problems with opening an empty open file.

"Assigning another value to handle argument in subsequent code will release the lock."Note: this is also true for losing the handle's context completely (like returning from a function, in which the handle was a local variable).

This will lock the testfile, then attempt to lock it again with a new file handle (obviously failing). Trying this again, though, results in proper locking again (and then failing again), because when exiting the function both handles are lost and automatically unlocked.

Because:1) flock() is not safe if multiple php sessions are simultaneously locking.2) fopen( , 'a') is not safe if multiple php sessions are simultaneously appending.3) usleep and sleep are known for having problems.

- When the lockfile is acquired the file named: $directory.date('Ymd').$logfile.1 (or .2 or .3 or .25) is opened or created.- If created the a "extended log file" header is written.- Write out the line.- Close the flie and if created set the correct access rights. (I had some problems creating files on a webserver, I did not see them when I opened a FTP session to the webdirectory. The chmod did the trick for me).

- Remove the lockfile.

There is only one drawback, multiple logfiles are created.e.g. (executed on 15 september 2010) Weblog('./' , 'visit', 'Somebody requested the index page');Could lead to 100 files, depending how many concurrent php sessions are simultaneously trying to append a logline:./20100915visit.1./20100915visit.2 ./20100915visit.3./20100915visit.4..../20100915visit.100

This function is donated to the public domain. Maybe you can give me some credit by leaving in the author comment, but it is not required. You may modify this as you wish.(This function was inspired by the function m_lock_file presented by Kuzma dot Deretuke at gmail dot com)