package core import ( "runtime" "strconv" "strings" "sync" ) // goroutineID 返回当前 goroutine 的 ID。 // // 仅用于实现下面的"操作级可重入读写锁":同一 goroutine 在一个写事务内多次进入公开 // 操作 (BeginTransaction → InsertRow → ... → Commit) 时重入不卡死,跨 goroutine // 则互斥排队。Go 官方不鼓励在业务里依赖 goroutine ID,但用它实现可重入互斥是 // 少数正当用途之一。 func goroutineID() int64 { var buf [64]byte n := runtime.Stack(buf[:], false) // 栈头形如 "goroutine 184 [running]:" s := strings.TrimPrefix(string(buf[:n]), "goroutine ") id, _, _ := strings.Cut(s, " ") v, _ := strconv.ParseInt(id, 10, 64) return v } // accessLock 操作级可重入读写锁。 // // 设计定位:引擎默认是"单写者、操作串行"模型,但允许并发读。这把锁把多 goroutine // 的并发调用自动安全化 —— 调用方即使不自己加锁,也不会出现: // - 两个写操作并发,把页 / 事务结构改坏; // - 读者在写操作改到一半时读到中间态(脏读); // - 多个查询互相阻塞(读读并行)。 // // 锁语义: // // RLock / RUnlock —— 读锁, 可并发, 不阻塞其他 RLock, 被写锁阻塞 // Lock / Unlock —— 写锁, 排他, 阻塞所有 RLock 和 Lock // // 可重入规则 (关键): // // 同 goroutine 已持有写锁 → 再 Lock 加深 depth, 再 RLock 直接跳过 // 同 goroutine 已持有自己的读锁 → 再 RLock 加深 rDepth[g], 再 Lock 直接跳过 (自安全) // // 后一条保证 scanWithPKID (RLock) → 回调 DeleteRow (Lock) 这类嵌套不卡死: // 同 goroutine 自己持有的读锁不阻塞自己的写锁请求 —— 单 goroutine 不可能并发改坏结构。 // // 跨 goroutine: // // 写锁等 (owner==0 && readers - totalSelfReaders == 0) —— 只等**别的 goroutine** 的读者 // 读锁等 (owner==0) // // 内部 mu 只用于短暂保护 owner/depth/readers 字段与 cond 等待, 绝不跨临界区持有。 type accessLock struct { mu sync.Mutex cond *sync.Cond // 懒初始化 owner int64 // 写锁持有者 goroutine ID (0 = 无人持有) depth int // 写锁重入深度 readers int32 // 全局读锁总数 (含各 goroutine 自己的) rDepths map[int64]int // 每个 goroutine 自己的读锁深度 } // Lock 获取写锁。 // - 同 goroutine 已持有写锁 → depth++, 直接返回 (可重入) // - 同 goroutine 已持有自己的读锁 → 直接跳过 (自安全, 避免 RLock→Lock 死锁) // - 否则阻塞, 直到 owner==0 且 readers==0 (没有其他 goroutine 在读) func (a *accessLock) Lock() { g := goroutineID() a.mu.Lock() if a.cond == nil { a.cond = sync.NewCond(&a.mu) if a.rDepths == nil { a.rDepths = make(map[int64]int) } } // 同 goroutine 已持有写锁 → 重入 if a.owner == g { a.depth++ a.mu.Unlock() return } // 同 goroutine 已持有自己的读锁 → 跳过 (单 goroutine 自安全) if selfDepth, ok := a.rDepths[g]; ok && selfDepth > 0 { a.mu.Unlock() return } // 等待: 别人持有写锁 OR readers > 0 // readers 里**已经扣掉了**自己持有的 (因为上面已经 return 如果自己有), 所以直接等 for a.owner != 0 || a.readers > 0 { a.cond.Wait() } a.owner = g a.depth = 1 a.mu.Unlock() } // Unlock 释放一层写锁。depth 归零才真正交锁并唤醒所有等待者。 func (a *accessLock) Unlock() { g := goroutineID() a.mu.Lock() if a.owner != g { a.mu.Unlock() return } a.depth-- if a.depth > 0 { a.mu.Unlock() return } a.owner = 0 a.depth = 0 if a.cond != nil { a.cond.Broadcast() } a.mu.Unlock() } // RLock 获取读锁。 // - 同 goroutine 已持有写锁 → 直接返回 (写锁已独占, 读安全) // - 同 goroutine 已有自己的读锁 → rDepths[g]++, readers++ (读可重入) // - 否则等待写锁释放, 然后 readers++, rDepths[g]=1 func (a *accessLock) RLock() { g := goroutineID() a.mu.Lock() if a.cond == nil { a.cond = sync.NewCond(&a.mu) if a.rDepths == nil { a.rDepths = make(map[int64]int) } } if a.owner == g { // 同 goroutine 持有写锁, 读一定安全, 跳过 a.mu.Unlock() return } // 同 goroutine 读锁重入 if a.rDepths[g] > 0 { a.rDepths[g]++ a.readers++ a.mu.Unlock() return } // 等待: 写锁被别人持有 for a.owner != 0 { a.cond.Wait() } a.rDepths[g] = 1 a.readers++ a.mu.Unlock() } // RUnlock 释放当前 goroutine 的一层读锁。 // 自己的 rDepth 归零才从 map 删除。全局 readers 归零时唤醒等待写锁的 goroutine。 func (a *accessLock) RUnlock() { g := goroutineID() a.mu.Lock() if a.rDepths == nil || a.rDepths[g] <= 0 { a.mu.Unlock() return } a.readers-- a.rDepths[g]-- if a.rDepths[g] == 0 { delete(a.rDepths, g) } if a.readers == 0 && a.cond != nil { a.cond.Broadcast() } a.mu.Unlock() } // ============================================================================ // 便捷入口 (调用点用 defer xxx()()) // ============================================================================ // lockOp 写锁便捷写法 —— 原有接口不变, 内部用 Lock。 func (db *DB) lockOp() func() { db.accMu.Lock() return db.accMu.Unlock } // lockRead 读锁便捷写法 —— 纯查询/读路径用这个, 多个 goroutine 可并行。 func (db *DB) lockRead() func() { db.accMu.RLock() return db.accMu.RUnlock } // lockWrite 显式写锁便捷写法 (和 lockOp 等价, 命名更明确)。 func (db *DB) lockWrite() func() { db.accMu.Lock() return db.accMu.Unlock }