Is a Java Filelock a Posix Advisory (Fcntl) Lock

Is a Java FileLock a POSIX advisory (fcntl) lock

Some Unix operating systems, including Linux, provide BSD-style (flock) locks, so it might be thought that Java FileLock could be implemented using BSD-style locks rather than POSIX locks. But that is not possible, because BSD-style locks are whole-file locks rather than record locks, and FileLock is a record lock: each lock is for a range of bytes in a file. Thus there is no real choice on a Unix system, and assuming that the implementation of FileLock uses POSIX fcntl locks is a safe assumption on a Unix operating system.

The resulting FileLock locks might or might not interact with BSD-style locks. BSD-style locks can be implemented using POSIX locks (this was the case for Linux before version 2.0), or the operating system might have the two styles of locking interact (this is the case for FreeBSD). But in general that can not be guaranteed, and BSD-style locks and Java locks might be effectively invisible to each other (this is the case for any version of Linux you are likely to encounter).

How safe is it to use Java FileLock?

Java FileLock uses advisory (not mandatory) locks on many platforms. That means it may only provide locking against other applications that also use FileLock (or the equivalent in other languages).

Neither Linux or Windows implement mandatory locking across the board. For instance:

  • For Linux and similar, file locking is advisory only.

  • For Windows, according to Wikipedia:

    "For applications that use the file read/write APIs in Windows, byte-range locks are enforced .... by the file systems that execute
    within Windows. For applications that use the file mapping APIs in
    Windows, byte-range locks are not enforced ..."

    In other words, locking on Windows can be either mandatory or advisory, depending on which API an Windows application uses to access files.


How safe is it to use Java FileLock?

If you are actually asking if it is safe to assume that FileLock provides mandatory file locking with respect to all other applications (Java & non-Java) irrespective of how they are written, the answer is No. It is NOT safe to make that assumption.


Is there a solution for locking a file appropriately in Java 6?

Only if all of the applications (Java & other) cooperate; e.g. by using FileLock or the equivalent.

If you can't make that assumption, there is no solution using portable Java. Indeed, on most (if not all) common OS platforms, there is no solution at all, AFAIK ... because the platform itself doesn't support mandatory file locking independent of the application.

Are POSIX file locks reentrant?

Advisory locks through fcntl are on a per process base and just accumulate locked intervals on the file for the given process. That is, it is up to the application to keep track of the intervals and any unlock call for an interval will unlock it, regardless on how many lock calls had been made for that interval.

Even worse, the closing of any file descriptor for the file cancels all locks on the file:

As well as being removed by an explicit F_UNLCK, record locks are
automatically released when the process terminates or if it closes any
file descriptor referring to a file on which locks are held. This is bad: it means that a process can lose the locks on a
file like
/etc/passwd or /etc/mtab when for some reason a library function decides to open, read and close it.

fcntl F_GETLK always returns F_UNLCK

You're misunderstanding the F_GETLK query. It returns F_UNLCK when nothing blocks the calling process from placing a lock of the given type at the given position.

Since the calling process is the one that created these existing locks, it can also create this new lock.


The Mac OS X manuals say

 F_GETLK 

Get the first lock that blocks the lock description pointed to by the third argument, arg,
taken as a pointer to a struct flock (see above). The information retrieved overwrites the
information passed to fcntl in the flock structure. If no lock is found that would prevent
this lock from being created, the structure is left unchanged by this function call except
for the lock type which is set to F_UNLCK
.

Linux IPC: Locking, but not file based locking

flock the directory itself — then you never need worry about where to put the lock file:

import errno
import fcntl
import os
import sys

# This will work on Linux

dirfd = os.open(THE_DIRECTORY, os.O_RDONLY) # FIXME: FD_CLOEXEC
try:
fcntl.flock(dirfd, fcntl.LOCK_EX|fcntl.LOCK_NB)
except IOError as ex:
if ex.errno != errno.EAGAIN:
raise
print "Somebody else is working here; quitting." # FIXME: logging
sys.exit(1)

do_the_work()
os.close(dirfd)

Fcntl in go doesn't work

If I create the file with touch lockfiletest.lock, that is with no file contents, your program fails with your error: error locking file2: bad file descriptor.

$ rm -f lockfiletest.lock
$ touch lockfiletest.lock
$ go run lockfiletest.go
2017/10/27 21:17:27 error locking file2: bad file descriptor

I changed the file open to closely match TestFcntlFlock.

$ uname -s
Linux
~/go/src/syscall$ $ go test -v -run=TestFcntlFlock syscall_unix_test.go
=== RUN TestFcntlFlock
--- PASS: TestFcntlFlock (0.01s)
PASS
ok syscall 0.008s
~/go/src/syscall$

For example,

package main

import (
"io"
"log"
"os"
"syscall"
"time"
)

func main() {
time.Sleep(time.Second)

name := "lockfiletest.lock"
file, err := os.OpenFile(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0666)
if err != nil {
log.Printf("error opening file: %s", err)
return
}
defer file.Close()

flockT := syscall.Flock_t{
Type: syscall.F_WRLCK,
Whence: io.SeekStart,
Start: 0,
Len: 0,
}
err = syscall.FcntlFlock(file.Fd(), syscall.F_SETLK, &flockT)
if err != nil {
log.Printf("error locking file: %s", err)
return
}

log.Println("lock2 accessed")

time.Sleep(time.Second * 5)

log.Println("func2 finished")

time.Sleep(time.Second * 15)
}

Output:

$ rm -f lockfiletest.lock
$ touch lockfiletest.lock
$ go run lockfiletest.go
2017/10/27 21:21:56 lock2 accessed
2017/10/27 21:22:01 func2 finished
$ rm -f lockfiletest.lock
$ go run lockfiletest.go
2017/10/27 21:22:25 lock2 accessed
2017/10/27 21:22:30 func2 finished
$ go run lockfiletest.go
2017/10/27 21:25:40 lock2 accessed
2017/10/27 21:25:45 func2 finished
$


Related Topics



Leave a reply



Submit