2

I have a go program that modifies my configuration file. I am trying to create a filelock from within the main() function, but it is throwing a panic: runtime error: invalid memory address or nil pointer dereference error. Without the lock, the program is working fine as expected. The piece of code that throws exception is

lockProgram, err := os.Create("/var/.daemon.lock")
defer lockProgram.Close()
CheckForError(err)
GetLock(lockProgram, syscall.LOCK_EX)
defer UngetLock(lockProgram)

//This is in a separate package

func CheckForError(e error) {
    if e != nil {
        Error.Println(e)
        panic(e)
    }
}

func GetLock(file *os.File, locktype int )  {
    fmt.Println("Acquiring lock on ", file.Name())
    syscall.Flock(int(file.Fd()), locktype)
    fmt.Println("Acquired filelock on ", file.Name())
}
func UngetLock(file *os.File)  {
    syscall.Flock(int(file.Fd()), syscall.LOCK_UN);
}

This same flock is working when I call it on my configuration file, but from a different package, not main package, but throws the same error when I try to put lock from within the main package. Please help me in finding out what am I doing wrong here.

nohup
  • 3,105
  • 3
  • 27
  • 52
  • 1
    You're calling defer before checking for an error. If there is an error the file will be nil. – JimB Jan 10 '16 at 20:33
  • @JimB, please correct me if I am wrong, but defer is stacked, and will be executed only when the function is about to get finished, right? Can it create a problem? – nohup Jan 10 '16 at 20:35
  • 1
    That doesn't matter. If your function ends and `lockProgram` is nil you will get a panic. You need to check the error and return early if you want to defer the close – JimB Jan 10 '16 at 20:39
  • Thank you @JimB, this helps. – nohup Jan 10 '16 at 20:42
  • When your program panics you get the stack trace. Please tell where exactly panic occurs. Is it in your CheckForError function? – kopiczko Jan 10 '16 at 20:54

1 Answers1

1

When an error occurs while creating the lock, lockProgram will be nil. This will cause the subsequent (deferred) call to lockProgram.Close() to fail.

Note that when you're panicking (like in your CheckForError function), deferred method calls will still be executed. This is explained in detail in this blog article (emphasis mine):

Panic is a built-in function that stops the ordinary flow of control and begins panicking. When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller. To the caller, F then behaves like a call to panic. The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes.

Solution: Check for errors first, and then defer the Close() call:

lockProgram, err := os.Create("/var/.daemon.lock")
CheckForError(err)
defer lockProgram.Close()
helmbert
  • 35,797
  • 13
  • 82
  • 95