// Copyright 2025 The Go Authors. All rights reserved. // Use of this source code is governed by a MIT // license that can be found in the LICENSE file. /* * Project: cockroach * Issue or PR : https://github.com/cockroachdb/cockroach/pull/7504 * Buggy version: bc963b438cdc3e0ad058a5282358e5aee0595e17 * fix commit-id: cab761b9f5ee5dee1448bc5d6b1d9f5a0ff0bad5 * Flaky: 1/100 */ package main import ( "os" "runtime" "runtime/pprof" "sync" "time" ) func init() { register("Cockroach7504", Cockroach7504) } func MakeCacheKey_cockroach7504(lease *LeaseState_cockroach7504) int { return lease.id } type LeaseState_cockroach7504 struct { mu sync.Mutex // L1 id int } type LeaseSet_cockroach7504 struct { data []*LeaseState_cockroach7504 } func (l *LeaseSet_cockroach7504) find(id int) *LeaseState_cockroach7504 { return l.data[id] } func (l *LeaseSet_cockroach7504) remove(s *LeaseState_cockroach7504) { for i := 0; i < len(l.data); i++ { if s == l.data[i] { l.data = append(l.data[:i], l.data[i+1:]...) break } } } type tableState_cockroach7504 struct { tableNameCache *tableNameCache_cockroach7504 mu sync.Mutex // L3 active *LeaseSet_cockroach7504 } func (t *tableState_cockroach7504) release(lease *LeaseState_cockroach7504) { t.mu.Lock() // L3 defer t.mu.Unlock() // L3 s := t.active.find(MakeCacheKey_cockroach7504(lease)) s.mu.Lock() // L1 runtime.Gosched() defer s.mu.Unlock() // L1 t.removeLease(s) } func (t *tableState_cockroach7504) removeLease(lease *LeaseState_cockroach7504) { t.active.remove(lease) t.tableNameCache.remove(lease) // L1 acquire/release } type tableNameCache_cockroach7504 struct { mu sync.Mutex // L2 tables map[int]*LeaseState_cockroach7504 } func (c *tableNameCache_cockroach7504) get(id int) { c.mu.Lock() // L2 defer c.mu.Unlock() // L2 lease, ok := c.tables[id] if !ok { return } if lease == nil { panic("nil lease in name cache") } lease.mu.Lock() // L1 defer lease.mu.Unlock() // L1 } func (c *tableNameCache_cockroach7504) remove(lease *LeaseState_cockroach7504) { c.mu.Lock() // L2 runtime.Gosched() defer c.mu.Unlock() // L2 key := MakeCacheKey_cockroach7504(lease) existing, ok := c.tables[key] if !ok { return } if existing == lease { delete(c.tables, key) } } type LeaseManager_cockroach7504 struct { _ [64]byte tableNames *tableNameCache_cockroach7504 tables map[int]*tableState_cockroach7504 } func (m *LeaseManager_cockroach7504) AcquireByName(id int) { m.tableNames.get(id) } func (m *LeaseManager_cockroach7504) findTableState(lease *LeaseState_cockroach7504) *tableState_cockroach7504 { existing, ok := m.tables[lease.id] if !ok { return nil } return existing } func (m *LeaseManager_cockroach7504) Release(lease *LeaseState_cockroach7504) { t := m.findTableState(lease) t.release(lease) } func NewLeaseManager_cockroach7504(tname *tableNameCache_cockroach7504, ts *tableState_cockroach7504) *LeaseManager_cockroach7504 { mgr := &LeaseManager_cockroach7504{ tableNames: tname, tables: make(map[int]*tableState_cockroach7504), } mgr.tables[0] = ts return mgr } func NewLeaseSet_cockroach7504(n int) *LeaseSet_cockroach7504 { lset := &LeaseSet_cockroach7504{} for i := 0; i < n; i++ { lease := new(LeaseState_cockroach7504) lset.data = append(lset.data, lease) } return lset } func Cockroach7504() { prof := pprof.Lookup("goroutineleak") defer func() { time.Sleep(100 * time.Millisecond) prof.WriteTo(os.Stdout, 2) }() for i := 0; i < 100; i++ { go func() { leaseNum := 2 lset := NewLeaseSet_cockroach7504(leaseNum) nc := &tableNameCache_cockroach7504{ tables: make(map[int]*LeaseState_cockroach7504), } for i := 0; i < leaseNum; i++ { nc.tables[i] = lset.find(i) } ts := &tableState_cockroach7504{ tableNameCache: nc, active: lset, } mgr := NewLeaseManager_cockroach7504(nc, ts) // G1 go func() { // lock L2-L1 mgr.AcquireByName(0) }() // G2 go func() { // lock L1-L2 mgr.Release(lset.find(0)) }() }() } }