Page 1 of 3

Enforce just one instance?

Posted: Sat Mar 20, 2021 3:03 am
by togFox
Googlefoo failed me.

Need to check if another instance of my app is already running then
- show message
- close this instance or switch to that instance.


Running the app twice is causing database locking issues. Any tips? Thanks.

Re: Enforce just one instance?

Posted: Sat Mar 20, 2021 6:36 am
by darkfrei
Maybe create, open, check for access to some file.

Re: Enforce just one instance?

Posted: Sun Mar 21, 2021 5:28 am
by zorg
A simple way would be to have your project create a file when opened, and delete it before it quits (there's a callback for the latter)
The project should check before creating the file if it exists already or not, if it does, it's already running, and it could just shut itself down.

Switching in the sense of closing the other process and having this one be the one running is a bit harder because of the first part, closing another process... not sure how that could be accomplished to be honest. (it's not impossible, but imo it's way more complicated than what it's worth thinking about)

Re: Enforce just one instance?

Posted: Sun Mar 21, 2021 9:34 am
by GVovkiv
zorg wrote: Sun Mar 21, 2021 5:28 am A simple way would be to have your project create a file when opened, and delete it before it quits (there's a callback for the latter)
The project should check before creating the file if it exists already or not, if it does, it's already running, and it could just shut itself down.

Switching in the sense of closing the other process and having this one be the one running is a bit harder because of the first part, closing another process... not sure how that could be accomplished to be honest. (it's not impossible, but imo it's way more complicated than what it's worth thinking about)
well, in Clickteam Fusion, there exist extension which allow to detect if game opened in other instance or not
And if it detect, it will callback "it's another extension"
which can be used to close game, for example:
if copyInstance == true then
love.event.quit()
end

also there exist function which allow to check if this instance original or copy, soo, i guess it's not that hard as it seems

Re: Enforce just one instance?

Posted: Sun Mar 21, 2021 10:16 am
by darkfrei
Write random (time) number to the file, if it changes then there is another process.

Re: Enforce just one instance?

Posted: Sun Mar 21, 2021 1:05 pm
by monolifed
You can also run a local server with a fixed port. Then the other instance can't use the same port.
(The code is just proof of concept)

Code: Select all

local socket = require"socket"
local s, err = socket.bind("0.0.0.0", "8329")
local role = err == "address already in use" and "EXTRA" or "PRIMARY"

local text = ""

if role == "PRIMARY" then
	s:settimeout(0)
	
	local servers, clients = {s}, {}
	local timer = 0
	local default_run = love.run
	love.run = function()
		runf = default_run()
		return function()
			ret = runf()
			
			local rlist = socket.select(servers, clients, 0)
			for _, client in ipairs(rlist) do
				if not clients[client] then
					table.insert(clients, client)
					clients[client] = #clients
					text = "Another instance opened"
					timer = 5
				else
					client:close()
					table.remove(clients, clients[client])
					clients[client] = nil
				end
			end
			
			return ret
		end
	end
	
	love.update = function(dt)
		if text == "" then return end
		if timer > 0 then timer = timer - dt end
		if timer <= 0 then text = "" end
	end

else -- if "extra"
	local client = socket.tcp()
	client:connect("0.0.0.0", "8329")
	client:send("PING")
	text = "Another instance is running\nPress any key to quit"

	love.keypressed = function()
		love.event.quit()
	end
end

love.draw = function()
	love.graphics.print(role, 50, 25)
	love.graphics.print(text, 50, 50)
end

Re: Enforce just one instance?

Posted: Sun Mar 21, 2021 1:20 pm
by ivan
A simple way would be to have your project create a file when opened, and delete it before it quits (there's a callback for the latter)
Yes, I use this method too, although you have to check if that file is locked or not. If the file is there but it's not locked then the previous instance has either crashed or was terminated.

PS. On further examination it appears that Love2d doesn't lock the file when opened in write mode (the io module doesn't support this either).
Any tips on how to lock temporary files would be greatly appreciated!

Re: Enforce just one instance?

Posted: Thu Mar 25, 2021 10:59 am
by RNavega
What the Qt libraries use for this is a 'shared memory object', something used with IPC ("interprocess communication").
It's a named block of memory that different processes can access and read/write to like a file that exists in system memory. It's got a unique name, that's how different processes can all use this same name to access it.

So when you start your LÖVE game, you first try to open the named memory block. If it fails to open then it doesn't exist yet (as this is the only living instance of your program in the OS session), so you create the named memory block yourself.
If it DOES open it, then you know that the named memory block already exists and there's probably another instance running. I say "probably" because that initial program instance might have crashed and didn't get to destroy the named memory block, so it lingers there (this happens on Linux/Mac systems; on Windows it's destroyed when the program that created it ends).

So provided your program is able to always destroy this shared memory block when it ends (be it from a crash or a successful run), this is a way to have a single app instance.

-----
Now to implement in Lua. This seems to be a (Win/Mac/Linux) cross-platform LuaJIT-FFI-based implementation of this:
https://github.com/luapower/mmap/blob/master/mmap.lua

...but it's got a lot of stuff that maybe you don't need. So for simpler reference there's this C++ library which should be easier to port:
https://github.com/itchio/shoom/tree/master/src

You would port that 'shoom' library, or at least the parts from it you want, by using the code from that Lua library above as reference for how to wrap the OS functions, the constants etc.

Re: Enforce just one instance?

Posted: Fri Mar 26, 2021 6:49 am
by zorg
ivan wrote: Sun Mar 21, 2021 1:20 pm
A simple way would be to have your project create a file when opened, and delete it before it quits (there's a callback for the latter)
Yes, I use this method too, although you have to check if that file is locked or not. If the file is there but it's not locked then the previous instance has either crashed or was terminated.

PS. On further examination it appears that Love2d doesn't lock the file when opened in write mode (the io module doesn't support this either).
Any tips on how to lock temporary files would be greatly appreciated!
As another post said, one could check the last modified date, and if the first/main instance hasn't updated that for long enough (on a separate thread perhaps, so visual hangs won't cause any issues), then it's safe to assume that that instance crashed/was terminated and didn't clean up after itself.

Re: Enforce just one instance?

Posted: Tue Mar 30, 2021 10:19 am
by RNavega
After some more research, instead of using shared memory objects like I suggested before, there's a safer solution based on this library:
https://github.com/oblique/named-lock/tree/master/src

It uses a named mutex on Windows (so your program can lock that mutex, which is a memory object, and if any other instances try to lock it as well, they'll fail and know that they're not the only instance), and on Linux and Mac it uses a file lock like it's being suggested in this thread, using the "flock" function.

Anyone trying to port this can still use that mmap Lua FFI library as reference for how to wrap the OS functions for use in your Lua code.
This would work for desktop platforms only though -- I don't know how it'd be done using FFI on Android -- and it also assumes that you need it to work on all OSes. The OP hasn't said if this is a requirement. If you only need if for a specific OS then you only need the functions for that OS alone obviously.

On Linux and Mac, the benefit of doing it with file locking instead of writing a timestamp in a file is that the OS becomes a gatekeeper into the file, so you don't have to keep polling it for changes which uses hard disk reads, something that I like to avoid if possible.