//
// Copyright (c) 2024, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 16 Jun 16 Brian Frank Creation
//
using concurrent
using [java] fanx.interop
using [java] java.io::RandomAccessFile
**
** LockFile is used to acquire an exclusive lock to prevent
** two different processes from using same files
**
const class LockFile
{
** Construct with given file
new make(File file) { this.file = file }
** Backing file we use to lock/acquire
const File file
private const AtomicRef fpRef := AtomicRef()
** Acquire the lock or raise CannotAcquireLockFileErr
This lock()
{
// use java.nio.LockFile
file.parent.create
jfile := Interop.toJava(file)
fp := RandomAccessFile(jfile, "rw")
lock := null
try
lock = fp.getChannel.tryLock
catch (Err e) // OverlappingFileLockException when doing locks within the same JVM
{}
if (lock == null) throw CannotAcquireLockFileErr(file.osPath)
// save away the fp
fpRef.val = Unsafe(fp)
// write info about who is creating this lock file
fp.writeBytes(
"""locked=${DateTime.now}
homeDir=${Env.cur.homeDir.osPath}
version=${typeof.pod.version}""")
fp.getFD.sync
return this
}
** Release the lock if we are holding one
This unlock()
{
fp := (fpRef.val as Unsafe)?.val as RandomAccessFile
if (fp != null) fp.close
Interop.toJava(file).delete
return this
}
** Command line test program
@NoDoc static Void main(Str[] args)
{
file := `test.lock`.toFile.normalize
echo
echo("Acquiring: $file.osPath ...")
LockFile(file).lock
echo("Acquired!")
echo
echo("Run this program in another console and verify CannotAcquireLockFileErr")
echo("Waiting, use Ctrl+C to end ...")
Actor.sleep(1day)
}
}
**************************************************************************
** CannotAcquireLockFileErr
**************************************************************************
** When another process has var directory locked
@NoDoc
const class CannotAcquireLockFileErr : Err
{
new make(Str msg, Err? cause := null) : super(msg, cause) {}
}