Optimal Lock File Method

Is there a way to share a lock (e.g. a lock file) between R processes?

While I couldn't find an R package, there is the Linux command lockfile that can be used:

write("Attempting to get lock", stderr())
system("lockfile /tmp/my_simple_lock")

# Do stuff

write("Releasing lock", stderr())
system("rm -f /tmp/my_simple_lock")

When is a special lock file or opening file in 'c' mode necessary with PHP flock?

I realized the answer to this question while typing the question, so
I'll post my answer as well. Obtaining an advisory lock on a file after
truncating it could be a problem when another reader script is trying to read
the file. The reader script would encounter a truncated file (an empty file)
if it happens to read the file between the time at which the writer
script opened the file in 'w' mode and the time at which it acquired
a lock on the file.

Here are two scripts that demonstrate the issue. The first script writes
its PID into a file called foo.txt. The second script attempts to read
the PID from this file.

write.php:

<?php
$f = fopen('foo.txt', 'w');

sleep(5); // Artificial delay between open and lock

flock($f, LOCK_EX);
fwrite($f, getmypid() . "\n");
flock($f, LOCK_UN);
fclose($f);
?>

read.php:

<?php
$f = fopen('foo.txt', 'r');
flock($f, LOCK_EX);

$size = filesize('foo.txt');
echo ($size === 0 ? "File is empty\n" : fread($f, $size));

flock($f, LOCK_UN);
fclose($f);
?>

The following shell session shows that read.php finds an empty file
when it tries to read the file after write.php opened the file and
before write.php acquired a lock on the file.

$ php write.php < /dev/null &
[1] 17511
$ for i in {1..10}; do php read.php; sleep 1; done
File is empty
File is empty
File is empty
File is empty
File is empty
[1]+ Done php write.php < /dev/null
17511
17511
17511
17511
17511

This issue occurs because we acquire a lock on the file after it has been truncated. That's a little too late. We want to first acquire a lock and then perform truncation or any other modification on it. There are two ways to do this.

Use a special lock file

write2.php:

<?php
$lock = fopen('foo.lock', 'w');

sleep(5); // Artificial delay between open and lock

flock($lock, LOCK_EX);
$f = fopen('foo.txt', 'w');
fwrite($f, getmypid() . "\n");
fclose($f);
flock($lock, LOCK_UN);
?>

The following shell session shows that read.php never encountered a
truncated file.

$ php write2.php < /dev/null &
[1] 17533
$ for i in {1..10}; do php read.php; sleep 1; done
17511
17511
17511
17511
17511
[1]+ Done php write2.php < /dev/null
17533
17533
17533
17533
17533

Open file in 'c' mode and then lock it

write3.php:

<?php
$f = fopen('foo.txt', 'c');

sleep(5); // Artificial delay between open and lock

flock($f, LOCK_EX);
ftruncate($f, 0);
fwrite($f, getmypid() . "\n");
flock($f, LOCK_UN);

fclose($f);
?>

This script takes advantage of the fact that opening the file in 'c' mode does not truncate the file automatially, so now we can truncate the file with ftruncate after we have acquired a lock on it and before writing to it. As a result read.php never encounters a truncated file.

$ php write3.php < /dev/null &
[1] 17558
$ for i in {1..10}; do php read.php; sleep 1; done
17533
17533
17533
17533
17533
[1]+ Done php write3.php < /dev/null
17558
17558
17558
17558
17558

Notepad beats them all?

Notepad reads files by first mapping them into memory, rather than using the "usual" file reading mechanisms presumably used by the other editors you tried. This method allows reading of files even if they have an exclusive range-based locks.

You can achieve the same in C# with something along the lines of:

using (var f = new FileStream(processIdPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var m = MemoryMappedFile.CreateFromFile(f, null, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.None, true))
using (var s = m.CreateViewStream(0, 0, MemoryMappedFileAccess.Read))
using (var r = new StreamReader(s))
{
var l = r.ReadToEnd();
Console.WriteLine(l);
}

How can I lock a file using java (if possible)

FileChannel.lock is probably what you want.

try (
FileInputStream in = new FileInputStream(file);
java.nio.channels.FileLock lock = in.getChannel().lock();
Reader reader = new InputStreamReader(in, charset)
) {
...
}

(Disclaimer: Code not compiled and certainly not tested.)

Note the section entitled "platform dependencies" in the API doc for FileLock.

Best practice to lock data objects

Never expose LockObject, hide implementation details:

private object m_LockObject = new object();

Next issue: why timeConsumingMethod receives DataObject instead of being
implemented within DataObject:

public class DataObject {
// locking object is a private implementation detail
private object m_LockObject = new object();

// TheMethod works with "this" DataObject instance, that's why
// the method belongs to DataObject
// let's return Task (e.g. to await it)
// Think on method's name;
public Task TheMethodAsync() {
// Task.Factory.StartNew is evil
return Task.Run(() => {
lock (m_LockObject) {
// ...
}
});
}

...
}

Then just call the method

 public void timeConsumingMethod(DataObject data) {
// When designing public methods do not forget about validation
if (null == data)
throw new ArgumentNullException("data");

// Think on awaiting the Task returned:
// "i run them in a Taks ... to avoid blocking the UI"
// await data.TheMethodAsync();
data.TheMethodAsync();

...
}

What is the best way to ensure only one instance of a Bash script is running?

If the script is the same across all users, you can use a lockfile approach. If you acquire the lock, proceed else show a message and exit.

As an example:

[Terminal #1] $ lockfile -r 0 /tmp/the.lock
[Terminal #1] $

[Terminal #2] $ lockfile -r 0 /tmp/the.lock
[Terminal #2] lockfile: Sorry, giving up on "/tmp/the.lock"

[Terminal #1] $ rm -f /tmp/the.lock
[Terminal #1] $

[Terminal #2] $ lockfile -r 0 /tmp/the.lock
[Terminal #2] $

After /tmp/the.lock has been acquired your script will be the only one with access to execution. When you are done, just remove the lock. In script form this might look like:

#!/bin/bash

lockfile -r 0 /tmp/the.lock || exit 1

# Do stuff here

rm -f /tmp/the.lock


Related Topics



Leave a reply



Submit