Single process instance - achievable in Go?

Hi all,
Very new to Go, I’m currently looking for a way to enforce that my program runs as a single instance, i.e. that the user cannot start a new instance of my program if one is already running. Think about your browser, most large apps, and any app on mobile OSes.
I was initially looking for an API relying on OS locks in the same way as offered by Qt system semaphores https://doc.qt.io/qt-5/qsystemsemaphore.html but did not really find anything solid.
I wonder whether there is another preferred means to achieve this in a crossplatform manner in Go without reinventing the wheel. Also, I do not want to rely on the filesystem as a means of interprocess-communication.
Many thanks for your opinions and guidance!
D

Not sure about preferred ways but you can try to use this package - https://github.com/marcsauter/single

Thanks a lot Grey for your reply. I gave it a try and it seems that it behaves just like I need.

So I ditched go-ipc https://github.com/nxgtw/go-ipc that seemed to be a comprehensive IPC library (includes system semaphores, shared mem, etc.) but was unfortunately useless for my specific use case due to a subtle behavior in unix/linux in case of crashes. Which is exactly my use case…

Many many thanks
D

A very trivial (and funny) idea that I had is to run somewhere in your application in a goroutine a dummy server doing nothing like in following example. Didn’t try too much but I guess should work on any OS because you can’t run a server on the same port twice.
:sunglasses:

package main

import "net/http"

func main() {
   if http.ListenAndServe(":65535", nil) != nil {
      println("Already run!")
      return
   }
}

@geosoft1 I thought of that approach as well but unfortunately it is not doable in my situation as we cannot afford opening a port. However, it behaves exactly as it should, whereas OS IPC primitives such as system semaphores and shared memory are not usable for that purpose on unix/linux systems because these systems do not cleanup/revert resources used by a process in case of crash. Note: these primitives behave as expected on Windows.

You could just do what a lot of Linux programs already does (and probably other operating systems as well). Just write your process id (pid) to a specific file when you start, and you can read that file and see if the process is still running when you start. That way you will know if there is already a instance running. Remember to read the file before you write your own pid :slight_smile:

Thanks Kris. Your solution has the merit of simplicity but isn’t it prone to race conditions/false positives? I have the feeling that any solution based on the file system is provably flawed, even on well designed file systems :frowning:

flock may help you.

package main

import (
	"fmt"
	"github.com/juju/fslock"
	"time"
)

func main() {
	lock := fslock.New("../pid.lock")
	err := lock.TryLock()
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	defer lock.Unlock()
}
1 Like

Still looks FS-based but way higher quality than https://github.com/marcsauter/single , thanks!