• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 12199391122

06 Dec 2024 01:10PM UTC coverage: 49.807% (-9.1%) from 58.933%
12199391122

push

github

web-flow
Merge pull request #9337 from Guayaba221/patch-1

chore: fix typo in ruby.md

100137 of 201051 relevant lines covered (49.81%)

2.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.93
/watchtower/wtclient/addr_iterator.go
1
package wtclient
2

3
import (
4
        "container/list"
5
        "errors"
6
        "fmt"
7
        "net"
8
        "sync"
9

10
        "github.com/lightningnetwork/lnd/watchtower/wtdb"
11
)
12

13
var (
14
        // ErrAddressesExhausted signals that a addressIterator has cycled
15
        // through all available addresses.
16
        ErrAddressesExhausted = errors.New("exhausted all addresses")
17

18
        // ErrAddrInUse indicates that an address is locked and cannot be
19
        // removed from the addressIterator.
20
        ErrAddrInUse = errors.New("address in use")
21
)
22

23
// AddressIterator handles iteration over a list of addresses. It strictly
24
// disallows the list of addresses it holds to be empty. It also allows callers
25
// to place locks on certain addresses in order to prevent other callers from
26
// removing the addresses in question from the iterator.
27
type AddressIterator interface {
28
        // Next returns the next candidate address. This iterator will always
29
        // return candidates in the order given when the iterator was
30
        // instantiated. If no more candidates are available,
31
        // ErrAddressesExhausted is returned.
32
        Next() (net.Addr, error)
33

34
        // NextAndLock does the same as described for Next, and it also places a
35
        // lock on the returned address so that the address can not be removed
36
        // until the lock on it has been released via ReleaseLock.
37
        NextAndLock() (net.Addr, error)
38

39
        // Peek returns the currently selected address in the iterator. If the
40
        // end of the iterator has been reached then it is reset and the first
41
        // item in the iterator is returned. Since the AddressIterator will
42
        // never have an empty address list, this function will never return a
43
        // nil value.
44
        Peek() net.Addr
45

46
        // PeekAndLock does the same as described for Peek, and it also places
47
        // a lock on the returned address so that the address can not be removed
48
        // until the lock on it has been released via ReleaseLock.
49
        PeekAndLock() net.Addr
50

51
        // ReleaseLock releases the lock held on the given address.
52
        ReleaseLock(addr net.Addr)
53

54
        // Add adds a new address to the iterator.
55
        Add(addr net.Addr)
56

57
        // Remove removes an existing address from the iterator. It disallows
58
        // the address from being removed if it is the last address in the
59
        // iterator or if there is currently a lock on the address.
60
        Remove(addr net.Addr) error
61

62
        // HasLocked returns true if the addressIterator has any locked
63
        // addresses.
64
        HasLocked() bool
65

66
        // GetAll returns a copy of all the addresses in the iterator.
67
        GetAll() []net.Addr
68

69
        // Reset clears the iterators state, and makes the address at the front
70
        // of the list the next item to be returned.
71
        Reset()
72

73
        // Copy constructs a new AddressIterator that has the same addresses
74
        // as this iterator.
75
        //
76
        // NOTE that the address locks are not expected to be copied.
77
        Copy() AddressIterator
78
}
79

80
// A compile-time check to ensure that addressIterator implements the
81
// AddressIterator interface.
82
var _ AddressIterator = (*addressIterator)(nil)
83

84
// addressIterator is a linked-list implementation of an AddressIterator.
85
type addressIterator struct {
86
        mu             sync.Mutex
87
        addrList       *list.List
88
        currentTopAddr *list.Element
89
        candidates     map[string]*candidateAddr
90
        totalLockCount int
91
}
92

93
type candidateAddr struct {
94
        addr     net.Addr
95
        numLocks int
96
}
97

98
// newAddressIterator constructs a new addressIterator.
99
func newAddressIterator(addrs ...net.Addr) (*addressIterator, error) {
4✔
100
        if len(addrs) == 0 {
4✔
101
                return nil, fmt.Errorf("must have at least one address")
×
102
        }
×
103

104
        iter := &addressIterator{
4✔
105
                addrList:   list.New(),
4✔
106
                candidates: make(map[string]*candidateAddr),
4✔
107
        }
4✔
108

4✔
109
        for _, addr := range addrs {
8✔
110
                addrID := addr.String()
4✔
111
                iter.addrList.PushBack(addrID)
4✔
112
                iter.candidates[addrID] = &candidateAddr{addr: addr}
4✔
113
        }
4✔
114
        iter.Reset()
4✔
115

4✔
116
        return iter, nil
4✔
117
}
118

119
// Reset clears the iterators state, and makes the address at the front of the
120
// list the next item to be returned.
121
//
122
// NOTE: This is part of the AddressIterator interface.
123
func (a *addressIterator) Reset() {
4✔
124
        a.mu.Lock()
4✔
125
        defer a.mu.Unlock()
4✔
126

4✔
127
        a.unsafeReset()
4✔
128
}
4✔
129

130
// unsafeReset clears the iterator state and makes the address at the front of
131
// the list the next item to be returned.
132
//
133
// NOTE: this method is not thread safe and so should only be called if the
134
// appropriate mutex is being held.
135
func (a *addressIterator) unsafeReset() {
4✔
136
        // Reset the next candidate to the front of the linked-list.
4✔
137
        a.currentTopAddr = a.addrList.Front()
4✔
138
}
4✔
139

140
// Next returns the next candidate address. This iterator will always return
141
// candidates in the order given when the iterator was instantiated. If no more
142
// candidates are available, ErrAddressesExhausted is returned.
143
//
144
// NOTE: This is part of the AddressIterator interface.
145
func (a *addressIterator) Next() (net.Addr, error) {
×
146
        return a.next(false)
×
147
}
×
148

149
// NextAndLock does the same as described for Next, and it also places a lock on
150
// the returned address so that the address can not be removed until the lock on
151
// it has been released via ReleaseLock.
152
//
153
// NOTE: This is part of the AddressIterator interface.
154
func (a *addressIterator) NextAndLock() (net.Addr, error) {
×
155
        return a.next(true)
×
156
}
×
157

158
// next returns the next candidate address. This iterator will always return
159
// candidates in the order given when the iterator was instantiated. If no more
160
// candidates are available, ErrAddressesExhausted is returned.
161
func (a *addressIterator) next(lock bool) (net.Addr, error) {
×
162
        a.mu.Lock()
×
163
        defer a.mu.Unlock()
×
164

×
165
        // In-case currentTopAddr is nil (meaning that Reset has not yet been
×
166
        // called), return an error indicating this.
×
167
        if a.currentTopAddr == nil {
×
168
                return nil, ErrAddressesExhausted
×
169
        }
×
170

171
        // Set the next candidate to the subsequent element. If we are at the
172
        // end of the address list, this could mean setting currentTopAddr to
173
        // nil.
174
        a.currentTopAddr = a.currentTopAddr.Next()
×
175

×
176
        for a.currentTopAddr != nil {
×
177
                // Propose the address at the front of the list.
×
178
                addrID := a.currentTopAddr.Value.(string)
×
179

×
180
                // Check whether this address is still considered a candidate.
×
181
                // If it's not, we'll proceed to the next.
×
182
                candidate, ok := a.candidates[addrID]
×
183

×
184
                // If the address cannot be found in the candidate set, then
×
185
                // this must mean that the Remove method was called for the
×
186
                // address. The Remove method would have checked that the
×
187
                // address is not the last one in the iterator and that it has
×
188
                // no locks on it. It is therefor safe to remove.
×
189
                if !ok {
×
190
                        // Grab the next address candidate. This might be nil
×
191
                        // if the iterator is on the last item in the list.
×
192
                        nextCandidate := a.currentTopAddr.Next()
×
193

×
194
                        // Remove the address from the list that is no longer
×
195
                        // in the candidate set.
×
196
                        a.addrList.Remove(a.currentTopAddr)
×
197

×
198
                        // Set the current top to the next candidate. This might
×
199
                        // mean setting it to nil if the iterator is on its last
×
200
                        // item in which case the loop will be exited and an
×
201
                        // ErrAddressesExhausted exhausted error will be
×
202
                        // returned.
×
203
                        a.currentTopAddr = nextCandidate
×
204
                        continue
×
205
                }
206

207
                if lock {
×
208
                        candidate.numLocks++
×
209
                        a.totalLockCount++
×
210
                }
×
211

212
                return candidate.addr, nil
×
213
        }
214

215
        return nil, ErrAddressesExhausted
×
216
}
217

218
// Peek returns the currently selected address in the iterator. If the end of
219
// the list has been reached then the iterator is reset and the first item in
220
// the list is returned. Since the addressIterator will never have an empty
221
// address list, this function will never return a nil value.
222
//
223
// NOTE: This is part of the AddressIterator interface.
224
func (a *addressIterator) Peek() net.Addr {
4✔
225
        return a.peek(false)
4✔
226
}
4✔
227

228
// PeekAndLock does the same as described for Peek, and it also places a lock on
229
// the returned address so that the address can not be removed until the lock
230
// on it has been released via ReleaseLock.
231
//
232
// NOTE: This is part of the AddressIterator interface.
233
func (a *addressIterator) PeekAndLock() net.Addr {
4✔
234
        return a.peek(true)
4✔
235
}
4✔
236

237
// peek returns the currently selected address in the iterator. If the end of
238
// the list has been reached then the iterator is reset and the first item in
239
// the list is returned. Since the addressIterator will never have an empty
240
// address list, this function will never return a nil value. If lock is set to
241
// true, the address will be locked for removal until ReleaseLock has been
242
// called for the address.
243
func (a *addressIterator) peek(lock bool) net.Addr {
4✔
244
        a.mu.Lock()
4✔
245
        defer a.mu.Unlock()
4✔
246

4✔
247
        for {
8✔
248
                // If currentTopAddr is nil, it means we have reached the end of
4✔
249
                // the list, so we reset it here. The iterator always has at
4✔
250
                // least one address, so we can be sure that currentTopAddr will
4✔
251
                // be non-nil after calling reset here.
4✔
252
                if a.currentTopAddr == nil {
4✔
253
                        a.unsafeReset()
×
254
                }
×
255

256
                addrID := a.currentTopAddr.Value.(string)
4✔
257
                candidate, ok := a.candidates[addrID]
4✔
258

4✔
259
                // If the address cannot be found in the candidate set, then
4✔
260
                // this must mean that the Remove method was called for the
4✔
261
                // address. The Remove method would have checked that the
4✔
262
                // address is not the last one in the iterator and that it has
4✔
263
                // no locks on it. It is therefor safe to remove.
4✔
264
                if !ok {
4✔
265
                        // Grab the next address candidate. This might be nil
×
266
                        // if the iterator is on the last item in the list.
×
267
                        nextCandidate := a.currentTopAddr.Next()
×
268

×
269
                        // Remove the address from the list that is no longer
×
270
                        // in the candidate set.
×
271
                        a.addrList.Remove(a.currentTopAddr)
×
272

×
273
                        // Set the current top to the next candidate. This might
×
274
                        // mean setting it to nil if the iterator is on its last
×
275
                        // item but this will be reset at the top of the for
×
276
                        // loop.
×
277
                        a.currentTopAddr = nextCandidate
×
278
                        continue
×
279
                }
280

281
                if lock {
8✔
282
                        candidate.numLocks++
4✔
283
                        a.totalLockCount++
4✔
284
                }
4✔
285

286
                return candidate.addr
4✔
287
        }
288
}
289

290
// ReleaseLock releases the lock held on the given address.
291
//
292
// NOTE: This is part of the AddressIterator interface.
293
func (a *addressIterator) ReleaseLock(addr net.Addr) {
4✔
294
        a.mu.Lock()
4✔
295
        defer a.mu.Unlock()
4✔
296

4✔
297
        candidateAddr, ok := a.candidates[addr.String()]
4✔
298
        if !ok {
4✔
299
                return
×
300
        }
×
301

302
        if candidateAddr.numLocks == 0 {
4✔
303
                return
×
304
        }
×
305

306
        candidateAddr.numLocks--
4✔
307
        a.totalLockCount--
4✔
308
}
309

310
// Add adds a new address to the iterator.
311
//
312
// NOTE: This is part of the AddressIterator interface.
313
func (a *addressIterator) Add(addr net.Addr) {
×
314
        a.mu.Lock()
×
315
        defer a.mu.Unlock()
×
316

×
317
        if _, ok := a.candidates[addr.String()]; ok {
×
318
                return
×
319
        }
×
320

321
        a.addrList.PushBack(addr.String())
×
322
        a.candidates[addr.String()] = &candidateAddr{addr: addr}
×
323

×
324
        // If we've reached the end of our queue, then this candidate
×
325
        // will become the next.
×
326
        if a.currentTopAddr == nil {
×
327
                a.currentTopAddr = a.addrList.Back()
×
328
        }
×
329
}
330

331
// Remove removes an existing address from the iterator. It disallows the
332
// address from being removed if it is the last address in the iterator or if
333
// there is currently a lock on the address.
334
//
335
// NOTE: This is part of the AddressIterator interface.
336
func (a *addressIterator) Remove(addr net.Addr) error {
×
337
        a.mu.Lock()
×
338
        defer a.mu.Unlock()
×
339

×
340
        candidate, ok := a.candidates[addr.String()]
×
341
        if !ok {
×
342
                return nil
×
343
        }
×
344

345
        if len(a.candidates) == 1 {
×
346
                return wtdb.ErrLastTowerAddr
×
347
        }
×
348

349
        if candidate.numLocks > 0 {
×
350
                return ErrAddrInUse
×
351
        }
×
352

353
        delete(a.candidates, addr.String())
×
354
        return nil
×
355
}
356

357
// HasLocked returns true if the addressIterator has any locked addresses.
358
//
359
// NOTE: This is part of the AddressIterator interface.
360
func (a *addressIterator) HasLocked() bool {
4✔
361
        a.mu.Lock()
4✔
362
        defer a.mu.Unlock()
4✔
363

4✔
364
        return a.totalLockCount > 0
4✔
365
}
4✔
366

367
// GetAll returns a copy of all the addresses in the iterator.
368
//
369
// NOTE: This is part of the AddressIterator interface.
370
func (a *addressIterator) GetAll() []net.Addr {
×
371
        a.mu.Lock()
×
372
        defer a.mu.Unlock()
×
373

×
374
        return a.getAllUnsafe()
×
375
}
×
376

377
// Copy constructs a new AddressIterator that has the same addresses
378
// as this iterator.
379
//
380
// NOTE that the address locks will not be copied.
381
func (a *addressIterator) Copy() AddressIterator {
4✔
382
        a.mu.Lock()
4✔
383
        defer a.mu.Unlock()
4✔
384

4✔
385
        addrs := a.getAllUnsafe()
4✔
386

4✔
387
        // Since newAddressIterator will only ever return an error if it is
4✔
388
        // initialised with zero addresses, we can ignore the error here since
4✔
389
        // we are initialising it with the set of addresses of this
4✔
390
        // addressIterator which is by definition a non-empty list.
4✔
391
        iter, _ := newAddressIterator(addrs...)
4✔
392

4✔
393
        return iter
4✔
394
}
4✔
395

396
// getAllUnsafe returns a copy of all the addresses in the iterator.
397
//
398
// NOTE: this method is not thread safe and so must only be called once the
399
// addressIterator mutex is already being held.
400
func (a *addressIterator) getAllUnsafe() []net.Addr {
4✔
401
        var addrs []net.Addr
4✔
402
        cursor := a.addrList.Front()
4✔
403

4✔
404
        for cursor != nil {
8✔
405
                addrID := cursor.Value.(string)
4✔
406

4✔
407
                addr, ok := a.candidates[addrID]
4✔
408
                if !ok {
4✔
409
                        cursor = cursor.Next()
×
410
                        continue
×
411
                }
412

413
                addrs = append(addrs, addr.addr)
4✔
414
                cursor = cursor.Next()
4✔
415
        }
416

417
        return addrs
4✔
418
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc