5

By referring to flock(): removing locked file without race condition? and Will flock'ed file be unlocked when the process die unexpectedly? ,

I produce the following code. My intention is to allow only single thread / single process to run the critical section code, within any given time.

<?php

// Exclusive locking based on function parameters.
$lockFileName = '/tmp/cheok.lock';
// Create if doesn't exist.
$lockFile = fopen($lockFileName, "w+");

if (!flock($lockFile, LOCK_EX)) {
    throw new \RumtimeException("Fail to perform flock on $lockFileName");
}

echo "start critical section...\n";
sleep(10);
echo "end critical section.\n";

// Warning: unlink(/tmp/cheok.lock): No such file or directory in
unlink($lockFileName);
flock($lockFile, LOCK_UN);

I will always get the warning

Warning: unlink(/tmp/cheok.lock): No such file or directory in

when the 2nd waiting process continues his execution, the 1st process already remove the physical disk file. The 2nd process tries to unlink file, which is already deleted by the 1st process.

And, what if, there is 3rd process join in, and tries the perform fopen while 2nd process is trying to perform unlink?

In short, what is the correct way to perform lock file cleanup?

Community
  • 1
  • 1
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875
  • Why delete it at all. Leave the file there and if you can get a lock on it, then you can continue with the critical process. If not, some other process is in control, you wait, and try again later. Afterall you are not interested in its existance, just that you can get an exclusive lock on it. – RiggsFolly Oct 02 '15 at 10:22
  • 1
    I'd love me some RumTime ;) – hank Oct 02 '15 at 10:30
  • @RiggsFolly This is just a demo code. In production code, we will be creating many files with different name, based on locking condition. If we don't remove the file, our server disk will be full soon :) – Cheok Yan Cheng Oct 02 '15 at 10:40
  • Why, they contain no data, all you are interested in is there existance. And if your file system cannot cope with all the possible files being created at the same time then this method will cause problems when that MAX is reached during live processing – RiggsFolly Oct 02 '15 at 10:48

2 Answers2

3

The question is tagged with , so I'm going to answer the question in the context of multi-threading with pthreads.

First, a little bit about file locking. The PHP manual fails to fully explain what an advisory lock is.

Taken from the man page for flock

flock() places advisory locks only; given suitable permissions on a file, a process is free to ignore the use of flock() and perform I/O on the file.

In a ecosystem filled with people who simultaneously don't know much about permissions, and use cron jobs to execute long running scripts, this can be the cause of much pain ... although nobody really knows it.

In the context of multithreading (with pthreads), you want to stay well away from file locking, pthreads provides a much superior API for implementing mutual exclusion.

Follows is example code that creates a number of Threads, and implements mutual exclusion:

<?php
class Test extends Thread {

    public function __construct(Threaded $monitor) {
        $this->monitor = $monitor;
    }

    public function run () {
        $this->monitor->synchronized(function(){
            for ($i = 0; $i < 1000; $i++)
                printf("%s #%lu: %d\n",
                    __CLASS__, $this->getThreadId(), $i);
        });
    }

    private $monitor;
}

$threads = [];
$monitor = new Threaded();
for ($i = 0; $i < 8; $i++) {
    $threads[$i] = new Test($monitor);
    $threads[$i]->start();
}

foreach ($threads as $thread) 
    $thread->join();

Because all the Threads share the same $monitor, you can use the synchronization built into Threaded objects to implement reliable mutual exclusion, causing the output to resemble:

Test #140561163798272: 0
<-- snip for brevity -->
Test #140561163798272: 999
Test #140561151424256: 0
<-- snip for brevity -->
Test #140561151424256: 999
Test #140561138841344: 0
<-- snip for brevity -->
Test #140561138841344: 999
Test #140561059149568: 0
<-- snip for brevity -->
Test #140561059149568: 999
Test #140561050756864: 0
<-- snip for brevity -->
Test #140561050756864: 999
Test #140561042364160: 0
<-- snip for brevity -->
Test #140561042364160: 999
Test #140561033971456: 0
<-- snip for brevity -->
Test #140561033971456: 999
Test #140561025578752: 0
<-- snip for brevity -->
Test #140561025578752: 999

We can see that each Thread is mutually excluded from executing the code in the Closure passed to Threaded::synchronized.

Joe Watkins
  • 17,032
  • 5
  • 41
  • 62
  • `given suitable permissions on a file, a process is free to ignore the use of flock() and perform I/O on the file` do you happen to know what those permissions are? The docu page seems a tad vague on in. – TMH Oct 21 '15 at 16:52
0

You can unlock a file by using the excellent Process Explorer task manager. We’ve covered Process Explorer in detail before, so here we’ll just dive right into how to unlock a file. You won’t need to install it first—it’s a portable app—but you will need to run it with administrative privileges. You actually can do this from within Process Explorer by clicking the “File” menu and selecting “Show Details for All Processes.”

anna
  • 1
  • (Not *quite* sure I'd like to see more answers like this, otherwise: Welcome to SO!) `We’ve covered…` *Who* are you? `excellent Process Explorer task manager` *What* `Process Explorer`? Is this a product placement? – greybeard Dec 29 '18 at 07:11