8

I have a file I'm writing to, but I need to lock it first (using flock()), to prevent any other script from writing to it. So I have:

$file=fopen($file_p);

if (flock($file, LOCK_EX)) {//lock was successful
    fwrite($file,$write_contents);          
}

But I need to check if it's already locked, to prevent other scripts from writing to it.

How can I do this?

Mark Amery
  • 143,130
  • 81
  • 406
  • 459

3 Answers3

15

As described in the docs, use LOCK_NB to make a non-blocking attempt to obtain the lock, and on failure check the $wouldblock argument to see if something else holds the lock.

if (!flock($fp, LOCK_EX|LOCK_NB, $wouldblock)) {
    if ($wouldblock) {
        // something already has a lock
    }
    else {
        // couldn't lock for some other reason
    }
}
else {
    // lock obtained
}
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
y o
  • 1,143
  • 9
  • 14
-1

Your flock call is the check to see if it's already locked. If it's locked, that if() statement would fail, so you could just throw an else on it with something like:

if (flock($file, LOCK_EX)) {//lock was successful
    fwrite($file,$write_contents);
} else {
    echo "$file is locked.";
}
Andrew Kandels
  • 1,287
  • 1
  • 8
  • 10
-2

I would check to see if I couldn't obtain a lock on the file, like this:

if (!flock($file, LOCK_EX)) {
    throw new Exception(sprintf('Unable to obtain lock on file: %s', $file));
}

fwrite($file, $write_contents);
Mike Purcell
  • 19,847
  • 10
  • 52
  • 89
  • 2
    This is incorrect. If you don't specify the `LOCK_NB` bitmask on the operation passed to flock, then *by default, this function will block until the requested lock is acquired* ([source](http://www.php.net//manual/en/function.flock.php)). With the example code you've given, `flock` will *not* return false to indicate a lock has been taken out on the file by another process, but will simply wait until that process releases its lock. – Mark Amery Jun 22 '14 at 20:29
  • @MarkAmery: Incorrect how? The logic is simple, If you cannot obtain an `exclusive lock` on the file, then throw the exception. It's up to the calling code to figure out how to handle the exception. – Mike Purcell Jun 22 '14 at 21:19
  • No - in the case that the reason for being unable to acquire the lock is that another process holds it, your comment above does not match how your code behaves. Instead the behaviour your code has is "If you cannot obtain an exclusive lock on the file, keep waiting until you can, then continue." - this is `flock`'s default behaviour. You need to use the `LOCK_NB` modifier, as shown in Ryan Y's answer, to make your code behave the way that your comment describes. – Mark Amery Jun 23 '14 at 11:19
  • That's what I am saying, I am ok with waiting until I can obtain an EXCLUSIVE lock on the file. For example, if you implement a queue based processing algorithm where 12-14 parallel processes can be running at the same time, each needing to write to the same file (i.e. log file), then I want to ensure each process obtains the exclusive lock, if it cannot, then wait until it can. I've never run into a situation where the wait time became an issue. – Mike Purcell Jun 23 '14 at 14:09
  • sure, but that's not an answer to the question the OP asked, which was how to *check* if the file is already locked. As I initially understood both your answer and your previous comment, you were saying that your code would perform that check and throw the exception if another process holds the lock. It sounds like it wasn't your intent to imply this, but offering the snippet above as an answer to "How do I check if a file is already locked?" without noting that you're offering an alternative approach is misleading. – Mark Amery Jun 23 '14 at 14:19
  • Additionally, there *are* situations in which you really do want to test if you can acquire the lock and die if not. For instance, if you have a long-running daemon that should never die but are worried about some unexpected error killing it, then as a fallback system you might want to use a crontab to repeatedly create new instances of the daemon that immediately die if another instance is running and holds the lock. In that case, using a blocking approach (like yours) instead of a checking approach (like Ryan's) would create a huge unnecessary backlog of processes waiting for the lock. – Mark Amery Jun 23 '14 at 14:21
  • "then as a fallback system you might want to use a crontab to repeatedly create new instances of the daemon that immediately die if another instance is running and holds the lock", yes we know what lock files are for. The only difference I see between mine and Ryan's answer is that he can programmatically do something if unable to obtain the lock on the file, like what? Sleep for 60 seconds and try again? Why would I assume that work, rather than let PHP internally handle that. If we can't use the return value of `flock` and check for a false, then why is it even available? – Mike Purcell Jun 23 '14 at 14:36
  • *"The only difference I see between mine and Ryan's answer is that he can programmatically do something if unable to obtain the lock on the file, like what?"* - like ***exit***, as I already said in the previous comment. With your approach, cron will spawn more and more processes endlessly, all waiting for the lock, until it takes down the whole box. Blocking calls to `flock` absolutely have their place, but a using one to acquire the lock that a long-running crontab script needs to run is just plain wrong and dangerous. – Mark Amery Sep 27 '15 at 13:11
  • I know you can't delete it yourself, but it would be helpful if you'd flag this answer to be deleted by the mods; there's a duplicate question at http://stackoverflow.com/questions/20771824/php-test-if-file-is-locked that I'd like to close as a dupe of this one, but I'm reluctant to do so with an incorrect answer still at the top. If I still haven't convinced you that this answer is wrong but you're willing to be engaged further, feel free to hit me up on chat or via email. – Mark Amery Dec 27 '15 at 00:44