Line data Source code
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 "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 atomic.Int32 24 : current *version 25 : memtables flushableList 26 : } 27 : 28 : // ref adds a reference to the readState. 29 1 : func (s *readState) ref() { 30 1 : s.refcnt.Add(1) 31 1 : } 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 1 : func (s *readState) unref() { 38 1 : if s.refcnt.Add(-1) != 0 { 39 1 : return 40 1 : } 41 1 : s.current.Unref() 42 1 : for _, mem := range s.memtables { 43 1 : mem.readerUnref(true) 44 1 : } 45 : 46 : // The last reference to the readState was released. Check to see if there 47 : // are new obsolete tables to delete. 48 1 : 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. 54 : // 55 : // DB.mu must be held. See unref() if DB.mu is NOT held by the caller. 56 1 : func (s *readState) unrefLocked() { 57 1 : if s.refcnt.Add(-1) != 0 { 58 1 : return 59 1 : } 60 1 : s.current.UnrefLocked() 61 1 : for _, mem := range s.memtables { 62 1 : mem.readerUnrefLocked(true) 63 1 : } 64 : 65 : // In this code path, the caller is responsible for scheduling obsolete table 66 : // deletion as necessary. 67 : } 68 : 69 : // loadReadState returns the current readState. The returned readState must be 70 : // unreferenced when the caller is finished with it. 71 1 : func (d *DB) loadReadState() *readState { 72 1 : d.readState.RLock() 73 1 : state := d.readState.val 74 1 : state.ref() 75 1 : d.readState.RUnlock() 76 1 : return state 77 1 : } 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. 82 1 : func (d *DB) updateReadStateLocked(checker func(*DB) error) { 83 1 : s := &readState{ 84 1 : db: d, 85 1 : current: d.mu.versions.currentVersion(), 86 1 : memtables: d.mu.mem.queue, 87 1 : } 88 1 : s.refcnt.Store(1) 89 1 : s.current.Ref() 90 1 : for _, mem := range s.memtables { 91 1 : mem.readerRef() 92 1 : } 93 : 94 1 : d.readState.Lock() 95 1 : old := d.readState.val 96 1 : d.readState.val = s 97 1 : d.readState.Unlock() 98 1 : if checker != nil { 99 1 : if err := checker(d); err != nil { 100 0 : d.opts.Logger.Fatalf("checker failed with error: %s", err) 101 0 : } 102 : } 103 1 : if old != nil { 104 1 : old.unrefLocked() 105 1 : } 106 : }