github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/read_state.go (about) 1 // Copyright 2019 The LevelDB-Go and Pebble and Bitalostored 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 bitalostable 6 7 import "sync/atomic" 8 9 // readState encapsulates the state needed for reading (the current version and 10 // list of memtables). Loading the readState is done without grabbing 11 // DB.mu. Instead, a separate DB.readState.RWMutex is used for 12 // synchronization. This mutex solely covers the current readState object which 13 // means it is rarely or ever contended. 14 // 15 // Note that various fancy lock-free mechanisms can be imagined for loading the 16 // readState, but benchmarking showed the ones considered to purely be 17 // pessimizations. The RWMutex version is a single atomic increment for the 18 // RLock and an atomic decrement for the RUnlock. It is difficult to do better 19 // than that without something like thread-local storage which isn't available 20 // in Go. 21 type readState struct { 22 db *DB 23 refcnt int32 24 current *version 25 memtables flushableList 26 } 27 28 // ref adds a reference to the readState. 29 func (s *readState) ref() { 30 atomic.AddInt32(&s.refcnt, 1) 31 } 32 33 // unref removes a reference to the readState. If this was the last reference, 34 // the reference the readState holds on the version is released. Requires DB.mu 35 // is NOT held as version.unref() will acquire it. See unrefLocked() if DB.mu 36 // is held by the caller. 37 func (s *readState) unref() { 38 if atomic.AddInt32(&s.refcnt, -1) != 0 { 39 return 40 } 41 s.current.Unref() 42 for _, mem := range s.memtables { 43 mem.readerUnref() 44 } 45 46 // The last reference to the readState was released. Check to see if there 47 // are new obsolete tables to delete. 48 s.db.maybeScheduleObsoleteTableDeletion() 49 } 50 51 // unrefLocked removes a reference to the readState. If this was the last 52 // reference, the reference the readState holds on the version is 53 // released. Requires DB.mu is held as version.unrefLocked() requires it. See 54 // unref() if DB.mu is NOT held by the caller. 55 func (s *readState) unrefLocked() { 56 if atomic.AddInt32(&s.refcnt, -1) != 0 { 57 return 58 } 59 s.current.UnrefLocked() 60 for _, mem := range s.memtables { 61 mem.readerUnref() 62 } 63 64 // NB: Unlike readState.unref(), we don't attempt to cleanup newly obsolete 65 // tables as unrefLocked() is only called during DB shutdown to release the 66 // current readState. 67 } 68 69 // loadReadState returns the current readState. The returned readState must be 70 // unreferenced when the caller is finished with it. 71 func (d *DB) loadReadState() *readState { 72 d.readState.RLock() 73 state := d.readState.val 74 state.ref() 75 d.readState.RUnlock() 76 return state 77 } 78 79 // updateReadStateLocked creates a new readState from the current version and 80 // list of memtables. Requires DB.mu is held. If checker is not nil, it is 81 // called after installing the new readState. If atomicFunc is not nil, it is 82 // executed atomically with the transition to the new read state. 83 func (d *DB) updateReadStateLocked(checker func(*DB) error) { 84 s := &readState{ 85 db: d, 86 refcnt: 1, 87 current: d.mu.versions.currentVersion(), 88 memtables: d.mu.mem.queue, 89 } 90 s.current.Ref() 91 for _, mem := range s.memtables { 92 mem.readerRef() 93 } 94 95 d.readState.Lock() 96 old := d.readState.val 97 d.readState.val = s 98 d.readState.Unlock() 99 if checker != nil { 100 if err := checker(d); err != nil { 101 d.opts.Logger.Fatalf("checker failed with error: %s", err) 102 } 103 } 104 if old != nil { 105 old.unrefLocked() 106 } 107 }