access_lock.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package core
  2. import (
  3. "runtime"
  4. "strconv"
  5. "strings"
  6. "sync"
  7. )
  8. // goroutineID 返回当前 goroutine 的 ID。
  9. //
  10. // 仅用于实现下面的"操作级可重入读写锁":同一 goroutine 在一个写事务内多次进入公开
  11. // 操作 (BeginTransaction → InsertRow → ... → Commit) 时重入不卡死,跨 goroutine
  12. // 则互斥排队。Go 官方不鼓励在业务里依赖 goroutine ID,但用它实现可重入互斥是
  13. // 少数正当用途之一。
  14. func goroutineID() int64 {
  15. var buf [64]byte
  16. n := runtime.Stack(buf[:], false)
  17. // 栈头形如 "goroutine 184 [running]:"
  18. s := strings.TrimPrefix(string(buf[:n]), "goroutine ")
  19. id, _, _ := strings.Cut(s, " ")
  20. v, _ := strconv.ParseInt(id, 10, 64)
  21. return v
  22. }
  23. // accessLock 操作级可重入读写锁。
  24. //
  25. // 设计定位:引擎默认是"单写者、操作串行"模型,但允许并发读。这把锁把多 goroutine
  26. // 的并发调用自动安全化 —— 调用方即使不自己加锁,也不会出现:
  27. // - 两个写操作并发,把页 / 事务结构改坏;
  28. // - 读者在写操作改到一半时读到中间态(脏读);
  29. // - 多个查询互相阻塞(读读并行)。
  30. //
  31. // 锁语义:
  32. //
  33. // RLock / RUnlock —— 读锁, 可并发, 不阻塞其他 RLock, 被写锁阻塞
  34. // Lock / Unlock —— 写锁, 排他, 阻塞所有 RLock 和 Lock
  35. //
  36. // 可重入规则 (关键):
  37. //
  38. // 同 goroutine 已持有写锁 → 再 Lock 加深 depth, 再 RLock 直接跳过
  39. // 同 goroutine 已持有自己的读锁 → 再 RLock 加深 rDepth[g], 再 Lock 直接跳过 (自安全)
  40. //
  41. // 后一条保证 scanWithPKID (RLock) → 回调 DeleteRow (Lock) 这类嵌套不卡死:
  42. // 同 goroutine 自己持有的读锁不阻塞自己的写锁请求 —— 单 goroutine 不可能并发改坏结构。
  43. //
  44. // 跨 goroutine:
  45. //
  46. // 写锁等 (owner==0 && readers - totalSelfReaders == 0) —— 只等**别的 goroutine** 的读者
  47. // 读锁等 (owner==0)
  48. //
  49. // 内部 mu 只用于短暂保护 owner/depth/readers 字段与 cond 等待, 绝不跨临界区持有。
  50. type accessLock struct {
  51. mu sync.Mutex
  52. cond *sync.Cond // 懒初始化
  53. owner int64 // 写锁持有者 goroutine ID (0 = 无人持有)
  54. depth int // 写锁重入深度
  55. readers int32 // 全局读锁总数 (含各 goroutine 自己的)
  56. rDepths map[int64]int // 每个 goroutine 自己的读锁深度
  57. }
  58. // Lock 获取写锁。
  59. // - 同 goroutine 已持有写锁 → depth++, 直接返回 (可重入)
  60. // - 同 goroutine 已持有自己的读锁 → 直接跳过 (自安全, 避免 RLock→Lock 死锁)
  61. // - 否则阻塞, 直到 owner==0 且 readers==0 (没有其他 goroutine 在读)
  62. func (a *accessLock) Lock() {
  63. g := goroutineID()
  64. a.mu.Lock()
  65. if a.cond == nil {
  66. a.cond = sync.NewCond(&a.mu)
  67. if a.rDepths == nil {
  68. a.rDepths = make(map[int64]int)
  69. }
  70. }
  71. // 同 goroutine 已持有写锁 → 重入
  72. if a.owner == g {
  73. a.depth++
  74. a.mu.Unlock()
  75. return
  76. }
  77. // 同 goroutine 已持有自己的读锁 → 跳过 (单 goroutine 自安全)
  78. if selfDepth, ok := a.rDepths[g]; ok && selfDepth > 0 {
  79. a.mu.Unlock()
  80. return
  81. }
  82. // 等待: 别人持有写锁 OR readers > 0
  83. // readers 里**已经扣掉了**自己持有的 (因为上面已经 return 如果自己有), 所以直接等
  84. for a.owner != 0 || a.readers > 0 {
  85. a.cond.Wait()
  86. }
  87. a.owner = g
  88. a.depth = 1
  89. a.mu.Unlock()
  90. }
  91. // Unlock 释放一层写锁。depth 归零才真正交锁并唤醒所有等待者。
  92. func (a *accessLock) Unlock() {
  93. g := goroutineID()
  94. a.mu.Lock()
  95. if a.owner != g {
  96. a.mu.Unlock()
  97. return
  98. }
  99. a.depth--
  100. if a.depth > 0 {
  101. a.mu.Unlock()
  102. return
  103. }
  104. a.owner = 0
  105. a.depth = 0
  106. if a.cond != nil {
  107. a.cond.Broadcast()
  108. }
  109. a.mu.Unlock()
  110. }
  111. // RLock 获取读锁。
  112. // - 同 goroutine 已持有写锁 → 直接返回 (写锁已独占, 读安全)
  113. // - 同 goroutine 已有自己的读锁 → rDepths[g]++, readers++ (读可重入)
  114. // - 否则等待写锁释放, 然后 readers++, rDepths[g]=1
  115. func (a *accessLock) RLock() {
  116. g := goroutineID()
  117. a.mu.Lock()
  118. if a.cond == nil {
  119. a.cond = sync.NewCond(&a.mu)
  120. if a.rDepths == nil {
  121. a.rDepths = make(map[int64]int)
  122. }
  123. }
  124. if a.owner == g {
  125. // 同 goroutine 持有写锁, 读一定安全, 跳过
  126. a.mu.Unlock()
  127. return
  128. }
  129. // 同 goroutine 读锁重入
  130. if a.rDepths[g] > 0 {
  131. a.rDepths[g]++
  132. a.readers++
  133. a.mu.Unlock()
  134. return
  135. }
  136. // 等待: 写锁被别人持有
  137. for a.owner != 0 {
  138. a.cond.Wait()
  139. }
  140. a.rDepths[g] = 1
  141. a.readers++
  142. a.mu.Unlock()
  143. }
  144. // RUnlock 释放当前 goroutine 的一层读锁。
  145. // 自己的 rDepth 归零才从 map 删除。全局 readers 归零时唤醒等待写锁的 goroutine。
  146. func (a *accessLock) RUnlock() {
  147. g := goroutineID()
  148. a.mu.Lock()
  149. if a.rDepths == nil || a.rDepths[g] <= 0 {
  150. a.mu.Unlock()
  151. return
  152. }
  153. a.readers--
  154. a.rDepths[g]--
  155. if a.rDepths[g] == 0 {
  156. delete(a.rDepths, g)
  157. }
  158. if a.readers == 0 && a.cond != nil {
  159. a.cond.Broadcast()
  160. }
  161. a.mu.Unlock()
  162. }
  163. // ============================================================================
  164. // 便捷入口 (调用点用 defer xxx()())
  165. // ============================================================================
  166. // lockOp 写锁便捷写法 —— 原有接口不变, 内部用 Lock。
  167. func (db *DB) lockOp() func() {
  168. db.accMu.Lock()
  169. return db.accMu.Unlock
  170. }
  171. // lockRead 读锁便捷写法 —— 纯查询/读路径用这个, 多个 goroutine 可并行。
  172. func (db *DB) lockRead() func() {
  173. db.accMu.RLock()
  174. return db.accMu.RUnlock
  175. }
  176. // lockWrite 显式写锁便捷写法 (和 lockOp 等价, 命名更明确)。
  177. func (db *DB) lockWrite() func() {
  178. db.accMu.Lock()
  179. return db.accMu.Unlock
  180. }