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  }