lazygit icon indicating copy to clipboard operation
lazygit copied to clipboard

fatal error: all goroutines are asleep - deadlock!

Open beetol opened this issue 1 year ago • 5 comments

Describe the bug

fatal error: all goroutines are asleep - deadlock!

To Reproduce Steps to reproduce the behavior (macOS Sonoma latest version, latest go installation from its website):

  1. go install github.com/jesseduffield/lazygit@latest

Expected behavior Open LazyGit interface

Version info:

lazygit version commit=, build date=, build source=unknown, version=unversioned, os=darwin, arch=amd64, git version=2.46.0

git version git version 2.46.0

Additional context Running on macOS Sonoma 14.6 Have added ~/go/bin to $PATH as instructed.

POTENTIAL DEADLOCK: Recursive locking: when running lazygit --debug

beetol avatar Sep 01 '24 03:09 beetol

Your reproduction recipe shows pretty much basic, normal lazygit usage as far as I can tell. There must be something more that is specific to your setup, otherwise lazygit would be unusable for everybody.

Are you starting lazygit from within a git repo? Does the problem happen for all your repos, or only certain ones? Have you checked if an official build (e.g. the homebrew version) has the same problem?

stefanhaller avatar Sep 02 '24 06:09 stefanhaller

Are you starting lazygit from within a git repo?

Yes, a good example is the mantinedev/mantine repo I've cloned. It also happens to one of my own repos.

Have you checked if an official build (e.g. the homebrew version) has the same problem?

Yes, have tried using the Homebrew version and I still get the same error.

On a tangent, with the steps above, I don't get this error on an Apple Silicon. Though when running lazygit --debug, it just hangs without any output.

beetol avatar Sep 02 '24 23:09 beetol

I was looking into #3903 and ran into this issue with a binary compiled from a local checkout. Here's the trace from two different attempts:

Attempt 1

POTENTIAL DEADLOCK: Inconsistent locking. saw this ordering in one goroutine:
happened before
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:442 helpers.(*RefreshHelper).refreshBranches { self.c.Mutexes().RefreshingBranchesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:273 helpers.(*RefreshHelper).refreshReflogAndBranches { self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex, loadBehindCounts) }
pkg/gui/controllers/helpers/refresh_helper.go:129 helpers.(*RefreshHelper).Refresh.func2.2 { if self.c.AppState.LocalBranchSortOrder == "recency" { }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux {  }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }

happened after
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/commands/git_commands/main_branches.go:42 git_commands.(*MainBranches).Get { self.mutex.Lock() }
pkg/commands/git_commands/main_branches.go:56 git_commands.(*MainBranches).GetMergeBase { func (self *MainBranches) GetMergeBase(refName string) string { }
pkg/commands/git_commands/commit_loader.go:105 git_commands.(*CommitLoader).GetCommits.func2 {  }
pkg/utils/utils.go:87 utils.Safe.func1 { func Safe(f func()) { }
pkg/utils/utils.go:98 utils.SafeWithError {  }
pkg/utils/utils.go:88 utils.Safe { _ = SafeWithError(func() error { f(); return nil }) }

in another goroutine: happened before
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/commands/git_commands/main_branches.go:42 git_commands.(*MainBranches).Get { self.mutex.Lock() }
pkg/commands/git_commands/main_branches.go:56 git_commands.(*MainBranches).GetMergeBase { func (self *MainBranches) GetMergeBase(refName string) string { }
pkg/commands/git_commands/commit_loader.go:105 git_commands.(*CommitLoader).GetCommits.func2 {  }
pkg/utils/utils.go:87 utils.Safe.func1 { func Safe(f func()) { }
pkg/utils/utils.go:98 utils.SafeWithError {  }
pkg/utils/utils.go:88 utils.Safe { _ = SafeWithError(func() error { f(); return nil }) }

happened after
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:442 helpers.(*RefreshHelper).refreshBranches { self.c.Mutexes().RefreshingBranchesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:258 helpers.(*RefreshHelper).refreshReflogCommitsConsideringStartup.func1 { self.refreshBranches(false, true, true) }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux {  }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }

Other goroutines holding locks:
goroutine 2 lock 0xc00040bb38
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:512 helpers.(*RefreshHelper).refreshFilesAndSubmodules { self.c.Mutexes().RefreshingFilesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:154 helpers.(*RefreshHelper).Refresh.func2.8 { _ = self.refreshFilesAndSubmodules() }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux {  }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }

goroutine 2 lock 0xc0003d3128
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/commands/git_commands/main_branches.go:42 git_commands.(*MainBranches).Get { self.mutex.Lock() }
pkg/commands/git_commands/main_branches.go:56 git_commands.(*MainBranches).GetMergeBase { func (self *MainBranches) GetMergeBase(refName string) string { }
pkg/commands/git_commands/commit_loader.go:105 git_commands.(*CommitLoader).GetCommits.func2 {  }
pkg/utils/utils.go:87 utils.Safe.func1 { func Safe(f func()) { }
pkg/utils/utils.go:98 utils.SafeWithError {  }
pkg/utils/utils.go:88 utils.Safe { _ = SafeWithError(func() error { f(); return nil }) }

goroutine 2 lock 0xc00040bb50
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:322 helpers.(*RefreshHelper).refreshCommitsWithLimit { self.c.Mutexes().LocalCommitsMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:277 helpers.(*RefreshHelper).refreshCommitsAndCommitFiles { _ = self.refreshCommitsWithLimit() }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux {  }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }

Attempt 2

POTENTIAL DEADLOCK: Recursive locking:
current goroutine 2 lock 0xc00040db40
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:442 helpers.(*RefreshHelper).refreshBranches { self.c.Mutexes().RefreshingBranchesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:258 helpers.(*RefreshHelper).refreshReflogCommitsConsideringStartup.func1 { self.refreshBranches(false, true, true) }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux {  }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }

Previous place where the lock was grabbed (same goroutine)
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:442 helpers.(*RefreshHelper).refreshBranches { self.c.Mutexes().RefreshingBranchesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:273 helpers.(*RefreshHelper).refreshReflogAndBranches { self.refreshBranches(refreshWorktrees, keepBranchSelectionIndex, loadBehindCounts) }
pkg/gui/controllers/helpers/refresh_helper.go:129 helpers.(*RefreshHelper).Refresh.func2.2 { if self.c.AppState.LocalBranchSortOrder == "recency" { }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux {  }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }

Other goroutines holding locks:
goroutine 2 lock 0xc00040db38
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:512 helpers.(*RefreshHelper).refreshFilesAndSubmodules { self.c.Mutexes().RefreshingFilesMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:154 helpers.(*RefreshHelper).Refresh.func2.8 { _ = self.refreshFilesAndSubmodules() }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux {  }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }

goroutine 2 lock 0xc00040db50
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/gui/controllers/helpers/refresh_helper.go:322 helpers.(*RefreshHelper).refreshCommitsWithLimit { self.c.Mutexes().LocalCommitsMutex.Lock() }
pkg/gui/controllers/helpers/refresh_helper.go:277 helpers.(*RefreshHelper).refreshCommitsAndCommitFiles { _ = self.refreshCommitsWithLimit() }
pkg/gui/controllers/helpers/refresh_helper.go:107 helpers.(*RefreshHelper).Refresh.func2.1.1 { f() }
vendor/github.com/jesseduffield/gocui/gui.go:700 gocui.(*Gui).onWorkerAux {  }
vendor/github.com/jesseduffield/gocui/gui.go:688 gocui.(*Gui).OnWorker.func1 { g.onWorkerAux(f, task) }

goroutine 2 lock 0xc00036a568
vendor/github.com/sasha-s/go-deadlock/deadlock.go:85 go-deadlock.(*Mutex).Lock { lock(m.mu.Lock, m) } <<<<<
pkg/commands/git_commands/main_branches.go:42 git_commands.(*MainBranches).Get { self.mutex.Lock() }
pkg/commands/git_commands/main_branches.go:56 git_commands.(*MainBranches).GetMergeBase { func (self *MainBranches) GetMergeBase(refName string) string { }
pkg/commands/git_commands/commit_loader.go:105 git_commands.(*CommitLoader).GetCommits.func2 {  }
pkg/utils/utils.go:87 utils.Safe.func1 { func Safe(f func()) { }
pkg/utils/utils.go:98 utils.SafeWithError {  }
pkg/utils/utils.go:88 utils.Safe { _ = SafeWithError(func() error { f(); return nil }) }

For what it's worth, this was only when running a locally compiled version: commit=a04ad24a60584135e94a7eb2ff32c956e1e5a83d, build date=2024-09-18T11:26:47Z, build source=unknown, version=a04ad24a, os=linux, arch=amd64, git version=2.34.1

The downloaded version runs fine: commit=2d0c7cb0fc85e3c262837eb9691813fa97e00e80, build date=2024-09-07T02:26:26Z, build source=binaryRelease, version=0.44.0, os=linux, arch=amd64, git version=2.34.1

RBird111 avatar Sep 22 '24 22:09 RBird111

I've just seen this after a clean clone of the lazygit repo, setting up the devcontainer, then running go run main.go -debug from within the devcontainer. It also doesn't work in the codespace recommended in CONTRIBUTING.md.

However, it doesn't happen without -debug.

I also can't run any of the integration tests, which is making contributing something somewhat challenging...

canton7 avatar Sep 27 '24 08:09 canton7

FWIW, deadlock.Opts.DisableLockOrderDetection = true at the top of NewGui disables that error, so at least you can run the tests. I had a flick through the code it was unhappy about, and couldn't spot anything wrong.

canton7 avatar Sep 29 '24 15:09 canton7

https://github.com/jesseduffield/lazygit/issues/4002

phanirithvij avatar Dec 02 '24 06:12 phanirithvij

Potentially connected to https://github.com/sasha-s/go-deadlock/issues/38

ChrisMcD1 avatar Feb 10 '25 05:02 ChrisMcD1