@@ -134,9 +134,14 @@ Connection::~Connection()
134
134
// callbacks. In the clean shutdown case both lists will be empty.
135
135
Lock lock{m_loop->m_mutex };
136
136
while (!m_sync_cleanup_fns.empty ()) {
137
- CleanupList fn;
138
- fn.splice (fn.begin (), m_sync_cleanup_fns, m_sync_cleanup_fns.begin ());
139
- Unlock (lock, fn.front ());
137
+ // Call the first function in the connection cleanup list. Before
138
+ // calling it, move it into a temporary variable so outside classes
139
+ // which registered for disconnect callbacks can check if the
140
+ // disconnection function is null and know if it's about to be called.
141
+ auto it{m_sync_cleanup_fns.begin ()};
142
+ std::function<void ()> fn = std::move (*it);
143
+ Unlock (lock, fn);
144
+ m_sync_cleanup_fns.erase (it);
140
145
}
141
146
}
142
147
@@ -157,6 +162,12 @@ CleanupIt Connection::addSyncCleanup(std::function<void()> fn)
157
162
void Connection::removeSyncCleanup (CleanupIt it)
158
163
{
159
164
const Lock lock (m_loop->m_mutex );
165
+ removeSyncCleanup (it, lock);
166
+ }
167
+
168
+ void Connection::removeSyncCleanup (CleanupIt it, const Lock& lock)
169
+ {
170
+ lock.assert_locked (m_loop->m_mutex );
160
171
m_sync_cleanup_fns.erase (it);
161
172
}
162
173
@@ -313,7 +324,7 @@ std::tuple<ConnThread, bool> SetThread(ConnThreads& threads, std::mutex& mutex,
313
324
thread = threads.emplace (
314
325
std::piecewise_construct, std::forward_as_tuple (connection),
315
326
std::forward_as_tuple (make_thread (), connection, /* destroy_connection= */ false )).first ;
316
- thread->second .setDisconnectCallback ([&threads, &mutex, thread] {
327
+ thread->second .setDisconnectCallback ([&threads, &mutex, thread, destroyed = thread-> second . m_destroyed ] {
317
328
// Note: it is safe to use the `thread` iterator in this cleanup
318
329
// function, because the iterator would only be invalid if the map entry
319
330
// was removed, and if the map entry is removed the ProxyClient<Thread>
@@ -324,6 +335,7 @@ std::tuple<ConnThread, bool> SetThread(ConnThreads& threads, std::mutex& mutex,
324
335
// try to unregister this callback after connection is destroyed.
325
336
// Remove connection pointer about to be destroyed from the map
326
337
const std::unique_lock<std::mutex> lock (mutex);
338
+ if (*destroyed) return ;
327
339
thread->second .m_disconnect_cb .reset ();
328
340
threads.erase (thread);
329
341
});
@@ -332,12 +344,18 @@ std::tuple<ConnThread, bool> SetThread(ConnThreads& threads, std::mutex& mutex,
332
344
333
345
ProxyClient<Thread>::~ProxyClient ()
334
346
{
347
+ // Waiter::m_mutex is already held here so it is safe to access
348
+ // m_disconnect_cb EventLoop::mutex needs to be locked to access
349
+ // **m_disconnect_cb, which Connection destructor will set to null before it
350
+ // invokes the callback.
351
+ const Lock lock (m_context.loop ->m_mutex );
335
352
// If thread is being destroyed before connection is destroyed, remove the
336
353
// cleanup callback that was registered to handle the connection being
337
354
// destroyed before the thread being destroyed.
338
- if (m_disconnect_cb) {
339
- m_context.connection ->removeSyncCleanup (*m_disconnect_cb);
355
+ if (m_disconnect_cb && **m_disconnect_cb ) {
356
+ m_context.connection ->removeSyncCleanup (*m_disconnect_cb, lock );
340
357
}
358
+ *m_destroyed = true ;
341
359
}
342
360
343
361
void ProxyClient<Thread>::setDisconnectCallback(const std::function<void ()>& fn)
0 commit comments