diff --git a/src/benchmark/benchmark_ipc_channel.cpp b/src/benchmark/benchmark_ipc_channel.cpp index 4232739e1..e81f55cf4 100644 --- a/src/benchmark/benchmark_ipc_channel.cpp +++ b/src/benchmark/benchmark_ipc_channel.cpp @@ -23,53 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc., TEST_CASE("benchmark_ipc_channel") { - // same as in test_threading.cpp (TODO: remove duplication) - struct IPCChannelResourcesSingleProcess final : public IPCChannelResources - { - void cleanupLast() noexcept override - { - delete data.shared; -#ifdef IPC_CHANNEL_IMPLEMENTATION_WIN32 - CloseHandle(data.sem_b); - CloseHandle(data.sem_a); -#endif - } - - void cleanupNotLast() noexcept override - { - // nothing to do (i.e. no unmapping needed) - } - - ~IPCChannelResourcesSingleProcess() override { cleanup(); } - }; - - auto resource_data = [] { - auto shared = new IPCChannelShared(); - -#ifdef IPC_CHANNEL_IMPLEMENTATION_WIN32 - HANDLE sem_a = CreateSemaphoreA(nullptr, 0, 1, nullptr); - REQUIRE(sem_a != INVALID_HANDLE_VALUE); - - HANDLE sem_b = CreateSemaphoreA(nullptr, 0, 1, nullptr); - REQUIRE(sem_b != INVALID_HANDLE_VALUE); - - return IPCChannelResources::Data{shared, sem_a, sem_b}; -#else - return IPCChannelResources::Data{shared}; -#endif - }(); - - auto resources_first = std::make_unique(); - resources_first->setFirst(resource_data); - - IPCChannelEnd end_a = IPCChannelEnd::makeA(std::move(resources_first)); - - // echos back messages. stops if "" is sent - std::thread thread_b([=] { - auto resources_second = std::make_unique(); - resources_second->setSecond(resource_data); - IPCChannelEnd end_b = IPCChannelEnd::makeB(std::move(resources_second)); - + auto end_a_thread_b_p = make_test_ipc_channel([](IPCChannelEnd end_b) { + // echos back messages. stops if "" is sent for (;;) { end_b.recv(); end_b.send(end_b.getRecvData(), end_b.getRecvSize()); @@ -77,6 +32,9 @@ TEST_CASE("benchmark_ipc_channel") break; } }); + // Can't use structured bindings before C++20, because of lamda captures below. + auto end_a = std::move(end_a_thread_b_p.first); + auto thread_b = std::move(end_a_thread_b_p.second); BENCHMARK("simple_call_1", i) { char buf[16] = {}; diff --git a/src/threading/ipc_channel.cpp b/src/threading/ipc_channel.cpp index 922dde34c..bf319fa38 100644 --- a/src/threading/ipc_channel.cpp +++ b/src/threading/ipc_channel.cpp @@ -353,3 +353,36 @@ bool IPCChannelEnd::recvWithTimeout(int timeout_ms) noexcept } return true; } + +std::pair make_test_ipc_channel( + const std::function &fun) +{ + auto resource_data = [] { + auto shared = new IPCChannelShared(); + +#ifdef IPC_CHANNEL_IMPLEMENTATION_WIN32 + HANDLE sem_a = CreateSemaphoreA(nullptr, 0, 1, nullptr); + HANDLE sem_b = CreateSemaphoreA(nullptr, 0, 1, nullptr); + FATAL_ERROR_IF(!sem_a || !sem_b, "CreateSemaphoreA failed"); + + return IPCChannelResources::Data{shared, sem_a, sem_b}; +#else + return IPCChannelResources::Data{shared}; +#endif + }(); + + auto resources_first = std::make_unique(); + resources_first->setFirst(resource_data); + + IPCChannelEnd end_a = IPCChannelEnd::makeA(std::move(resources_first)); + + std::thread thread_b([=] { + auto resources_second = std::make_unique(); + resources_second->setSecond(resource_data); + IPCChannelEnd end_b = IPCChannelEnd::makeB(std::move(resources_second)); + + fun(std::move(end_b)); + }); + + return {std::move(end_a), std::move(thread_b)}; +} diff --git a/src/threading/ipc_channel.h b/src/threading/ipc_channel.h index c701947e4..b83eda8ca 100644 --- a/src/threading/ipc_channel.h +++ b/src/threading/ipc_channel.h @@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include +#include #if defined(_WIN32) #define IPC_CHANNEL_IMPLEMENTATION_WIN32 @@ -256,3 +258,29 @@ private: // (this buffer only grows) std::vector m_large_recv = std::vector(IPC_CHANNEL_MSG_SIZE); }; + +// For testing purposes +struct IPCChannelResourcesSingleProcess final : public IPCChannelResources +{ + void cleanupLast() noexcept override + { + delete data.shared; +#ifdef IPC_CHANNEL_IMPLEMENTATION_WIN32 + CloseHandle(data.sem_b); + CloseHandle(data.sem_a); +#endif + } + + void cleanupNotLast() noexcept override + { + // nothing to do (i.e. no unmapping needed) + } + + ~IPCChannelResourcesSingleProcess() override { cleanup(); } +}; + +// For testing +// Returns one end and a thread holding the other end. The thread will execute +// fun, and pass it the other end. +std::pair make_test_ipc_channel( + const std::function &fun); diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp index 2d82e4353..e1b5e25c4 100644 --- a/src/unittest/test_threading.cpp +++ b/src/unittest/test_threading.cpp @@ -236,52 +236,8 @@ void TestThreading::testTLS() void TestThreading::testIPCChannel() { - struct IPCChannelResourcesSingleProcess final : public IPCChannelResources - { - void cleanupLast() noexcept override - { - delete data.shared; -#ifdef IPC_CHANNEL_IMPLEMENTATION_WIN32 - CloseHandle(data.sem_b); - CloseHandle(data.sem_a); -#endif - } - - void cleanupNotLast() noexcept override - { - // nothing to do (i.e. no unmapping needed) - } - - ~IPCChannelResourcesSingleProcess() override { cleanup(); } - }; - - auto resource_data = [] { - auto shared = new IPCChannelShared(); - -#ifdef IPC_CHANNEL_IMPLEMENTATION_WIN32 - HANDLE sem_a = CreateSemaphoreA(nullptr, 0, 1, nullptr); - UASSERT(sem_a != INVALID_HANDLE_VALUE); - - HANDLE sem_b = CreateSemaphoreA(nullptr, 0, 1, nullptr); - UASSERT(sem_b != INVALID_HANDLE_VALUE); - - return IPCChannelResources::Data{shared, sem_a, sem_b}; -#else - return IPCChannelResources::Data{shared}; -#endif - }(); - - auto resources_first = std::make_unique(); - resources_first->setFirst(resource_data); - - IPCChannelEnd end_a = IPCChannelEnd::makeA(std::move(resources_first)); - - // echos back messages. stops if "" is sent - std::thread thread_b([=] { - auto resources_second = std::make_unique(); - resources_second->setSecond(resource_data); - IPCChannelEnd end_b = IPCChannelEnd::makeB(std::move(resources_second)); - + auto [end_a, thread_b] = make_test_ipc_channel([](IPCChannelEnd end_b) { + // echos back messages. stops if "" is sent for (;;) { UASSERT(end_b.recvWithTimeout(-1)); UASSERT(end_b.sendWithTimeout(end_b.getRecvData(), end_b.getRecvSize(), -1));