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  }