16
16
#include < unordered_map>
17
17
#include < utility>
18
18
19
- #include < boost/signals2/signal.hpp>
20
-
21
- struct ValidationInterfaceConnections {
22
- boost::signals2::scoped_connection UpdatedBlockTip;
23
- boost::signals2::scoped_connection TransactionAddedToMempool;
24
- boost::signals2::scoped_connection BlockConnected;
25
- boost::signals2::scoped_connection BlockDisconnected;
26
- boost::signals2::scoped_connection TransactionRemovedFromMempool;
27
- boost::signals2::scoped_connection ChainStateFlushed;
28
- boost::signals2::scoped_connection BlockChecked;
29
- boost::signals2::scoped_connection NewPoWValidBlock;
30
- };
31
-
19
+ // ! The MainSignalsInstance manages a list of shared_ptr<CValidationInterface>
20
+ // ! callbacks.
21
+ // !
22
+ // ! A std::unordered_map is used to track what callbacks are currently
23
+ // ! registered, and a std::list is to used to store the callbacks that are
24
+ // ! currently registered as well as any callbacks that are just unregistered
25
+ // ! and about to be deleted when they are done executing.
32
26
struct MainSignalsInstance {
33
- boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload )> UpdatedBlockTip;
34
- boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
35
- boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex)> BlockConnected;
36
- boost::signals2::signal<void (const std::shared_ptr<const CBlock>&, const CBlockIndex* pindex)> BlockDisconnected;
37
- boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
38
- boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
39
- boost::signals2::signal<void (const CBlock&, const BlockValidationState&)> BlockChecked;
40
- boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
41
-
27
+ private:
28
+ Mutex m_mutex;
29
+ // ! List entries consist of a callback pointer and reference count. The
30
+ // ! count is equal to the number of current executions of that entry, plus 1
31
+ // ! if it's registered. It cannot be 0 because that would imply it is
32
+ // ! unregistered and also not being executed (so shouldn't exist).
33
+ struct ListEntry { std::shared_ptr<CValidationInterface> callbacks; int count = 1 ; };
34
+ std::list<ListEntry> m_list GUARDED_BY (m_mutex);
35
+ std::unordered_map<CValidationInterface*, std::list<ListEntry>::iterator> m_map GUARDED_BY (m_mutex);
36
+
37
+ public:
42
38
// We are not allowed to assume the scheduler only runs in one thread,
43
39
// but must ensure all callbacks happen in-order, so we end up creating
44
40
// our own queue here :(
45
41
SingleThreadedSchedulerClient m_schedulerClient;
46
- std::unordered_map<CValidationInterface*, ValidationInterfaceConnections> m_connMainSignals;
47
42
48
43
explicit MainSignalsInstance (CScheduler *pscheduler) : m_schedulerClient(pscheduler) {}
44
+
45
+ void Register (std::shared_ptr<CValidationInterface> callbacks)
46
+ {
47
+ LOCK (m_mutex);
48
+ auto inserted = m_map.emplace (callbacks.get (), m_list.end ());
49
+ if (inserted.second ) inserted.first ->second = m_list.emplace (m_list.end ());
50
+ inserted.first ->second ->callbacks = std::move (callbacks);
51
+ }
52
+
53
+ void Unregister (CValidationInterface* callbacks)
54
+ {
55
+ LOCK (m_mutex);
56
+ auto it = m_map.find (callbacks);
57
+ if (it != m_map.end ()) {
58
+ if (!--it->second ->count ) m_list.erase (it->second );
59
+ m_map.erase (it);
60
+ }
61
+ }
62
+
63
+ // ! Clear unregisters every previously registered callback, erasing every
64
+ // ! map entry. After this call, the list may still contain callbacks that
65
+ // ! are currently executing, but it will be cleared when they are done
66
+ // ! executing.
67
+ void Clear ()
68
+ {
69
+ LOCK (m_mutex);
70
+ for (auto it = m_list.begin (); it != m_list.end ();) {
71
+ it = --it->count ? std::next (it) : m_list.erase (it);
72
+ }
73
+ m_map.clear ();
74
+ }
75
+
76
+ template <typename F> void Iterate (F&& f)
77
+ {
78
+ WAIT_LOCK (m_mutex, lock);
79
+ for (auto it = m_list.begin (); it != m_list.end ();) {
80
+ ++it->count ;
81
+ {
82
+ REVERSE_LOCK (lock);
83
+ f (*it->callbacks );
84
+ }
85
+ it = --it->count ? std::next (it) : m_list.erase (it);
86
+ }
87
+ }
49
88
};
50
89
51
90
static CMainSignals g_signals;
@@ -78,15 +117,7 @@ CMainSignals& GetMainSignals()
78
117
void RegisterSharedValidationInterface (std::shared_ptr<CValidationInterface> pwalletIn) {
79
118
// Each connection captures pwalletIn to ensure that each callback is
80
119
// executed before pwalletIn is destroyed. For more details see #18338.
81
- ValidationInterfaceConnections& conns = g_signals.m_internals ->m_connMainSignals [pwalletIn.get ()];
82
- conns.UpdatedBlockTip = g_signals.m_internals ->UpdatedBlockTip .connect (std::bind (&CValidationInterface::UpdatedBlockTip, pwalletIn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
83
- conns.TransactionAddedToMempool = g_signals.m_internals ->TransactionAddedToMempool .connect (std::bind (&CValidationInterface::TransactionAddedToMempool, pwalletIn, std::placeholders::_1));
84
- conns.BlockConnected = g_signals.m_internals ->BlockConnected .connect (std::bind (&CValidationInterface::BlockConnected, pwalletIn, std::placeholders::_1, std::placeholders::_2));
85
- conns.BlockDisconnected = g_signals.m_internals ->BlockDisconnected .connect (std::bind (&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1, std::placeholders::_2));
86
- conns.TransactionRemovedFromMempool = g_signals.m_internals ->TransactionRemovedFromMempool .connect (std::bind (&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, std::placeholders::_1));
87
- conns.ChainStateFlushed = g_signals.m_internals ->ChainStateFlushed .connect (std::bind (&CValidationInterface::ChainStateFlushed, pwalletIn, std::placeholders::_1));
88
- conns.BlockChecked = g_signals.m_internals ->BlockChecked .connect (std::bind (&CValidationInterface::BlockChecked, pwalletIn, std::placeholders::_1, std::placeholders::_2));
89
- conns.NewPoWValidBlock = g_signals.m_internals ->NewPoWValidBlock .connect (std::bind (&CValidationInterface::NewPoWValidBlock, pwalletIn, std::placeholders::_1, std::placeholders::_2));
120
+ g_signals.m_internals ->Register (std::move (pwalletIn));
90
121
}
91
122
92
123
void RegisterValidationInterface (CValidationInterface* callbacks)
@@ -103,15 +134,15 @@ void UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> c
103
134
104
135
void UnregisterValidationInterface (CValidationInterface* pwalletIn) {
105
136
if (g_signals.m_internals ) {
106
- g_signals.m_internals ->m_connMainSignals . erase (pwalletIn);
137
+ g_signals.m_internals ->Unregister (pwalletIn);
107
138
}
108
139
}
109
140
110
141
void UnregisterAllValidationInterfaces () {
111
142
if (!g_signals.m_internals ) {
112
143
return ;
113
144
}
114
- g_signals.m_internals ->m_connMainSignals . clear ();
145
+ g_signals.m_internals ->Clear ();
115
146
}
116
147
117
148
void CallFunctionInValidationInterfaceQueue (std::function<void ()> func) {
@@ -151,7 +182,7 @@ void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockInd
151
182
// in the same critical section where the chain is updated
152
183
153
184
auto event = [pindexNew, pindexFork, fInitialDownload , this ] {
154
- m_internals->UpdatedBlockTip (pindexNew, pindexFork, fInitialDownload );
185
+ m_internals->Iterate ([&](CValidationInterface& callbacks) { callbacks. UpdatedBlockTip (pindexNew, pindexFork, fInitialDownload ); } );
155
186
};
156
187
ENQUEUE_AND_LOG_EVENT (event, " %s: new block hash=%s fork block hash=%s (in IBD=%s)" , __func__,
157
188
pindexNew->GetBlockHash ().ToString (),
@@ -161,7 +192,7 @@ void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockInd
161
192
162
193
void CMainSignals::TransactionAddedToMempool (const CTransactionRef &ptx) {
163
194
auto event = [ptx, this ] {
164
- m_internals->TransactionAddedToMempool (ptx);
195
+ m_internals->Iterate ([&](CValidationInterface& callbacks) { callbacks. TransactionAddedToMempool (ptx); } );
165
196
};
166
197
ENQUEUE_AND_LOG_EVENT (event, " %s: txid=%s wtxid=%s" , __func__,
167
198
ptx->GetHash ().ToString (),
@@ -170,7 +201,7 @@ void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
170
201
171
202
void CMainSignals::TransactionRemovedFromMempool (const CTransactionRef &ptx) {
172
203
auto event = [ptx, this ] {
173
- m_internals->TransactionRemovedFromMempool (ptx);
204
+ m_internals->Iterate ([&](CValidationInterface& callbacks) { callbacks. TransactionRemovedFromMempool (ptx); } );
174
205
};
175
206
ENQUEUE_AND_LOG_EVENT (event, " %s: txid=%s wtxid=%s" , __func__,
176
207
ptx->GetHash ().ToString (),
@@ -179,7 +210,7 @@ void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
179
210
180
211
void CMainSignals::BlockConnected (const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) {
181
212
auto event = [pblock, pindex, this ] {
182
- m_internals->BlockConnected (pblock, pindex);
213
+ m_internals->Iterate ([&](CValidationInterface& callbacks) { callbacks. BlockConnected (pblock, pindex); } );
183
214
};
184
215
ENQUEUE_AND_LOG_EVENT (event, " %s: block hash=%s block height=%d" , __func__,
185
216
pblock->GetHash ().ToString (),
@@ -189,7 +220,7 @@ void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, c
189
220
void CMainSignals::BlockDisconnected (const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
190
221
{
191
222
auto event = [pblock, pindex, this ] {
192
- m_internals->BlockDisconnected (pblock, pindex);
223
+ m_internals->Iterate ([&](CValidationInterface& callbacks) { callbacks. BlockDisconnected (pblock, pindex); } );
193
224
};
194
225
ENQUEUE_AND_LOG_EVENT (event, " %s: block hash=%s block height=%d" , __func__,
195
226
pblock->GetHash ().ToString (),
@@ -198,7 +229,7 @@ void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock
198
229
199
230
void CMainSignals::ChainStateFlushed (const CBlockLocator &locator) {
200
231
auto event = [locator, this ] {
201
- m_internals->ChainStateFlushed (locator);
232
+ m_internals->Iterate ([&](CValidationInterface& callbacks) { callbacks. ChainStateFlushed (locator); } );
202
233
};
203
234
ENQUEUE_AND_LOG_EVENT (event, " %s: block hash=%s" , __func__,
204
235
locator.IsNull () ? " null" : locator.vHave .front ().ToString ());
@@ -207,10 +238,10 @@ void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
207
238
void CMainSignals::BlockChecked (const CBlock& block, const BlockValidationState& state) {
208
239
LOG_EVENT (" %s: block hash=%s state=%s" , __func__,
209
240
block.GetHash ().ToString (), state.ToString ());
210
- m_internals->BlockChecked (block, state);
241
+ m_internals->Iterate ([&](CValidationInterface& callbacks) { callbacks. BlockChecked (block, state); } );
211
242
}
212
243
213
244
void CMainSignals::NewPoWValidBlock (const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
214
245
LOG_EVENT (" %s: block hash=%s" , __func__, block->GetHash ().ToString ());
215
- m_internals->NewPoWValidBlock (pindex, block);
246
+ m_internals->Iterate ([&](CValidationInterface& callbacks) { callbacks. NewPoWValidBlock (pindex, block); } );
216
247
}
0 commit comments