marcio
marcio

Reputation: 10512

Go "panic: sync: unlock of unlocked mutex" without a known reason

I have a cli application in Go (still in development) and no changes were made in source code neither on dependencies but all of a sudden it started to panic panic: sync: unlock of unlocked mutex.

The only place I'm running concurrent code is to handle when program is requested to close:

func handleProcTermination() {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    go func() {
        <-c
        curses.Endwin()
        os.Exit(0)
    }()
    defer curses.Endwin()
}

Only thing I did was to rename my $GOPATH and work space folder. Can this operation cause such error?

Have some you experienced any related problem without having any explanation? Is there a rational check list that would help to find the cause of the problem?

Upvotes: 3

Views: 11014

Answers (4)

Sasi Varunan
Sasi Varunan

Reputation: 2864

For those who come here and didn't solve your problem. Check If the application is compiled in one version of Linux but running in another version. At least in my case it happened.

Upvotes: -1

Jean Spector
Jean Spector

Reputation: 1106

Make sure you don't copy the lock somewhere.

What can happen with seemingly bulletproof code in concurrent environments is that the struct including the code gets copied elsewhere, which results in the underlying lock being different.

Consider this code snippet:

type someStruct struct {
    lock sync.Mutex
}

func (s *someStruct) DoSomethingUnderLock() {
    s.lock.Lock()
    defer s.lock.Unlock() // This will panic
    time.Sleep(200 * time.Millisecond)
}

func main() {
    s1 := &someStruct{}

    go func() {
        time.Sleep(100 * time.Millisecond) // Wait until DoSomethingUnderLock takes the lock
        s2 := &someStruct{}
        *s1 = *s2
    }()

    s1.DoSomethingUnderLock()
}

*s1 = *s2 is the key here - it results in a different lock being used by the same receiver function and if the struct is replaced while the lock is taken, we'll get sync: unlock of unlocked mutex.

What makes it harder to debug is that someStruct might be nested in another struct (and another, and another), and if the outer struct gets replaced (as long as someStruct is not a reference there) in a similar manner, the result will be the same.

If this is the case, you can use a reference to the lock (or the whole struct) instead. Now you need to initialize it, but it's a small price that might save you some obscure bugs. See the modified code that doesn't panic here.

Upvotes: 1

marcio
marcio

Reputation: 10512

Ok, after some unfruitful debugging sessions, as a last resort, I simply wiped all third party code (dependencies) from the workspace:

cd $GOPATH
rm -rf pkg/ bin/ src/github.com  src/golang.org # the idea is to remove all except your own source

Used go get to get all used dependencies again:

go get github.com/yadayada/yada
go get # etc

And the problem is gone! Application is starting normally and tests are passing. No startup panics anymore. It looks like this problem happens when you mv your work space folder but I'm not 100% sure yet. Hope it helps someone else.

From now on, re install dependencies will be my first step when weird panic conditions like that suddenly appear.

Upvotes: 5

Hans Vandierendonck
Hans Vandierendonck

Reputation: 41

You're not giving much information to go on, so my answer is generic.

In theory, bugs in concurrent code can remain unnoticed for a long time and then suddenly show up. In practice, if the bug is easily repeatable (happens nearly every run) this usually indicates that something did change in the code or environment.

The solution: debug. Knowing what has changed can be helpful to identify the bug. In this case, it appears that lock/unlock pairs or not matching up. If you are not passing locks between threads, you should be able to find a code path within the thread that has not acquired the lock, or has released it early. It may be helpful to put assertions at certain points to validate that you are holding the lock when you think you are.

Upvotes: 3

Related Questions