cogentcore / core

A free and open source framework for building powerful, fast, elegant 2D and 3D apps that run on macOS, Windows, Linux, iOS, Android, and the web with a single Go codebase, allowing you to Code Once, Run Everywhere.
http://cogentcore.org/core
BSD 3-Clause "New" or "Revised" License
1.7k stars 79 forks source link

fatal error: sync: RUnlock of unlocked RWMutex #886

Closed ddkwork closed 7 months ago

ddkwork commented 7 months ago

Describe the bug

see title

How to reproduce

Run multiple times

Example code

package main

import (
    "cogentcore.org/core/gi"
    "cogentcore.org/core/giv"
    "cogentcore.org/core/icons"
)

type TableStruct struct { //gti:add
    Icon       icons.Icon
    IntField   int `default:"2"`
    FloatField float32
    StrField   string
    File       gi.Filename
}

func main() {
    table := make([]*TableStruct, 0, 100000)
    b := gi.NewBody("leak")
    tv := giv.NewTableView(b, "tv")
    tv.SetReadOnly(true)
    tv.SetSlice(&table)

    go func() {
        for i := 0; i < 100000; i++ {
            table = append(table, &TableStruct{IntField: i, FloatField: float32(i) / 10.0})
            updt := tv.UpdateStartAsync()
            tv.UpdateWidgets()
            tv.UpdateEndAsyncRender(updt)
            if len(table) > 0 {
                tv.ScrollToIdx(len(table) - 1)
            }
        }
    }()

    b.RunMainWindow()
}

Relevant output

GOROOT=C:\Users\Admin\go\pkg\mod\golang.org\toolchain@v0.0.1-go1.22.0.windows-amd64 #gosetup
GOPATH=C:\Users\Admin\go #gosetup
C:\Users\Admin\go\pkg\mod\golang.org\toolchain@v0.0.1-go1.22.0.windows-amd64\bin\go.exe build -o C:\Users\Admin\AppData\Local\JetBrains\GoLand2023.3\tmp\GoLand\___1go_build_github_com_ddkwork_workspace_leak.exe github.com/ddkwork/workspace/leak #gosetup
C:\Users\Admin\AppData\Local\JetBrains\GoLand2023.3\tmp\GoLand\___1go_build_github_com_ddkwork_workspace_leak.exe
fatal error: sync: RUnlock of unlocked RWMutex

goroutine 14 [running]:
sync.fatal({0x7ff75412e055?, 0xc000037ef0?})
        C:/Users/Admin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.0.windows-amd64/src/runtime/panic.go:1007 +0x18
sync.(*RWMutex).rUnlockSlow(0xc0000262b0, 0x37f28?)

        C:/Users/Admin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.0.windows-amd64/src/sync/rwmutex.go:128 +0x39
sync.(*RWMutex).RUnlock(...)

        C:/Users/Admin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.0.windows-amd64/src/sync/rwmutex.go:118
cogentcore.org/core/gi.(*RenderContext).ReadUnlock(0xc00012e598?)
        D:/workspace/workspace/core/gi/renderwin.go:856 +0x27
cogentcoD:/workspace/workspace/core/gi/render.go:150 +0xd92ba408, 0x1)
cogentcoD:/workspace/workspace/core/gi/render.go:168 +0x1c0xc0002ba408, 0x1)
main.maiD:/workspace/workspace/leak/leak.go:29 +0x145
goroutine 1 [syscall, locked to thread]:.go:24 +0x175
github.com/go-gl/glfw/v3.3/glfw._Cfunc_glfwWaitEvents()
        _cgo_gotypes.go:2494 +0x49
github.com/go-gl/glfw/v3.3/glfw.WaitEvents()
        C:/Users/Admin/go/pkg/mod/github.com/go-gl/glfw/v3.3/glfw@v0.0.0-2024011
                                                                               18000515-a250818d05e3/window.go:949 +0x13
cogentcore.org/core/goosi/driver/desktop.(*App).MainLoop(0x7ff754c7bd00)        
        D:/workspace/workspace/core/goosi/driver/desktop/app.go:73 +0x125       
cogentcore.org/core/gi.Wait()
        D:/workspace/workspace/core/gi/renderwin.go:43 +0x4b
cogentcore.org/core/gi.(*Stage).Wait(...)
        D:/workspace/workspace/core/gi/stage.go:316
cogentcore.org/core/gi.(*Body).RunMainWindow(0xc0002ba408?)
        D:/workspace/workspace/core/gi/mainstage.go:38 +0x2b
main.main()
        D:/workspace/workspace/leak/leak.go:36 +0x17f

goroutine 36 [select]:
cogentcore.org/core/goosi/driver/base.(*Window[...]).WinLoop(0x7ff754542de0)    
        D:/workspace/workspace/core/goosi/driver/base/window.go:93 +0x136       
created by cogentcore.org/core/goosi/driver/desktop.(*App).NewWindow in goroutin
                                                                               ne 1
        D:/workspace/workspace/core/goosi/driver/desktop/app.go:171 +0xa66      

goroutine 62 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000504b40, 0x0)
        C:/Users/Admin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.0.windows-a
                                                                               amd64/src/runtime/sema.go:569 +0x15d
sync.(*Cond).Wait(0x0?)
        C:/Users/Admin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.0.windows-a
                                                                               amd64/src/sync/cond.go:70 +0x85
cogentcore.org/core/events.(*Deque).NextEvent(0xc000504af8)
        D:/workspace/workspace/core/events/deque.go:65 +0x6e
cogentcore.org/core/gi.(*RenderWin).EventLoop(0xc000752000)
        D:/workspace/workspace/core/gi/renderwin.go:606 +0xb5
created by cogentcore.org/core/gi.(*RenderWin).GoStartEventLoop in goroutine 1  
        D:/workspace/workspace/core/gi/renderwin.go:565 +0xa5

goroutine 63 [semacquire]:
sync.runtime_Semacquire(0x0?)
        C:/Users/Admin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.0.windows-a
                                                                               amd64/src/runtime/sema.go:62 +0x25
sync.(*WaitGroup).Wait(0x0?)
        C:/Users/Admin/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.0.windows-a
                                                                               amd64/src/sync/waitgroup.go:116 +0x48
cogentcore.org/core/gi.Wait.func2()
        D:/workspace/workspace/core/gi/renderwin.go:40 +0x37
created by cogentcore.org/core/gi.Wait in goroutine 1
        D:/workspace/workspace/core/gi/renderwin.go:38 +0x37

进程 已完成,退出代码为 2

A

Platform

Windows

ddkwork commented 7 months ago

Stable reproduction method: Run it a few more times, only observe the situation that it is not working normally, and start over after it is running

kkoreilly commented 7 months ago

I will work on fixing this.

kkoreilly commented 7 months ago

You should only start updating the data in the table once the scene is shown. For example, this code should work:

package main

import (
    "cogentcore.org/core/events"
    "cogentcore.org/core/gi"
    "cogentcore.org/core/giv"
    "cogentcore.org/core/icons"
)

type TableStruct struct { //gti:add
    Icon       icons.Icon
    IntField   int `default:"2"`
    FloatField float32
    StrField   string
    File       gi.Filename
}

func main() {
    table := make([]*TableStruct, 0, 100000)
    b := gi.NewBody("leak")
    tv := giv.NewTableView(b, "tv")
    tv.SetReadOnly(true)
    tv.SetSlice(&table)

    b.OnShow(func(e events.Event) {
        go func() {
            for i := 0; i < 100000; i++ {
                table = append(table, &TableStruct{IntField: i, FloatField: float32(i) / 10.0})
                updt := tv.UpdateStartAsync()
                tv.UpdateWidgets()
                tv.UpdateEndAsyncLayout(updt)
                if len(table) > 0 {
                    tv.ScrollToIdx(len(table) - 1)
                }
            }
        }()
    })

    b.RunMainWindow()
}
ddkwork commented 7 months ago

You should only start updating the data in the table once the scene is shown. For example, this code should work:

package main

import (
  "cogentcore.org/core/events"
  "cogentcore.org/core/gi"
  "cogentcore.org/core/giv"
  "cogentcore.org/core/icons"
)

type TableStruct struct { //gti:add
  Icon       icons.Icon
  IntField   int `default:"2"`
  FloatField float32
  StrField   string
  File       gi.Filename
}

func main() {
  table := make([]*TableStruct, 0, 100000)
  b := gi.NewBody("leak")
  tv := giv.NewTableView(b, "tv")
  tv.SetReadOnly(true)
  tv.SetSlice(&table)

  b.OnShow(func(e events.Event) {
      go func() {
          for i := 0; i < 100000; i++ {
              table = append(table, &TableStruct{IntField: i, FloatField: float32(i) / 10.0})
              updt := tv.UpdateStartAsync()
              tv.UpdateWidgets()
              tv.UpdateEndAsyncLayout(updt)
              if len(table) > 0 {
                  tv.ScrollToIdx(len(table) - 1)
              }
          }
      }()
  })

  b.RunMainWindow()
}

Thanks!