github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/read_state.go (about) 1 // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package pebble 6 7 import ( 8 "sync/atomic" 9 ) 10 11 // readState encapsulates the state needed for reading (the current version and 12 // list of memtables). Loading the readState is done without grabbing 13 // DB.mu. Instead, a separate DB.readState.RWMutex is used for 14 // synchronization. This mutex solely covers the current readState object which 15 // means it is rarely or ever contended. 16 // 17 // Note that various fancy lock-free mechanisms can be imagined for loading the 18 // readState, but benchmarking showed the ones considered to purely be 19 // pessimizations. The RWMutex version is a single atomic increment for the 20 // RLock and an atomic decrement for the RUnlock. It is difficult to do better 21 // than that without something like thread-local storage which isn't available 22 // in Go. 23 type readState struct { 24 refcnt int32 25 current *version 26 memtables []flushable 27 } 28 29 // ref adds a reference to the readState. 30 func (s *readState) ref() { 31 atomic.AddInt32(&s.refcnt, 1) 32 } 33 34 // unref removes a reference to the readState. If this was the last reference, 35 // the reference the readState holds on the version is released. Requires DB.mu 36 // is NOT held as version.unref() will acquire it. See unrefLocked() if DB.mu 37 // is held by the caller. 38 func (s *readState) unref() { 39 if atomic.AddInt32(&s.refcnt, -1) == 0 { 40 s.current.Unref() 41 } 42 } 43 44 // unrefLocked removes a reference to the readState. If this was the last 45 // reference, the reference the readState holds on the version is 46 // released. Requires DB.mu is held as version.unrefLocked() requires it. See 47 // unref() if DB.mu is NOT held by the caller. 48 func (s *readState) unrefLocked() { 49 if atomic.AddInt32(&s.refcnt, -1) == 0 { 50 s.current.UnrefLocked() 51 } 52 } 53 54 // loadReadState returns the current readState. The returned readState must be 55 // unreferenced when the caller is finished with it. 56 func (d *DB) loadReadState() *readState { 57 d.readState.RLock() 58 state := d.readState.val 59 state.ref() 60 d.readState.RUnlock() 61 return state 62 } 63 64 // updateReadStateLocked creates a new readState from the current version and 65 // list of memtables. Requires DB.mu is held. 66 func (d *DB) updateReadStateLocked() { 67 s := &readState{ 68 refcnt: 1, 69 current: d.mu.versions.currentVersion(), 70 memtables: d.mu.mem.queue, 71 } 72 s.current.Ref() 73 74 d.readState.Lock() 75 old := d.readState.val 76 d.readState.val = s 77 d.readState.Unlock() 78 79 if old != nil { 80 old.unrefLocked() 81 } 82 }