Skip to content

Commit 39f7507

Browse files
committed
propagate listener exceptions
1 parent 5eaf8ff commit 39f7507

File tree

1 file changed

+19
-9
lines changed

1 file changed

+19
-9
lines changed

src/main/kotlin/com/github/iamcalledrob/singleinstance/SingleInstance.kt

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,45 @@ class SingleInstance(
2424
//
2525
// This is intentionally unhandled because this situation could happen even if dial succeeds -- the dial
2626
// could succeed just *before* the other instance terminates.
27-
tryListen()?.let { listener ->
27+
try {
28+
// Throws AlreadyLockedException if lock can't be acquired
29+
val listener = tryListen()
30+
2831
// Listening, accept incoming dials indefinitely
2932
CoroutineScope(Dispatchers.IO).launch {
3033
accept(listener, onArgsReceived)
3134
}
32-
} ?: run {
35+
36+
} catch (e: AlreadyLockedException) {
3337
// Another instance is listening, dial and send args
3438
dial(args)
3539
onExit()
3640
}
3741
}
3842

39-
private fun tryListen(): ServerSocketChannel? {
43+
private fun tryListen(): ServerSocketChannel {
4044
// Acquire an exclusive lock, which will be auto-released by the OS on process death.
4145
// Lock protects the domain socket
4246
val lockPath = "$socketPath.lock"
43-
lock = FileChannel.open(Path(lockPath), StandardOpenOption.WRITE, StandardOpenOption.CREATE)
44-
.tryLock() ?: return null
47+
val fileChannel = FileChannel.open(Path(lockPath), StandardOpenOption.WRITE, StandardOpenOption.CREATE)
48+
lock = fileChannel.tryLock() ?: throw AlreadyLockedException(lockPath)
4549

4650
// Lock acquired, no other instances are running. Safe to clean up existing socket to allow for bind.
4751
File(socketPath).delete()
4852

49-
return ServerSocketChannel.open(StandardProtocolFamily.UNIX).apply {
50-
bind(UnixDomainSocketAddress.of(socketPath))
51-
}
53+
val channel = ServerSocketChannel.open(StandardProtocolFamily.UNIX)
54+
val address = UnixDomainSocketAddress.of(socketPath)
55+
channel.bind(address)
56+
57+
return channel
5258
}
5359
// Keep a reference to the lock, which prevents the channel from being auto-closed (which would release the lock)
5460
private var lock: FileLock? = null
5561

5662
private fun dial(args: Array<String>) {
57-
writeArgs(SocketChannel.open(UnixDomainSocketAddress.of(socketPath)), args)
63+
val address = UnixDomainSocketAddress.of(socketPath)
64+
val channel = SocketChannel.open(address)
65+
writeArgs(channel, args)
5866
}
5967

6068
private fun accept(listener: ServerSocketChannel, onArgsReceived: suspend (Array<String>) -> Unit) {
@@ -108,6 +116,8 @@ class SingleInstance(
108116
}
109117
}
110118

119+
internal class AlreadyLockedException(path: String) : Exception("$path already locked")
120+
111121
/** Provides a socket path in the temp dir for the provided identifier.
112122
* Identifier must not contain characters which are unsuitable for a file name.
113123
*

0 commit comments

Comments
 (0)