Is there a good way to ensure my app is running on...
# compose-desktop
s
Is there a good way to ensure my app is running only in a single instance on Windows? I use a lock file in users TEMP so far, because that's the mechanic I know of - and it's easy to implement. For some reasons (I assume some virus scanners?) sometimes this returns with a FileNotFoundException or other IO exceptions according to my Sentry crash logs. So I wonder how you guys handle this problem.
Copy code
private fun ensureSingleInstance(): Boolean {

    val lockFile = File(System.getenv("TEMP") + "/ashampoo-photo-organizer.lock")
    val channel = RandomAccessFile(lockFile, "rw").channel

    fileLock = channel.tryLock()

    return fileLock != null
}
1
m
I like the file locking idea, I was successfully using a more complex idea for a few months, but I will try switching to file locking.
s
What complex idea did you try and why do you consider switching?
m
Writing to a file every second as a kind of "heartbeat". A new instance can check that the timestamp in the file is not older than 1 second, so there must be another instance alive writing to it. It works great, but using a file lock seems simpler.
s
Okay, that’s still prone to I/O errors.
I am thinking about opening a specific port maybe. I feel there must be something that’s reliable.
m
that’s still prone to I/O errors.
I didn't see any issues. Maybe the TEMP directory is the culprit? I keep my file APPDATA.
> there must be something that’s reliable. If your program is unable to create/write to a file then that seems like an issue in itself. Since it won't be able to save anything, or remember settings, or do any other basic things.
e
https://learn.microsoft.com/en-us/windows/win32/sync/interprocess-synchronization create a mutex with a name of your choosing (a guid you create will avoid conflicts)
👍 1
m
It looks good, but it's Windows specific, so not a solution for a Windows + Mac + Linux desktop app, unless you have other solutions for the other systems anyway.
e
you need a different solution on different platforms anyway, even file locking doesn't work the same
👍 1
m
Writing to a file every second seems to work fine on any OS. What can go wrong with file locking? I only tried file locking on Linux so I don't know yet how well it works on Mac or Windows.
e
locking depends on filesystem in Linux, has different (bsd-like) semantics on macos, and both macos and windows have significantly slower file IO subsystems than Linux so you're adding noticeable load by writing every second…
👍 1
m
Seems like the issue is complex enough that it would warrant creating a library that would handle it in a system, and even filesystem specific way.
s
It sounds like that. Maybe there is already a lib I did not find so far. The solution in my original post is only used on Windows. Linux doesn’t have this „this file is used by another process“ thing as far as I know. On macOS apps are single instance by default I believe.
If your program is unable to create/write to a file then that seems like an issue in itself. Since it won't be able to save anything, or remember settings, or do any other basic things.
I don’t know what’s up with the users systems, but a few have strange I/O errors sometimes. Rare, but happens. Normally that’s no a big deal as a missing image file will be skipped and read on the next attempt/sync again. Just not starting at all has bigger significance.
m
Single instance is handled for you on macOS when packaged. On other platforms it requires IPC. You have to be able to communicate things to the running instance, not just prevent a second startup.
👍 1
s
Agreed. If I could send the running instance a signal to bring itself to front because closing the second instance this would be the ideal behaviour.
e
on desktop linux you'd use dbus
💡 1
s
The only thing I found for java is part of a bigger Apache framework. Not tested yet. https://curator.apache.org/apidocs/org/apache/curator/framework/recipes/locks/InterProcessMutex.html I don't have the ability to write my own JNI libs. 😅
But it's good to know what the right way(tm) to do this is. So my initial question is answered. 😄