@@ -69,32 +69,40 @@ struct Announcement {
69
69
/* * Whether this is a wtxid request. */
70
70
const bool m_is_wtxid : 1 ;
71
71
72
- /* * What state this announcement is in. */
73
- State m_state : 3 ;
72
+ /* * What state this announcement is in.
73
+ * This is a uint8_t instead of a State to silence a GCC warning in versions prior to 8.4 and 9.3.
74
+ * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414 */
75
+ uint8_t m_state : 3 ;
76
+
77
+ /* * Convert m_state to a State enum. */
78
+ State GetState () const { return static_cast <State>(m_state); }
79
+
80
+ /* * Convert a State enum to a uint8_t and store it in m_state. */
81
+ void SetState (State state) { m_state = static_cast <uint8_t >(state); }
74
82
75
83
/* * Whether this announcement is selected. There can be at most 1 selected peer per txhash. */
76
84
bool IsSelected () const
77
85
{
78
- return m_state == State::CANDIDATE_BEST || m_state == State::REQUESTED;
86
+ return GetState () == State::CANDIDATE_BEST || GetState () == State::REQUESTED;
79
87
}
80
88
81
89
/* * Whether this announcement is waiting for a certain time to pass. */
82
90
bool IsWaiting () const
83
91
{
84
- return m_state == State::REQUESTED || m_state == State::CANDIDATE_DELAYED;
92
+ return GetState () == State::REQUESTED || GetState () == State::CANDIDATE_DELAYED;
85
93
}
86
94
87
95
/* * Whether this announcement can feasibly be selected if the current IsSelected() one disappears. */
88
96
bool IsSelectable () const
89
97
{
90
- return m_state == State::CANDIDATE_READY || m_state == State::CANDIDATE_BEST;
98
+ return GetState () == State::CANDIDATE_READY || GetState () == State::CANDIDATE_BEST;
91
99
}
92
100
93
101
/* * Construct a new announcement from scratch, initially in CANDIDATE_DELAYED state. */
94
102
Announcement (const GenTxid& gtxid, NodeId peer, bool preferred, std::chrono::microseconds reqtime,
95
103
SequenceNumber sequence) :
96
104
m_txhash (gtxid.GetHash()), m_time(reqtime), m_peer(peer), m_sequence(sequence), m_preferred(preferred),
97
- m_is_wtxid (gtxid.IsWtxid()), m_state(State::CANDIDATE_DELAYED) {}
105
+ m_is_wtxid (gtxid.IsWtxid()), m_state(static_cast < uint8_t >( State::CANDIDATE_DELAYED) ) {}
98
106
};
99
107
100
108
// ! Type alias for priorities.
@@ -143,7 +151,7 @@ struct ByPeerViewExtractor
143
151
using result_type = ByPeerView;
144
152
result_type operator ()(const Announcement& ann) const
145
153
{
146
- return ByPeerView{ann.m_peer , ann.m_state == State::CANDIDATE_BEST, ann.m_txhash };
154
+ return ByPeerView{ann.m_peer , ann.GetState () == State::CANDIDATE_BEST, ann.m_txhash };
147
155
}
148
156
};
149
157
@@ -166,8 +174,8 @@ class ByTxHashViewExtractor {
166
174
using result_type = ByTxHashView;
167
175
result_type operator ()(const Announcement& ann) const
168
176
{
169
- const Priority prio = (ann.m_state == State::CANDIDATE_READY) ? m_computer (ann) : 0 ;
170
- return ByTxHashView{ann.m_txhash , ann.m_state , prio};
177
+ const Priority prio = (ann.GetState () == State::CANDIDATE_READY) ? m_computer (ann) : 0 ;
178
+ return ByTxHashView{ann.m_txhash , ann.GetState () , prio};
171
179
}
172
180
};
173
181
@@ -261,8 +269,8 @@ std::unordered_map<NodeId, PeerInfo> RecomputePeerInfo(const Index& index)
261
269
for (const Announcement& ann : index) {
262
270
PeerInfo& info = ret[ann.m_peer ];
263
271
++info.m_total ;
264
- info.m_requested += (ann.m_state == State::REQUESTED);
265
- info.m_completed += (ann.m_state == State::COMPLETED);
272
+ info.m_requested += (ann.GetState () == State::REQUESTED);
273
+ info.m_completed += (ann.GetState () == State::COMPLETED);
266
274
}
267
275
return ret;
268
276
}
@@ -274,15 +282,15 @@ std::map<uint256, TxHashInfo> ComputeTxHashInfo(const Index& index, const Priori
274
282
for (const Announcement& ann : index) {
275
283
TxHashInfo& info = ret[ann.m_txhash ];
276
284
// Classify how many announcements of each state we have for this txhash.
277
- info.m_candidate_delayed += (ann.m_state == State::CANDIDATE_DELAYED);
278
- info.m_candidate_ready += (ann.m_state == State::CANDIDATE_READY);
279
- info.m_candidate_best += (ann.m_state == State::CANDIDATE_BEST);
280
- info.m_requested += (ann.m_state == State::REQUESTED);
285
+ info.m_candidate_delayed += (ann.GetState () == State::CANDIDATE_DELAYED);
286
+ info.m_candidate_ready += (ann.GetState () == State::CANDIDATE_READY);
287
+ info.m_candidate_best += (ann.GetState () == State::CANDIDATE_BEST);
288
+ info.m_requested += (ann.GetState () == State::REQUESTED);
281
289
// And track the priority of the best CANDIDATE_READY/CANDIDATE_BEST announcements.
282
- if (ann.m_state == State::CANDIDATE_BEST) {
290
+ if (ann.GetState () == State::CANDIDATE_BEST) {
283
291
info.m_priority_candidate_best = computer (ann);
284
292
}
285
- if (ann.m_state == State::CANDIDATE_READY) {
293
+ if (ann.GetState () == State::CANDIDATE_READY) {
286
294
info.m_priority_best_candidate_ready = std::max (info.m_priority_best_candidate_ready , computer (ann));
287
295
}
288
296
// Also keep track of which peers this txhash has an announcement for (so we can detect duplicates).
@@ -369,8 +377,8 @@ class TxRequestTracker::Impl {
369
377
Iter<Tag> Erase (Iter<Tag> it)
370
378
{
371
379
auto peerit = m_peerinfo.find (it->m_peer );
372
- peerit->second .m_completed -= it->m_state == State::COMPLETED;
373
- peerit->second .m_requested -= it->m_state == State::REQUESTED;
380
+ peerit->second .m_completed -= it->GetState () == State::COMPLETED;
381
+ peerit->second .m_requested -= it->GetState () == State::REQUESTED;
374
382
if (--peerit->second .m_total == 0 ) m_peerinfo.erase (peerit);
375
383
return m_index.get <Tag>().erase (it);
376
384
}
@@ -380,11 +388,11 @@ class TxRequestTracker::Impl {
380
388
void Modify (Iter<Tag> it, Modifier modifier)
381
389
{
382
390
auto peerit = m_peerinfo.find (it->m_peer );
383
- peerit->second .m_completed -= it->m_state == State::COMPLETED;
384
- peerit->second .m_requested -= it->m_state == State::REQUESTED;
391
+ peerit->second .m_completed -= it->GetState () == State::COMPLETED;
392
+ peerit->second .m_requested -= it->GetState () == State::REQUESTED;
385
393
m_index.get <Tag>().modify (it, std::move (modifier));
386
- peerit->second .m_completed += it->m_state == State::COMPLETED;
387
- peerit->second .m_requested += it->m_state == State::REQUESTED;
394
+ peerit->second .m_completed += it->GetState () == State::COMPLETED;
395
+ peerit->second .m_requested += it->GetState () == State::REQUESTED;
388
396
}
389
397
390
398
// ! Convert a CANDIDATE_DELAYED announcement into a CANDIDATE_READY. If this makes it the new best
@@ -393,26 +401,26 @@ class TxRequestTracker::Impl {
393
401
void PromoteCandidateReady (Iter<ByTxHash> it)
394
402
{
395
403
assert (it != m_index.get <ByTxHash>().end ());
396
- assert (it->m_state == State::CANDIDATE_DELAYED);
404
+ assert (it->GetState () == State::CANDIDATE_DELAYED);
397
405
// Convert CANDIDATE_DELAYED to CANDIDATE_READY first.
398
- Modify<ByTxHash>(it, [](Announcement& ann){ ann.m_state = State::CANDIDATE_READY; });
406
+ Modify<ByTxHash>(it, [](Announcement& ann){ ann.SetState ( State::CANDIDATE_READY) ; });
399
407
// The following code relies on the fact that the ByTxHash is sorted by txhash, and then by state (first
400
408
// _DELAYED, then _READY, then _BEST/REQUESTED). Within the _READY announcements, the best one (highest
401
409
// priority) comes last. Thus, if an existing _BEST exists for the same txhash that this announcement may
402
410
// be preferred over, it must immediately follow the newly created _READY.
403
411
auto it_next = std::next (it);
404
412
if (it_next == m_index.get <ByTxHash>().end () || it_next->m_txhash != it->m_txhash ||
405
- it_next->m_state == State::COMPLETED) {
413
+ it_next->GetState () == State::COMPLETED) {
406
414
// This is the new best CANDIDATE_READY, and there is no IsSelected() announcement for this txhash
407
415
// already.
408
- Modify<ByTxHash>(it, [](Announcement& ann){ ann.m_state = State::CANDIDATE_BEST; });
409
- } else if (it_next->m_state == State::CANDIDATE_BEST) {
416
+ Modify<ByTxHash>(it, [](Announcement& ann){ ann.SetState ( State::CANDIDATE_BEST) ; });
417
+ } else if (it_next->GetState () == State::CANDIDATE_BEST) {
410
418
Priority priority_old = m_computer (*it_next);
411
419
Priority priority_new = m_computer (*it);
412
420
if (priority_new > priority_old) {
413
421
// There is a CANDIDATE_BEST announcement already, but this one is better.
414
- Modify<ByTxHash>(it_next, [](Announcement& ann){ ann.m_state = State::CANDIDATE_READY; });
415
- Modify<ByTxHash>(it, [](Announcement& ann){ ann.m_state = State::CANDIDATE_BEST; });
422
+ Modify<ByTxHash>(it_next, [](Announcement& ann){ ann.SetState ( State::CANDIDATE_READY) ; });
423
+ Modify<ByTxHash>(it, [](Announcement& ann){ ann.SetState ( State::CANDIDATE_BEST) ; });
416
424
}
417
425
}
418
426
}
@@ -427,27 +435,27 @@ class TxRequestTracker::Impl {
427
435
auto it_prev = std::prev (it);
428
436
// The next best CANDIDATE_READY, if any, immediately precedes the REQUESTED or CANDIDATE_BEST
429
437
// announcement in the ByTxHash index.
430
- if (it_prev->m_txhash == it->m_txhash && it_prev->m_state == State::CANDIDATE_READY) {
438
+ if (it_prev->m_txhash == it->m_txhash && it_prev->GetState () == State::CANDIDATE_READY) {
431
439
// If one such CANDIDATE_READY exists (for this txhash), convert it to CANDIDATE_BEST.
432
- Modify<ByTxHash>(it_prev, [](Announcement& ann){ ann.m_state = State::CANDIDATE_BEST; });
440
+ Modify<ByTxHash>(it_prev, [](Announcement& ann){ ann.SetState ( State::CANDIDATE_BEST) ; });
433
441
}
434
442
}
435
- Modify<ByTxHash>(it, [new_state](Announcement& ann){ ann.m_state = new_state; });
443
+ Modify<ByTxHash>(it, [new_state](Announcement& ann){ ann.SetState ( new_state) ; });
436
444
}
437
445
438
446
// ! Check if 'it' is the only announcement for a given txhash that isn't COMPLETED.
439
447
bool IsOnlyNonCompleted (Iter<ByTxHash> it)
440
448
{
441
449
assert (it != m_index.get <ByTxHash>().end ());
442
- assert (it->m_state != State::COMPLETED); // Not allowed to call this on COMPLETED announcements.
450
+ assert (it->GetState () != State::COMPLETED); // Not allowed to call this on COMPLETED announcements.
443
451
444
452
// This announcement has a predecessor that belongs to the same txhash. Due to ordering, and the
445
453
// fact that 'it' is not COMPLETED, its predecessor cannot be COMPLETED here.
446
454
if (it != m_index.get <ByTxHash>().begin () && std::prev (it)->m_txhash == it->m_txhash ) return false ;
447
455
448
456
// This announcement has a successor that belongs to the same txhash, and is not COMPLETED.
449
457
if (std::next (it) != m_index.get <ByTxHash>().end () && std::next (it)->m_txhash == it->m_txhash &&
450
- std::next (it)->m_state != State::COMPLETED) return false ;
458
+ std::next (it)->GetState () != State::COMPLETED) return false ;
451
459
452
460
return true ;
453
461
}
@@ -460,7 +468,7 @@ class TxRequestTracker::Impl {
460
468
assert (it != m_index.get <ByTxHash>().end ());
461
469
462
470
// Nothing to be done if it's already COMPLETED.
463
- if (it->m_state == State::COMPLETED) return true ;
471
+ if (it->GetState () == State::COMPLETED) return true ;
464
472
465
473
if (IsOnlyNonCompleted (it)) {
466
474
// This is the last non-COMPLETED announcement for this txhash. Delete all.
@@ -490,9 +498,9 @@ class TxRequestTracker::Impl {
490
498
// and convert them to CANDIDATE_READY and COMPLETED respectively.
491
499
while (!m_index.empty ()) {
492
500
auto it = m_index.get <ByTime>().begin ();
493
- if (it->m_state == State::CANDIDATE_DELAYED && it->m_time <= now) {
501
+ if (it->GetState () == State::CANDIDATE_DELAYED && it->m_time <= now) {
494
502
PromoteCandidateReady (m_index.project <ByTxHash>(it));
495
- } else if (it->m_state == State::REQUESTED && it->m_time <= now) {
503
+ } else if (it->GetState () == State::REQUESTED && it->m_time <= now) {
496
504
if (expired) expired->emplace_back (it->m_peer , ToGenTxid (*it));
497
505
MakeCompleted (m_index.project <ByTxHash>(it));
498
506
} else {
@@ -596,7 +604,7 @@ class TxRequestTracker::Impl {
596
604
std::vector<const Announcement*> selected;
597
605
auto it_peer = m_index.get <ByPeer>().lower_bound (ByPeerView{peer, true , uint256::ZERO});
598
606
while (it_peer != m_index.get <ByPeer>().end () && it_peer->m_peer == peer &&
599
- it_peer->m_state == State::CANDIDATE_BEST) {
607
+ it_peer->GetState () == State::CANDIDATE_BEST) {
600
608
selected.emplace_back (&*it_peer);
601
609
++it_peer;
602
610
}
@@ -625,8 +633,8 @@ class TxRequestTracker::Impl {
625
633
// returned by GetRequestable always correspond to CANDIDATE_BEST announcements).
626
634
627
635
it = m_index.get <ByPeer>().find (ByPeerView{peer, false , txhash});
628
- if (it == m_index.get <ByPeer>().end () || (it->m_state != State::CANDIDATE_DELAYED &&
629
- it->m_state != State::CANDIDATE_READY)) {
636
+ if (it == m_index.get <ByPeer>().end () || (it->GetState () != State::CANDIDATE_DELAYED &&
637
+ it->GetState () != State::CANDIDATE_READY)) {
630
638
// There is no CANDIDATE announcement tracked for this peer, so we have nothing to do. Either this
631
639
// txhash wasn't tracked at all (and the caller should have called ReceivedInv), or it was already
632
640
// requested and/or completed for other reasons and this is just a superfluous RequestedTx call.
@@ -638,24 +646,24 @@ class TxRequestTracker::Impl {
638
646
// other CANDIDATE_BEST or REQUESTED can exist.
639
647
auto it_old = m_index.get <ByTxHash>().lower_bound (ByTxHashView{txhash, State::CANDIDATE_BEST, 0 });
640
648
if (it_old != m_index.get <ByTxHash>().end () && it_old->m_txhash == txhash) {
641
- if (it_old->m_state == State::CANDIDATE_BEST) {
649
+ if (it_old->GetState () == State::CANDIDATE_BEST) {
642
650
// The data structure's invariants require that there can be at most one CANDIDATE_BEST or one
643
651
// REQUESTED announcement per txhash (but not both simultaneously), so we have to convert any
644
652
// existing CANDIDATE_BEST to another CANDIDATE_* when constructing another REQUESTED.
645
653
// It doesn't matter whether we pick CANDIDATE_READY or _DELAYED here, as SetTimePoint()
646
654
// will correct it at GetRequestable() time. If time only goes forward, it will always be
647
655
// _READY, so pick that to avoid extra work in SetTimePoint().
648
- Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.m_state = State::CANDIDATE_READY; });
649
- } else if (it_old->m_state == State::REQUESTED) {
656
+ Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.SetState ( State::CANDIDATE_READY) ; });
657
+ } else if (it_old->GetState () == State::REQUESTED) {
650
658
// As we're no longer waiting for a response to the previous REQUESTED announcement, convert it
651
659
// to COMPLETED. This also helps guaranteeing progress.
652
- Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.m_state = State::COMPLETED; });
660
+ Modify<ByTxHash>(it_old, [](Announcement& ann) { ann.SetState ( State::COMPLETED) ; });
653
661
}
654
662
}
655
663
}
656
664
657
665
Modify<ByPeer>(it, [expiry](Announcement& ann) {
658
- ann.m_state = State::REQUESTED;
666
+ ann.SetState ( State::REQUESTED) ;
659
667
ann.m_time = expiry;
660
668
});
661
669
}
0 commit comments