Skip to content

Commit 7d08d83

Browse files
committed
refactor: Add CleanupRun function to dedup clean list code
Also rename "cleanup" variables to distinguish between cleanup iterators and cleanup callback functions.
1 parent 621a04a commit 7d08d83

File tree

4 files changed

+31
-29
lines changed

4 files changed

+31
-29
lines changed

include/mp/proxy-io.h

+11-18
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,16 @@ struct ProxyClient<Thread> : public ProxyClientBase<Thread, ::capnp::Void>
6363
ProxyClient(const ProxyClient&) = delete;
6464
~ProxyClient();
6565

66-
void setCleanup(std::function<void()> cleanup);
66+
void setCleanup(std::function<void()> fn);
6767

6868
//! Cleanup function to run when the connection is closed. If the Connection
6969
//! gets destroyed before this ProxyClient<Thread> object, this cleanup
7070
//! callback lets it destroy this object and remove its entry in the
7171
//! thread's request_threads or callback_threads map (after resetting
72-
//! m_cleanup so the destructor does not try to access it). But if this
72+
//! m_cleanup_it so the destructor does not try to access it). But if this
7373
//! object gets destroyed before the Connection, there's no need to run the
7474
//! cleanup function and the destructor will unregister it.
75-
std::optional<CleanupIt> m_cleanup;
75+
std::optional<CleanupIt> m_cleanup_it;
7676
};
7777

7878
template <>
@@ -379,7 +379,7 @@ ProxyClientBase<Interface, Impl>::ProxyClientBase(typename Interface::Client cli
379379
}
380380

381381
// Handler for the connection getting destroyed before this client object.
382-
auto cleanup = m_context.connection->addSyncCleanup([this]() {
382+
auto cleanup_it = m_context.connection->addSyncCleanup([this]() {
383383
// Release client capability by move-assigning to temporary.
384384
{
385385
typename Interface::Client(std::move(self().m_client));
@@ -402,11 +402,11 @@ ProxyClientBase<Interface, Impl>::ProxyClientBase(typename Interface::Client cli
402402
// The first case is handled here when m_context.connection is not null. The
403403
// second case is handled by the cleanup function, which sets m_context.connection to
404404
// null so nothing happens here.
405-
m_context.cleanup.emplace_front([this, destroy_connection, cleanup]{
405+
m_context.cleanup_fns.emplace_front([this, destroy_connection, cleanup_it]{
406406
if (m_context.connection) {
407407
// Remove cleanup callback so it doesn't run and try to access
408408
// this object after it's already destroyed.
409-
m_context.connection->removeSyncCleanup(cleanup);
409+
m_context.connection->removeSyncCleanup(cleanup_it);
410410

411411
// If the capnp interface defines a destroy method, call it to destroy
412412
// the remote object, waiting for it to be deleted server side. If the
@@ -437,9 +437,7 @@ ProxyClientBase<Interface, Impl>::ProxyClientBase(typename Interface::Client cli
437437
template <typename Interface, typename Impl>
438438
ProxyClientBase<Interface, Impl>::~ProxyClientBase() noexcept
439439
{
440-
for (auto& cleanup : m_context.cleanup) {
441-
cleanup();
442-
}
440+
CleanupRun(m_context.cleanup_fns);
443441
}
444442

445443
template <typename Interface, typename Impl>
@@ -476,14 +474,12 @@ ProxyServerBase<Interface, Impl>::~ProxyServerBase()
476474
// connection is broken). Probably some refactoring of the destructor
477475
// and invokeDestroy function is possible to make this cleaner and more
478476
// consistent.
479-
m_context.connection->addAsyncCleanup([impl=std::move(m_impl), c=std::move(m_context.cleanup)]() mutable {
477+
m_context.connection->addAsyncCleanup([impl=std::move(m_impl), fns=std::move(m_context.cleanup_fns)]() mutable {
480478
impl.reset();
481-
for (auto& cleanup : c) {
482-
cleanup();
483-
}
479+
CleanupRun(fns);
484480
});
485481
}
486-
assert(m_context.cleanup.size() == 0);
482+
assert(m_context.cleanup_fns.size() == 0);
487483
std::unique_lock<std::mutex> lock(m_context.connection->m_loop.m_mutex);
488484
m_context.connection->m_loop.removeClient(lock);
489485
}
@@ -509,10 +505,7 @@ template <typename Interface, typename Impl>
509505
void ProxyServerBase<Interface, Impl>::invokeDestroy()
510506
{
511507
m_impl.reset();
512-
for (auto& cleanup : m_context.cleanup) {
513-
cleanup();
514-
}
515-
m_context.cleanup.clear();
508+
CleanupRun(m_context.cleanup_fns);
516509
}
517510

518511
using ConnThreads = std::map<Connection*, ProxyClient<Thread>>;

include/mp/proxy.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,17 @@ struct ProxyType;
3939
using CleanupList = std::list<std::function<void()>>;
4040
using CleanupIt = typename CleanupList::iterator;
4141

42+
inline void CleanupRun(CleanupList& fns) {
43+
for (auto& fn : fns) {
44+
fn();
45+
}
46+
}
47+
4248
//! Context data associated with proxy client and server classes.
4349
struct ProxyContext
4450
{
4551
Connection* connection;
46-
std::list<std::function<void()>> cleanup;
52+
CleanupList cleanup_fns;
4753

4854
ProxyContext(Connection* connection) : connection(connection) {}
4955
};
@@ -147,7 +153,7 @@ struct ProxyServerBase : public virtual Interface_::Server
147153
//! state can be destroyed without blocking, because ProxyServer destructors are
148154
//! called from the EventLoop thread, and if they block, it could deadlock the
149155
//! program. One way to do avoid blocking is to clean up the state by pushing
150-
//! cleanup callbacks to the m_context.cleanup list, which run after the server
156+
//! cleanup callbacks to the m_context.cleanup_fns list, which run after the server
151157
//! m_impl object is destroyed on the same thread destroying it (which will
152158
//! either be an IPC worker thread if the ProxyServer is being explicitly
153159
//! destroyed by a client calling a destroy() method with a Context argument and

src/mp/proxy.cpp

+11-8
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,9 @@ std::tuple<ConnThread, bool> SetThread(ConnThreads& threads, std::mutex& mutex,
283283
// destructor unregisters the cleanup.
284284

285285
// Connection is being destroyed before thread client is, so reset
286-
// thread client m_cleanup member so thread client destructor does not
286+
// thread client m_cleanup_it member so thread client destructor does not
287287
// try unregister this callback after connection is destroyed.
288-
thread->second.m_cleanup.reset();
288+
thread->second.m_cleanup_it.reset();
289289
// Remove connection pointer about to be destroyed from the map
290290
std::unique_lock<std::mutex> lock(mutex);
291291
threads.erase(thread);
@@ -295,16 +295,19 @@ std::tuple<ConnThread, bool> SetThread(ConnThreads& threads, std::mutex& mutex,
295295

296296
ProxyClient<Thread>::~ProxyClient()
297297
{
298-
if (m_cleanup) {
299-
m_context.connection->removeSyncCleanup(*m_cleanup);
298+
// If thread is being destroyed before connection is destroyed, remove the
299+
// cleanup callback that was registered to handle the connection being
300+
// destroyed before the thread being destroyed.
301+
if (m_cleanup_it) {
302+
m_context.connection->removeSyncCleanup(*m_cleanup_it);
300303
}
301304
}
302305

303-
void ProxyClient<Thread>::setCleanup(std::function<void()> cleanup)
306+
void ProxyClient<Thread>::setCleanup(std::function<void()> fn)
304307
{
305-
assert(cleanup);
306-
assert(!m_cleanup);
307-
m_cleanup = m_context.connection->addSyncCleanup(cleanup);
308+
assert(fn);
309+
assert(!m_cleanup_it);
310+
m_cleanup_it = m_context.connection->addSyncCleanup(fn);
308311
}
309312

310313
ProxyServer<Thread>::ProxyServer(ThreadContext& thread_context, std::thread&& thread)

test/mp/test/test.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ KJ_TEST("Call FooInterface methods")
122122
thread.join();
123123

124124
bool destroyed = false;
125-
foo->m_context.cleanup.emplace_front([&destroyed]{ destroyed = true; });
125+
foo->m_context.cleanup_fns.emplace_front([&destroyed]{ destroyed = true; });
126126
foo.reset();
127127
KJ_EXPECT(destroyed);
128128
}

0 commit comments

Comments
 (0)