Note that when using the "green threads" version of Sun's Java VM, the
non-blocking feature of the "tryToLock" method does not work; that
method will block just like the "lock" method does.


package local;

import java.io.*;

public class LockableFile extends File {
    public LockableFile(String path) {
	super(path);
    }

    // valid parameters to lock()
    public final static int READ = 0,
                            WRITE = 1;

    /** Try to lock file, return false if not possible */
    public synchronized boolean tryToLock(int type) throws IOException {
	if (lock0(type, false)) {
	    thread = Thread.currentThread();
	    return true;
	}
	return false;
    }

    /** Lock file, waiting until possible */
    public synchronized void lock(int type) throws IOException {
	if (lock0(type, true))
	    thread = Thread.currentThread();
    }

    /** Unlock file */
    public synchronized void unlock() throws IOException {
	try {
	    unlock0();
	} finally {
	    thread = null;
	}
    }

    /** Return true if file is locked */
    public synchronized boolean isLocked() {
	return thread != null;
    }

    /** Return thread that locked file */
    public synchronized Thread getLockingThread() {
	return thread;
    }

    private native boolean lock0(int type, boolean block) throws IOException;
    private native void unlock0() throws IOException;

    private int fd = -1;
    private Thread thread;

    static {
	System.loadLibrary("LockableFile");
    }
}



#include "local_LockableFile.h"
#include <javaString.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

long
local_LockableFile_lock0(
    struct Hlocal_LockableFile *this_h,
    long mode,
    long block)
{
    Classlocal_LockableFile *this = unhand(this_h);
    struct flock lock;

    if (this->fd == -1 && !open_fd(this))
	return;		/* must have been an error */

    if (!setup_lock(&lock, mode))
	return;
    if (block) {
	if (fcntl(this->fd, F_SETLKW, &lock) == -1) {
	    SignalError(EE(),
		"java/io/IOException",
		strerror(errno));
	    return 0;
	}
	return 1;		/* true: successfully locked */
    } else {
	if (fcntl(this->fd, F_SETLK, &lock) == -1) {
	    /* don't throw exception if failed because already locked */
	    if (errno != EAGAIN && errno != EACCES)
		SignalError(EE(),
		    "java/io/IOException",
		    strerror(errno));
	    return 0;
	}
	return 1;		/* true: successfully locked */
    }
}

void
local_LockableFile_unlock0(
    struct Hlocal_LockableFile *this_h)
{
    Classlocal_LockableFile *this = unhand(this_h);
    struct flock lock;

    lock.l_whence = lock.l_start = lock.l_len = 0;
    lock.l_type = F_UNLCK;
    if (fcntl(this->fd, F_SETLKW, &lock) == -1)
	SignalError(EE(),
	    "java/io/IOException", strerror(errno));
}

static int
open_fd(Classlocal_LockableFile *this)
{
    char *path = allocCString(this->path);
    if ((this->fd = open(path, O_RDWR)) == -1) {
	switch (errno) {
	  case ENOENT:
	    SignalError(EE(),
		"java/io/FileNotFoundException",
		makeCString(this->path));
	    break;
	  default:
	    SignalError(EE(),
		"java/io/IOException",
		strerror(errno));
	}
    }
    free(path);		/* we're done with this */
    return (this->fd != -1);
}

static int
setup_lock(
    struct flock *lock,
    long mode)
{
    lock->l_whence = lock->l_start = lock->l_len = 0;
    switch (mode) {
      case local_LockableFile_READ:
	lock->l_type = F_RDLCK;
	break;
      case local_LockableFile_WRITE:
	lock->l_type = F_WRLCK;
	break;
      default:
	SignalError(EE(),
	    "java/lang/IllegalArgumentException", NULL);
	return 0;
    }
    return 1;
}
