Release of Flock in Case of Errors

Release of flock in case of errors?

In that case, is the lock ever released by the operating system?

Does it see "hey, the script that acquired the lock crashed" and release the lock?

Does it release the lock immediately?

All of these questions are system dependent. Perl 5 does not implement a file locking function, it just provides a common interface to flock(2), fcntl(2) locking, or lockf(3) (depending on what is available in the OS). There may also be a difference between what happens when a program exits, segfaults, or is killed with a sigkill.

A quick test under Linux shows that a lock is removed under normal exit conditions:

$ perl -le 'open my $fh, ">", "f" or die $!; print flock($fh, 6) ? "got lock" : "was already locked", "\n"'
got lock
$ perl -le 'open my $fh, ">", "f" or die $!; print flock($fh, 6) ? "got lock" : "was already locked", "\n"'
got lock

Let's see what happens when we die:

$ perl -le 'open my $fh, ">", "f" or die $!; print flock($fh, 6) ? "got lock" : "was already locked", "\n"; die "died"'
got lock
died at -e line 1.
$ perl -le 'open my $fh, ">", "f" or die $!; print flock($fh, 6) ? "got lock" : "was already locked", "\n"; die "died"'
got lock
died at -e line 1.

To get a segfault, we will need access to C, I am using Inline to get it:

$ cat segfault.pl
#!/usr/bin/perl

use strict;
use warnings;

use Inline "C";

open my $fh, ">", "f" or die $!;

print flock($fh, 6) ? "got lock" : "was already locked", "\n";

crash();

__DATA__
__C__

void crash() {
int* ptr = NULL;
*ptr = 5;
}
$ perl segfault.pl
got lock
Segmentation fault
$ perl segfault.pl
got lock
Segmentation fault

And finally, here is what happens when a program is sent SIGKILL:

$ cat fork.pl
#!/usr/bin/perl

use strict;
use warnings;

$SIG{CHLD} = "IGNORE"; #auto-reap children

die "could not fork: $!" unless defined(my $pid = fork);
unless ($pid) {
#child
open my $fh, ">", "f" or die $!;
print flock($fh, 6) ? "got lock" : "was already locked", "\n";
sleep(100);
exit;
}

kill 9, $pid;

die "could not fork: $!" unless defined($pid = fork);
unless ($pid) {
#child
open my $fh, ">", "f" or die $!;
print flock($fh, 6) ? "got lock" : "was already locked", "\n";
exit;
}
$ perl fork.pl
got lock
got lock

From these experiments, we can see that the lock is released in Linux for each of the cases you were concerned with.

Also, is there one perl instance running for each script, so that it's clear which script crashed/stopped without releasing the lock?

Yes, Perl 5 has one perl process per script. Even if you fork, the child gets its own perl process. Threading does not provide a separate perl process.

Note: if a parent process gets a lock and does not give it up before locking, then the child will have the same lock even if the parent exits.

flock(1) is failing to release lock

The problem is that you're running TrueCrypt in the background, keeping the fd open. You should close the fd to prevent background processes from hanging on to the lock.

In lieu of your actual code, here's a test case:

foo() {
(
flock -n 9 && echo "ok" || { echo failed; exit 1; }
sleep 10 &
) 9> lock
}
foo; foo

# Output:
# ok
# failed

sleep is forked with fd 9 open, causing the lock to be kept. Let's close fd 9 when backgrounding the process:

foo() {
(
flock -n 9 && echo "ok" || { echo failed; exit 1; }
sleep 10 9>&- &
# ^-------- Right here
) 9> lock
}
foo; foo

# Output:
# ok
# ok

What happens to PHP file lock if the script times out or is terminated while the lock was on?

I just made a couple of tests on the following shared server:

PHP Version 5.4.34
Linux 3.12.35.1418868052 #1 SMP x86_64

And my conclusion is that file locks are released automatically once the script finishes running, even in case of a fatal error, a timeout or out-of-memory fault that terminates the script, or if I comment out flock($file, LOCK_UN); function.

C++ Ubuntu not releasing lock on lock file when terminated

I solved this issue, since the system and fork commands seem to pass on the flock, by saving the command to execute in a vector. Unlock the lock file right before looping the Commands vector and execute each command. This leaves the main with a very tiny gap of running while not locked, but for now it seems to work great. This also supports ungraceful terminations.

[...]
if (LockFile(ExecuteFileName, Extra) == -1) {
cout << "Already running!" << endl; //MAIN IS ALREADY RUNNING
//RETURNS ME Resource temporarily unavailable when processor is running from an earlier run
exit(EXIT_SUCCESS);
}
vector<string> Commands;
if (StartProcessor) { //PSEUDO
int LockFileProcessor = LockFile("Processor");
if (LockFileProcessor != -1) {
string Command = "nohup setsid ./Processor"; //NOHUP CREATES ZOMBIE
Command += IntToString(result->getInt("Klantnummer"));
Command += " > /dev/null 2>&1 &"; //DISCARD OUTPUT
Commands.push_back(Command);
}
}
//UNLOCK MAIN
if (UnlockFile(LockFileMain)) {
for(auto const& Command: Commands) {
system(Command.c_str());
}
}

What is the use of 'flock' in PHP

One of the problem when working with the file system in PHP is that after you start working on a file with fopen it is still possible for one or more scripts to update the same file,

That can cause several problems, just think if the same file is updated at the same time.

The flock() function in PHP locks the file, once opened, to avoid this problem. It also returns a boolean value depending on if the lock has been successfully or not.

flock() also uses different flags to set how the function has to work,
they are:

  • LOCK_SH to acquire a shared lock (reader).
  • LOCK_EX to acquire an exclusive lock (writer).
  • LOCK_UN to release a lock (shared or exclusive).

Here is an example, hope it helps understand:

$fp = fopen("/tmp/lock.txt", "r+");

if (flock($fp, LOCK_EX)) { // acquire an exclusive lock
// update the file
} else {
echo "Couldn't get the lock!";
}

fclose($fp);

more info in this post

flock(): removing locked file without race condition?

Sorry if I reply to a dead question:

After locking the file, open another copy of it, fstat both copies and check the inode number, like this:

lockfile = "/tmp/some_name.lock";

while(1) {
fd = open(lockfile, O_CREAT);
flock(fd, LOCK_EX);

fstat(fd, &st0);
stat(lockfile, &st1);
if(st0.st_ino == st1.st_ino) break;

close(fd);
}

do_something();

unlink(lockfile);
flock(fd, LOCK_UN);

This prevents the race condition, because if a program holds a lock on a file that is still on the file system, every other program that has a leftover file will have a wrong inode number.

I actually proved it in the state-machine model, using the following properties:

If P_i has a descriptor locked on the filesystem then no other process is in the critical section.

If P_i is after the stat with the right inode or in the critical section it has the descriptor locked on the filesystem.

Flock in Perl doesnt work

There's another problem if your flock implementation is based on lockf(3) or fcntl(2), which it probably is. Namely, LOCK_EX should be used with "write intent", on a file opened for output.

For lockf(3), perldoc -f flock says

Note that the emulation built with lockf(3) doesn't provide shared locks, and it requires that FILEHANDLE be open with write intent.

and for fcntl(2):

Note that the fcntl(2) emulation of flock(3) requires that FILEHANDLE be open with read intent to use LOCK_SH and requires that it be open with write intent to use LOCK_EX.

A workaround for input files or for more complicated synchronized operations is for all processes to sync on a trivial lock file, like:

open my $lock, '>>', "$filename.lock";
flock $lock, LOCK_EX;

# can't get here until our process has the lock ...
open(my $fh, '<', $filename) or die $!; #file open
... read file, manipulate ...
close $fh;
open my $fh2, '>', $filename;
... rewrite file ...
close $fh2;

# done with this operation, can release the lock and let another
# process access the file
close $lock;

PHP - Counter issue using flock function

If two threads will execute your code almost simultaneusly with the small delay, the 1st thread will open file for write and erase it contents before locking.

The 2nd thread will read the empty file contents, wait for lock release, and then overwrite the correct data.

The solution is to open file not in "w", but in "a" or "c" mode and then use fwrite, fseek and ftruncate.



Related Topics



Leave a reply



Submit