0

I'm trying to prevent a program from opening another instance if it's already open, to do this I create a file with .lock extension and remove it when exiting the program. However everything except the remove works.

package main

import (
    "os"
    "os/signal"
    "fmt"
)

func main() {
    var test string
    exitsig := make(chan os.Signal, 1)
    signal.Notify(exitsig, os.Interrupt)
    var (
        lockstate bool = false
    )

    if _, err := os.Stat("ms.lock"); err == nil {
      return

    } else if os.IsNotExist(err) {
      var file, err = os.Create("ms.lock")
        if err != nil {
            return
        }
        file.Close()
        lockstate = true
    }

    go func() {
      <- exitsig
        fmt.Println("Error removing file")
        fmt.Scanf("%s", &test)
        if lockstate {
            var err = os.Remove("ms.lock")
            if err != nil {
                fmt.Println("Error removing file")
                fmt.Scanf("%s", &test)
            }
        }
      os.Exit(0)
    }()

}

I've tried exiting by ctrl+c, exiting by pressing the close button on the top right corner of the window but it never sends a signal, the os.Interrupt signal is never caught. What is the reason for this?

Also, I need the signal to be non-platform specific, so it should work on both windows and unix based systems.

Chase
  • 5,315
  • 2
  • 15
  • 41
  • Catching window-close events is tricky: see https://stackoverflow.com/questions/10792361/how-do-i-gracefully-exit-an-x11-event-loop and https://learn.microsoft.com/en-us/dotnet/api/system.windows.window.closing?view=netframework-4.8. Note that running from CLI means you don't get direct interaction with the window manager, though; on Unix systems you'll generally get SIGTERM or SIGHUP: see https://unix.stackexchange.com/questions/82952/is-it-harmful-to-close-a-terminal-window-without-properly-exiting-an-application – torek Oct 07 '19 at 08:00
  • It's also no longer commonly used this way, but: consider that the disposition of SIGINT might be SIG_IGN at the time your program starts up. If so, it's a bit rude to just start catching SIGINT signals. You can prefix your catching with `if !signal.Ignored(s)` where `s` is the `syscall.SIGINT`, `syscall.SIGTERM`, etc., number. – torek Oct 07 '19 at 08:02

1 Answers1

1

I think it's because your main function exists soon after launching the goroutine. If the main function ends, all running goroutines will die too.

Here's code that works for me:

package main

import (
    "fmt"
    "os"
    "os/signal"
    "sync"
)

func main() {

    exitsig := make(chan os.Signal, 1)
    signal.Notify(exitsig, os.Interrupt)
    var (
        lockstate bool = false
    )

    if _, err := os.Stat("ms.lock"); err == nil {
        return

    } else if os.IsNotExist(err) {
        var file, err = os.Create("ms.lock")
        if err != nil {
            return
        }
        file.Close()
        lockstate = true
    }

    go func() {
        <-exitsig
        if lockstate {
            var err = os.Remove("ms.lock")
            if err != nil {
                fmt.Println("Error removing file: ", err)
            }
        }
        os.Exit(0)
    }()

    wg := &sync.WaitGroup{}
    wg.Add(1)
    wg.Wait()

}

I added the waitgroup to wait in the main thread. Works fine on MacOS - creates ms.lock file and waits. Killing it with Cmd + C removes the file.

Should work anywhere as long as the signal fires.

masnun
  • 11,635
  • 4
  • 39
  • 50
  • Will it work when you close the window itself? (through the red cross) – Chase Oct 07 '19 at 06:17
  • Let me try that out. – masnun Oct 07 '19 at 06:19
  • No, it doesn't, probably because it sends a different signal. – masnun Oct 07 '19 at 06:21
  • You need to try and log the signal it gets when the window is closed. – masnun Oct 07 '19 at 06:28
  • I wrote a Python script to write the signal to a text file before it gets killed off. I can see the values if it was interrupted. But when the terminal closes, I get nothing, I assume it's because it doesn't get any signals at all. Gets killed off directly. – masnun Oct 07 '19 at 06:38
  • My suggestion would be to build a GUI that can handle the close event. Run the go program through that GUI and pass SIGTERM when the GUI closes. – masnun Oct 07 '19 at 06:50