9#include <mp/proxy.capnp.h>
14#include <capnp/capability.h>
15#include <capnp/common.h>
17#include <condition_variable>
21#include <kj/async-io.h>
22#include <kj/async-prelude.h>
25#include <kj/exception.h>
26#include <kj/function.h>
35#include <sys/socket.h>
48 KJ_LOG(ERROR,
"Uncaught exception in daemonized task.", exception);
67 loop_lock->assert_locked(loop->m_mutex);
68 assert(loop->m_num_refs > 0);
69 loop->m_num_refs -= 1;
71 loop->m_cv.notify_all();
72 int post_fd{loop->m_post_fd};
75 KJ_SYSCALL(write(post_fd, &buffer, 1));
79 if (relock) loop_lock->lock();
95 m_canceler.cancel(
"Interrupted by disconnect");
198 m_async_fns->emplace_back(std::move(fn));
210 KJ_SYSCALL(socketpair(AF_UNIX, SOCK_STREAM, 0, fds));
219 KJ_ASSERT(m_post_fn ==
nullptr);
220 KJ_ASSERT(!m_async_fns);
223 KJ_ASSERT(m_num_refs == 0);
239 m_async_fns.emplace();
242 kj::Own<kj::AsyncIoStream> wait_stream{
243 m_io_context.lowLevelProvider->wrapSocketFd(
m_wait_fd, kj::LowLevelAsyncIoProvider::TAKE_OWNERSHIP)};
247 const size_t read_bytes = wait_stream->read(&buffer, 0, 1).wait(
m_io_context.waitScope);
248 if (read_bytes != 1)
throw std::logic_error(
"EventLoop wait_stream closed unexpectedly");
255 MP_LOG(*
this,
Log::Error) <<
"EventLoop: m_post_fn threw: " << kj::str(*exception).cStr();
267 MP_LOG(*
this,
Log::Info) <<
"EventLoop::loop done, cancelling event listeners.";
270 wait_stream =
nullptr;
271 KJ_SYSCALL(::close(post_fd));
292 KJ_SYSCALL(write(post_fd, &buffer, 1));
303 }
else if (!m_async_fns->empty()) {
306 while (m_async_fns) {
307 if (!m_async_fns->empty()) {
309 const std::function<void()> fn = std::move(m_async_fns->front());
310 m_async_fns->pop_front();
326 return m_num_refs == 0 && m_async_fns->empty();
336 std::tie(thread, inserted) = threads.ref.try_emplace(connection);
339 thread->second.emplace(make_thread(), connection,
false);
340 thread->second->m_disconnect_cb = connection->
addSyncCleanup([threads, thread] {
349 thread->second->m_disconnect_cb.reset();
353 threads.ref.erase(thread);
356 return {thread, inserted};
392 std::unique_ptr<Waiter> waiter;
407 waiter->m_cv.notify_all();
415 return kj::READY_NOW;
423 throw std::runtime_error(
"makePool called on connection with existing pool");
426 const uint32_t count = context.getParams().getCount();
427 for (uint32_t i = 0; i < count; ++i) {
428 const std::string thread_name =
"pool/" + std::to_string(i);
429 std::promise<ThreadContext*> thread_context;
430 std::thread thread([&loop, &thread_context, thread_name]() {
437 auto thread_server = kj::heap<ProxyServer<Thread>>(
m_connection, *thread_context.get_future().get(), std::move(thread));
440 return kj::READY_NOW;
447 const std::string from = context.getParams().getName();
448 std::promise<ThreadContext*> thread_context;
449 std::thread thread([&loop, &thread_context, from]() {
459 auto thread_server = kj::heap<ProxyServer<Thread>>(
m_connection, *thread_context.get_future().get(), std::move(thread));
460 auto thread_client =
m_connection.m_threads.add(kj::mv(thread_server));
461 context.getResults().setResult(kj::mv(thread_client));
462 return kj::READY_NOW;
CleanupIt addSyncCleanup(std::function< void()> fn)
CleanupList m_sync_cleanup_fns
std::optional<::capnp::RpcSystem<::capnp::rpc::twoparty::VatId > > m_rpc_system
void removeSyncCleanup(CleanupIt it)
kj::AsyncIoContext m_io_context
Capnp IO context.
void startAsyncThread() MP_REQUIRES(m_mutex)
std::condition_variable m_cv
EventLoop(const char *exe_name, LogFn log_fn, void *context=nullptr)
Construct event loop object with default logging options.
void addAsyncCleanup(std::function< void()> fn)
bool done() const MP_REQUIRES(m_mutex)
Check if loop should exit.
int m_post_fd
Pipe write handle used to wake up the event loop thread.
LogOptions m_log_opts
Logging options.
std::unique_ptr< kj::TaskSet > m_task_set
Capnp list of pending promises.
void * m_context
External context pointer.
int m_wait_fd
Pipe read handle used to wake up the event loop thread.
std::function< void()> testing_hook_makethread
Hook called when ProxyServer<ThreadMap>::makeThread() is called.
LoggingErrorHandler m_error_handler
Capnp error handler. Needs to outlive m_task_set.
std::thread m_async_thread
std::function< void()> testing_hook_makethread_created
std::thread::id m_thread_id
ID of the event loop thread.
void post(kj::Function< void()> fn)
EventLoopRef(EventLoop &loop, Lock *lock=nullptr)
void reset(bool relock=false)
std::unique_lock< std::mutex > m_lock
void taskFailed(kj::Exception &&exception) override
Functions to serialize / deserialize common bitcoin types.
void Unlock(Lock &lock, Callback &&callback)
kj::StringPtr KJ_STRINGIFY(Log flags)
std::list< std::function< void()> > CleanupList
ConnThreads::iterator ConnThread
std::string ThreadName(const char *exe_name)
Format current thread name as "{exe_name}-{$pid}/{thread_name}-{$tid}".
std::atomic< int > server_reqs
typename CleanupList::iterator CleanupIt
std::tuple< ConnThread, bool > SetThread(GuardedRef< ConnThreads > threads, Connection *connection, const std::function< Thread::Client()> &make_thread)
ThreadContext g_thread_context
Log
Log flags. Update stringify function if changed!
std::string LongThreadName(const char *exe_name)
ProxyClient(const ProxyClient &)=delete
std::optional< CleanupIt > m_disconnect_cb
ProxyContext(Connection *connection)
ProxyServer(Connection &connection, ThreadContext &thread_context, std::thread &&thread)
ThreadContext & m_thread_context
ProxyServer(Connection &connection)
Connection & m_connection
Convenient wrapper around std::variant<T*, T>.
std::unique_ptr< Waiter > waiter
std::string thread_name
Identifying string for debug.