| // Copyright 2026 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package sql |
| |
| import ( |
| "sync" |
| "sync/atomic" |
| ) |
| |
| // A closingMutex is an RWMutex for synchronizing close. |
| // Unlike a sync.RWMutex, RLock takes priority over Lock. |
| // Reads can starve out close, but reads are safely reentrant. |
| type closingMutex struct { |
| // state is 2*readers+writerWaiting. |
| // 0 is unlocked |
| // 1 is unlocked and a writer needs to wake |
| // >0 is read-locked |
| // <0 is write-locked |
| state atomic.Int64 |
| mu sync.Mutex |
| read *sync.Cond |
| write *sync.Cond |
| } |
| |
| func (m *closingMutex) RLock() { |
| if m.TryRLock() { |
| return |
| } |
| |
| // Wait for writer. |
| m.mu.Lock() |
| defer m.mu.Unlock() |
| for { |
| if m.TryRLock() { |
| return |
| } |
| m.init() |
| m.read.Wait() |
| } |
| } |
| |
| func (m *closingMutex) RUnlock() { |
| for { |
| x := m.state.Load() |
| if x < 2 { |
| panic("runlock of un-rlocked mutex") |
| } |
| if m.state.CompareAndSwap(x, x-2) { |
| if x-2 == 1 { |
| // We were the last reader, and a writer is waiting. |
| // The lock makes sure the writer sees the broadcast. |
| m.mu.Lock() |
| defer m.mu.Unlock() |
| m.write.Broadcast() |
| } |
| return |
| } |
| } |
| } |
| |
| func (m *closingMutex) Lock() { |
| m.mu.Lock() |
| defer m.mu.Unlock() |
| for { |
| x := m.state.Load() |
| if (x == 0 || x == 1) && m.state.CompareAndSwap(x, -1) { |
| return |
| } |
| // Set writer waiting bit and sleep. |
| if x&1 == 0 && !m.state.CompareAndSwap(x, x|1) { |
| continue |
| } |
| m.init() |
| m.write.Wait() |
| } |
| } |
| |
| func (m *closingMutex) Unlock() { |
| m.mu.Lock() |
| defer m.mu.Unlock() |
| if !m.state.CompareAndSwap(-1, 0) { |
| panic("unlock of unlocked mutex") |
| } |
| if m.read != nil { |
| m.read.Broadcast() |
| m.write.Broadcast() |
| } |
| } |
| |
| func (m *closingMutex) TryRLock() bool { |
| for { |
| x := m.state.Load() |
| if x < 0 { |
| return false |
| } |
| if m.state.CompareAndSwap(x, x+2) { |
| return true |
| } |
| } |
| } |
| |
| func (m *closingMutex) init() { |
| // Lazily create the read/write Conds. |
| // In the common, uncontended case, we'll never need them. |
| if m.read == nil { |
| m.read = sync.NewCond(&m.mu) |
| m.write = sync.NewCond(&m.mu) |
| } |
| } |