Skip to content

Commit 95d42a6

Browse files
Add LockError type for when lock cannot be acquired
1 parent c1f2b05 commit 95d42a6

File tree

3 files changed

+38
-6
lines changed

3 files changed

+38
-6
lines changed

library/src/libdaemonjvm/LockFiles.scala

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,38 @@ import java.nio.file.attribute.PosixFilePermission
88
import java.nio.file.StandardOpenOption
99
import scala.collection.JavaConverters._
1010
import scala.util.Properties
11+
import libdaemonjvm.server.LockError
12+
import java.nio.channels.OverlappingFileLockException
1113

1214
final case class LockFiles(
1315
lockFile: Path,
1416
pidFile: Path,
1517
socketPaths: SocketPaths
1618
) {
17-
def withLock[T](t: => T): T = {
19+
def withLock[T](t: => Either[LockError, T]): Either[LockError, T] = {
1820
if (!Files.exists(lockFile)) {
1921
Files.createDirectories(lockFile.normalize.getParent)
2022
Files.write(lockFile, Array.emptyByteArray)
2123
}
22-
var c: FileChannel = null
23-
var l: FileLock = null
24+
var c: FileChannel = null
25+
var l: Either[OverlappingFileLockException, FileLock] = null
2426
try {
2527
c = FileChannel.open(lockFile, StandardOpenOption.WRITE)
26-
l = c.lock()
27-
t
28+
l =
29+
try Right(c.tryLock())
30+
catch {
31+
case ex: OverlappingFileLockException =>
32+
Left(ex)
33+
}
34+
l match {
35+
case Left(ex) => Left(new LockError.Locked(lockFile, ex))
36+
case Right(null) => Left(new LockError.Locked(lockFile))
37+
case Right(_) => t
38+
}
2839
}
2940
finally {
3041
if (l != null)
31-
try l.release()
42+
try l.foreach(_.release())
3243
catch {
3344
case _: ClosedChannelException =>
3445
case _: IOException =>

library/src/libdaemonjvm/server/LockError.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ object LockError {
2525
extends FatalError(s"Cannot delete $file", cause)
2626
final class ZombieFound(val pid: Int, val connectionError: Throwable)
2727
extends RecoverableError(s"Cannot connect to process $pid", connectionError)
28+
final class Locked(val file: Path, cause: Throwable = null)
29+
extends RecoverableError(s"$file already locked", cause)
2830
}

tests/test/src/libdaemonjvm/tests/LockTests.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,23 @@ class LockTests extends munit.FunSuite {
140140
}
141141
}
142142

143+
test("locked") {
144+
TestUtil.withTestDir { dir =>
145+
val files = TestUtil.lockFiles(dir)
146+
val e = files.withLock {
147+
TestUtil.tryAcquire(files) { maybeChannel =>
148+
maybeChannel match {
149+
case Left(e: LockError.Locked) =>
150+
case Left(otherError) =>
151+
throw new Exception("Unexpected error type (expected Locked)", otherError)
152+
case Right(channel) =>
153+
sys.error("Opening new server channel should have failed")
154+
}
155+
}
156+
Right(())
157+
}
158+
expect(e.isRight)
159+
}
160+
}
161+
143162
}

0 commit comments

Comments
 (0)