Enforce just one instance?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
togFox
Party member
Posts: 770
Joined: Sat Jan 30, 2021 9:46 am
Location: Brisbane, Oztralia

Enforce just one instance?

Post 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.
Current project:
https://togfox.itch.io/backyard-gridiron-manager
American football manager/sim game - build and manage a roster and win season after season
User avatar
darkfrei
Party member
Posts: 1169
Joined: Sat Feb 08, 2020 11:09 pm

Re: Enforce just one instance?

Post by darkfrei »

Maybe create, open, check for access to some file.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
zorg
Party member
Posts: 3436
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Enforce just one instance?

Post 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)
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
GVovkiv
Party member
Posts: 668
Joined: Fri Jan 15, 2021 7:29 am

Re: Enforce just one instance?

Post 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
User avatar
darkfrei
Party member
Posts: 1169
Joined: Sat Feb 08, 2020 11:09 pm

Re: Enforce just one instance?

Post by darkfrei »

Write random (time) number to the file, if it changes then there is another process.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
monolifed
Party member
Posts: 188
Joined: Sat Feb 06, 2016 9:42 pm

Re: Enforce just one instance?

Post 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
Last edited by monolifed on Sat Mar 27, 2021 8:01 pm, edited 1 time in total.
User avatar
ivan
Party member
Posts: 1911
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Enforce just one instance?

Post 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!
RNavega
Party member
Posts: 239
Joined: Sun Aug 16, 2020 1:28 pm

Re: Enforce just one instance?

Post 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.
User avatar
zorg
Party member
Posts: 3436
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Enforce just one instance?

Post 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.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
RNavega
Party member
Posts: 239
Joined: Sun Aug 16, 2020 1:28 pm

Re: Enforce just one instance?

Post 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.
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 55 guests