mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-19 23:03:45 +01:00
Squashed 'src/ipc/libmultiprocess/' changes from 13424cf2ecc1..a4f929696490
a4f929696490 Merge bitcoin-core/libmultiprocess#224: doc: fix typos f4344ae87da0 Merge bitcoin-core/libmultiprocess#222: test, ci: Fix threadsanitizer errors in mptest 1434642b3804 doc: fix typos 73d22ba2e930 test: Fix tsan race in thread busy test b74e1bba014d ci: Use tsan-instrumented cap'n proto in sanitizers job c332774409ad test: Fix failing exception check in new thread busy test ca3c05d56709 test: Use KJ_LOG instead of std::cout for logging 7eb1da120ab6 ci: Use tsan-instrumented libcxx in sanitizers job ec86e4336e98 Merge bitcoin-core/libmultiprocess#220: Add log levels and advertise them to users via logging callback 515ce93ad349 Logging: Pass LogData struct to logging callback 213574ccc43d Logging: reclassify remaining log messages e4de0412b430 Logging: Break out expensive log messages and classify them as Trace 408874a78fdc Logging: Use new logging macros 67b092d835cd Logging: Disable logging if messsage level is less than the requested level d0a1ba7ebf21 Logging: add log levels to mirror Core's 463a8296d188 Logging: Disable moving or copying Logger 83a2e10c0b03 Logging: Add an EventLoop constructor to allow for user-specified log options 58cf47a7fc8c Merge bitcoin-core/libmultiprocess#221: test default PassField impl handles output parameters db03a663f514 Merge bitcoin-core/libmultiprocess#214: Fix crash on simultaneous IPC calls using the same thread afcc40b0f1e8 Merge bitcoin-core/libmultiprocess#213: util+doc: Clearer errors when attempting to run examples + polished docs 6db669628387 test In|Out parameter 29cf2ada75ea test default PassField impl handles output parameters 1238170f68e8 test: simultaneous IPC calls using same thread eb069ab75d83 Fix crash on simultaneous IPC calls using the same thread ec03a9639ab5 doc: Precision and typos 2b4348193551 doc: Where possible, remove links to ryanofsky/bitcoin/ 286fe469c9c9 util: Add helpful error message when failing to execute file 47d79db8a552 Merge bitcoin-core/libmultiprocess#201: bug: fix mptest hang, ProxyClient<Thread> deadlock in disconnect handler f15ae9c9b9fb Merge bitcoin-core/libmultiprocess#211: Add .gitignore 4a269b21b8c8 bug: fix ProxyClient<Thread> deadlock if disconnected as IPC call is returning 85df96482c49 Use try_emplace in SetThread instead of threads.find ca9b380ea91a Use std::optional in ConnThreads to allow shortening locks 9b0799113557 doc: describe ThreadContext struct and synchronization requirements d60db601ed9b proxy-io.h: add Waiter::m_mutex thread safety annotations 4e365b019a9f ci: Use -Wthread-safety not -Wthread-safety-analysis 15d7bafbb001 Add .gitignore fe1cd8c76131 Merge bitcoin-core/libmultiprocess#208: ci: Test minimum cmake version in olddeps job b713a0b7bfbc Merge bitcoin-core/libmultiprocess#207: ci: output CMake version in CI script 0f580397c913 ci: Test minimum cmake version in olddeps job d603dcc0eef0 ci: output CMake version in CI script git-subtree-dir: src/ipc/libmultiprocess git-subtree-split: a4f92969649018ca70f949a09148bccfeaecd99a
This commit is contained in:
@@ -13,6 +13,8 @@ $Proxy.includeTypes("mp/test/foo-types.h");
|
||||
|
||||
interface FooInterface $Proxy.wrap("mp::test::FooImplementation") {
|
||||
add @0 (a :Int32, b :Int32) -> (result :Int32);
|
||||
addOut @19 (a :Int32, b :Int32) -> (ret :Int32);
|
||||
addInOut @20 (x :Int32, sum :Int32) -> (sum :Int32);
|
||||
mapSize @1 (map :List(Pair(Text, Text))) -> (result :Int32);
|
||||
pass @2 (arg :FooStruct) -> (result :FooStruct);
|
||||
raise @3 (arg :FooStruct) -> (error :FooStruct $Proxy.exception("mp::test::FooStruct"));
|
||||
|
||||
@@ -62,6 +62,8 @@ class FooImplementation
|
||||
{
|
||||
public:
|
||||
int add(int a, int b) { return a + b; }
|
||||
void addOut(int a, int b, int& out) { out = a + b; }
|
||||
void addInOut(int x, int& sum) { sum += x; }
|
||||
int mapSize(const std::map<std::string, std::string>& map) { return map.size(); }
|
||||
FooStruct pass(FooStruct foo) { return foo; }
|
||||
void raise(FooStruct foo) { throw foo; }
|
||||
|
||||
@@ -5,26 +5,32 @@
|
||||
#include <mp/test/foo.capnp.h>
|
||||
#include <mp/test/foo.capnp.proxy.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <capnp/capability.h>
|
||||
#include <capnp/rpc.h>
|
||||
#include <condition_variable>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <kj/async.h>
|
||||
#include <kj/async-io.h>
|
||||
#include <kj/common.h>
|
||||
#include <kj/debug.h>
|
||||
#include <kj/exception.h>
|
||||
#include <kj/memory.h>
|
||||
#include <kj/string.h>
|
||||
#include <kj/test.h>
|
||||
#include <memory>
|
||||
#include <mp/proxy.h>
|
||||
#include <mp/proxy.capnp.h>
|
||||
#include <mp/proxy-io.h>
|
||||
#include <mp/util.h>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -60,9 +66,10 @@ public:
|
||||
|
||||
TestSetup(bool client_owns_connection = true)
|
||||
: thread{[&] {
|
||||
EventLoop loop("mptest", [](bool raise, const std::string& log) {
|
||||
std::cout << "LOG" << raise << ": " << log << "\n";
|
||||
if (raise) throw std::runtime_error(log);
|
||||
EventLoop loop("mptest", [](mp::LogMessage log) {
|
||||
// Info logs are not printed by default, but will be shown with `mptest --verbose`
|
||||
KJ_LOG(INFO, log.level, log.message);
|
||||
if (log.level == mp::Log::Raise) throw std::runtime_error(log.message);
|
||||
});
|
||||
auto pipe = loop.m_io_context.provider->newTwoWayPipe();
|
||||
|
||||
@@ -113,6 +120,11 @@ KJ_TEST("Call FooInterface methods")
|
||||
ProxyClient<messages::FooInterface>* foo = setup.client.get();
|
||||
|
||||
KJ_EXPECT(foo->add(1, 2) == 3);
|
||||
int ret;
|
||||
foo->addOut(3, 4, ret);
|
||||
KJ_EXPECT(ret == 7);
|
||||
foo->addInOut(3, ret);
|
||||
KJ_EXPECT(ret == 10);
|
||||
|
||||
FooStruct in;
|
||||
in.name = "name";
|
||||
@@ -297,5 +309,71 @@ KJ_TEST("Calling IPC method, disconnecting and blocking during the call")
|
||||
signal.set_value();
|
||||
}
|
||||
|
||||
KJ_TEST("Make simultaneous IPC calls to trigger 'thread busy' error")
|
||||
{
|
||||
TestSetup setup;
|
||||
ProxyClient<messages::FooInterface>* foo = setup.client.get();
|
||||
std::promise<void> signal;
|
||||
|
||||
foo->initThreadMap();
|
||||
// Use callFnAsync() to get the client to set up the request_thread
|
||||
// that will be used for the test.
|
||||
setup.server->m_impl->m_fn = [&] {};
|
||||
foo->callFnAsync();
|
||||
ThreadContext& tc{g_thread_context};
|
||||
Thread::Client *callback_thread, *request_thread;
|
||||
foo->m_context.loop->sync([&] {
|
||||
Lock lock(tc.waiter->m_mutex);
|
||||
callback_thread = &tc.callback_threads.at(foo->m_context.connection)->m_client;
|
||||
request_thread = &tc.request_threads.at(foo->m_context.connection)->m_client;
|
||||
});
|
||||
|
||||
setup.server->m_impl->m_fn = [&] {
|
||||
try
|
||||
{
|
||||
signal.get_future().get();
|
||||
}
|
||||
catch (const std::future_error& e)
|
||||
{
|
||||
KJ_EXPECT(e.code() == std::make_error_code(std::future_errc::future_already_retrieved));
|
||||
}
|
||||
};
|
||||
|
||||
auto client{foo->m_client};
|
||||
bool caught_thread_busy = false;
|
||||
// NOTE: '3' was chosen because it was the lowest number
|
||||
// of simultaneous calls required to reliably catch a "thread busy" error
|
||||
std::atomic<size_t> running{3};
|
||||
foo->m_context.loop->sync([&]
|
||||
{
|
||||
for (size_t i = 0; i < running; i++)
|
||||
{
|
||||
auto request{client.callFnAsyncRequest()};
|
||||
auto context{request.initContext()};
|
||||
context.setCallbackThread(*callback_thread);
|
||||
context.setThread(*request_thread);
|
||||
foo->m_context.loop->m_task_set->add(request.send().then(
|
||||
[&](auto&& results) {
|
||||
running -= 1;
|
||||
tc.waiter->m_cv.notify_all();
|
||||
},
|
||||
[&](kj::Exception&& e) {
|
||||
KJ_EXPECT(std::string_view{e.getDescription().cStr()} ==
|
||||
"remote exception: std::exception: thread busy");
|
||||
caught_thread_busy = true;
|
||||
running -= 1;
|
||||
signal.set_value();
|
||||
tc.waiter->m_cv.notify_all();
|
||||
}
|
||||
));
|
||||
}
|
||||
});
|
||||
{
|
||||
Lock lock(tc.waiter->m_mutex);
|
||||
tc.waiter->wait(lock, [&running] { return running == 0; });
|
||||
}
|
||||
KJ_EXPECT(caught_thread_busy);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace mp
|
||||
|
||||
Reference in New Issue
Block a user