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 : }
|