| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- 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
- }
|