github.com/MetalBlockchain/metalgo@v1.11.9/database/pebbledb/iterator.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package pebbledb
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"slices"
    10  	"sync"
    11  
    12  	"github.com/cockroachdb/pebble"
    13  
    14  	"github.com/MetalBlockchain/metalgo/database"
    15  )
    16  
    17  var (
    18  	_ database.Iterator = (*iter)(nil)
    19  
    20  	errCouldNotGetValue = errors.New("could not get iterator value")
    21  )
    22  
    23  type iter struct {
    24  	// [lock] ensures that only one goroutine can access [iter] at a time.
    25  	// Note that [Database.Close] calls [iter.Release] so we need [lock] to ensure
    26  	// that the user and [Database.Close] don't execute [iter.Release] concurrently.
    27  	// Invariant: [Database.lock] is never grabbed while holding [lock].
    28  	lock sync.Mutex
    29  
    30  	db   *Database
    31  	iter *pebble.Iterator
    32  
    33  	initialized bool
    34  	closed      bool
    35  	err         error
    36  
    37  	hasNext bool
    38  	nextKey []byte
    39  	nextVal []byte
    40  }
    41  
    42  // Must not be called with [db.lock] held.
    43  func (it *iter) Next() bool {
    44  	it.lock.Lock()
    45  	defer it.lock.Unlock()
    46  
    47  	switch {
    48  	case it.err != nil:
    49  		it.hasNext = false
    50  		return false
    51  	case it.closed:
    52  		it.hasNext = false
    53  		it.err = database.ErrClosed
    54  		return false
    55  	case !it.initialized:
    56  		it.hasNext = it.iter.First()
    57  		it.initialized = true
    58  	default:
    59  		it.hasNext = it.iter.Next()
    60  	}
    61  
    62  	if !it.hasNext {
    63  		return false
    64  	}
    65  
    66  	key := it.iter.Key()
    67  	value, err := it.iter.ValueAndErr()
    68  	if err != nil {
    69  		it.hasNext = false
    70  		it.err = fmt.Errorf("%w: %w", errCouldNotGetValue, err)
    71  		return false
    72  	}
    73  
    74  	it.nextKey = key
    75  	it.nextVal = value
    76  	return true
    77  }
    78  
    79  func (it *iter) Error() error {
    80  	it.lock.Lock()
    81  	defer it.lock.Unlock()
    82  
    83  	if it.err != nil || it.closed {
    84  		return it.err
    85  	}
    86  	return updateError(it.iter.Error())
    87  }
    88  
    89  func (it *iter) Key() []byte {
    90  	it.lock.Lock()
    91  	defer it.lock.Unlock()
    92  
    93  	if !it.hasNext {
    94  		return nil
    95  	}
    96  	return slices.Clone(it.nextKey)
    97  }
    98  
    99  func (it *iter) Value() []byte {
   100  	it.lock.Lock()
   101  	defer it.lock.Unlock()
   102  
   103  	if !it.hasNext {
   104  		return nil
   105  	}
   106  	return slices.Clone(it.nextVal)
   107  }
   108  
   109  func (it *iter) Release() {
   110  	it.db.lock.Lock()
   111  	defer it.db.lock.Unlock()
   112  
   113  	it.lock.Lock()
   114  	defer it.lock.Unlock()
   115  
   116  	it.release()
   117  }
   118  
   119  // Assumes [it.lock] and [it.db.lock] are held.
   120  func (it *iter) release() {
   121  	if it.closed {
   122  		return
   123  	}
   124  
   125  	// Cloning these values ensures that calling it.Key() or it.Value() after
   126  	// releasing the iterator will not segfault.
   127  	it.nextKey = slices.Clone(it.nextKey)
   128  	it.nextVal = slices.Clone(it.nextVal)
   129  
   130  	// Remove the iterator from the list of open iterators.
   131  	it.db.openIterators.Remove(it)
   132  
   133  	it.closed = true
   134  	if err := it.iter.Close(); err != nil {
   135  		it.err = updateError(err)
   136  	}
   137  }