diff --git a/src/benchmark/CMakeLists.txt b/src/benchmark/CMakeLists.txt index e8150848a..2ecab382d 100644 --- a/src/benchmark/CMakeLists.txt +++ b/src/benchmark/CMakeLists.txt @@ -1,10 +1,11 @@ set (BENCHMARK_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_activeobjectmgr.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_ipc_channel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_lighting.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapblock.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapmodify.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_sha.cpp PARENT_SCOPE) diff --git a/src/benchmark/benchmark_ipc_channel.cpp b/src/benchmark/benchmark_ipc_channel.cpp new file mode 100644 index 000000000..86d8fde6e --- /dev/null +++ b/src/benchmark/benchmark_ipc_channel.cpp @@ -0,0 +1,95 @@ +/* +Minetest +Copyright (C) 2024 DS + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "benchmark_setup.h" +#include "threading/ipc_channel.h" +#include + +#include "log.h" + +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)); + + for (;;) { + end_b.recv(); + end_b.send(end_b.getRecvData(), end_b.getRecvSize()); + if (end_b.getRecvSize() == 0) + break; + } + }); + + BENCHMARK("simple_call", i) { + char buf[16] = {}; + buf[i & 0xf] = i; + end_a.exchange(buf, 16); + return reinterpret_cast(end_a.getRecvData())[i & 0xf]; + }; + + // stop thread_b + end_a.exchange(nullptr, 0); + REQUIRE(end_a.getRecvSize() == 0); + + thread_b.join(); +} diff --git a/src/threading/ipc_channel.cpp b/src/threading/ipc_channel.cpp index e2ed45637..0cb2ce29b 100644 --- a/src/threading/ipc_channel.cpp +++ b/src/threading/ipc_channel.cpp @@ -242,7 +242,8 @@ IPCChannelEnd IPCChannelEnd::makeB(std::unique_ptr resource void IPCChannelEnd::sendSmall(const void *data, size_t size) noexcept { write_once(&m_out->size, size); - memcpy(m_out->data, data, size); + if (size != 0) + memcpy(m_out->data, data, size); #if defined(IPC_CHANNEL_IMPLEMENTATION_WIN32) post(m_sem_out); #else @@ -275,7 +276,8 @@ bool IPCChannelEnd::sendLarge(const void *data, size_t size, int timeout_ms) noe size -= IPC_CHANNEL_MSG_SIZE; data = (u8 *)data + IPC_CHANNEL_MSG_SIZE; } while (size > IPC_CHANNEL_MSG_SIZE); - memcpy(m_out->data, data, size); + if (size != 0) + memcpy(m_out->data, data, size); #if defined(IPC_CHANNEL_IMPLEMENTATION_WIN32) post(m_sem_out); #else diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp index 0918df732..2d82e4353 100644 --- a/src/unittest/test_threading.cpp +++ b/src/unittest/test_threading.cpp @@ -276,6 +276,7 @@ void TestThreading::testIPCChannel() 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); @@ -297,6 +298,7 @@ void TestThreading::testIPCChannel() UASSERTEQ(int, ((const char *)end_a.getRecvData())[i - 1], 123); } + // stop thread_b UASSERT(end_a.exchangeWithTimeout(buf, 0, -1)); UASSERTEQ(int, end_a.getRecvSize(), 0);