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 }