0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-04 21:08:57 +01:00
construct/ircd/ios.cc

544 lines
9.9 KiB
C++

// Matrix Construct
//
// Copyright (C) Matrix Construct Developers, Authors & Contributors
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
/// Logging facility
decltype(ircd::ios::log)
ircd::ios::log
{
"ios"
};
/// "main" thread for IRCd; the one the main context landed on.
decltype(ircd::ios::main_thread_id)
ircd::ios::main_thread_id;
/// The embedder/executable's (library user) asio::executor provided on init.
decltype(ircd::ios::user)
ircd::ios::user;
/// Our library-specific/isolate executor.
decltype(ircd::ios::main)
ircd::ios::main;
decltype(ircd::boost_version_api)
ircd::boost_version_api
{
"boost", info::versions::API, BOOST_VERSION,
{
BOOST_VERSION / 100000,
BOOST_VERSION / 100 % 1000,
BOOST_VERSION % 100,
}
};
decltype(ircd::boost_version_abi)
ircd::boost_version_abi
{
"boost", info::versions::ABI //TODO: get this
};
//
// init
//
void
ircd::ios::init(asio::executor &&user)
{
// Sample the ID of this thread. Since this is the first transfer of
// control to libircd after static initialization we have nothing to
// consider a main thread yet. We need something set for many assertions
// to pass.
main_thread_id = std::this_thread::get_id();
// Set a reference to the user's ios_service
ios::user = std::move(user);
// (simple passthru for now)
ios::main = ios::user;
}
//
// descriptor
//
template<>
decltype(ircd::util::instance_list<ircd::ios::descriptor>::allocator)
ircd::util::instance_list<ircd::ios::descriptor>::allocator
{};
template<>
decltype(ircd::util::instance_list<ircd::ios::descriptor>::list)
ircd::util::instance_list<ircd::ios::descriptor>::list
{
allocator
};
decltype(ircd::ios::descriptor::ids)
ircd::ios::descriptor::ids;
//
// descriptor::descriptor
//
ircd::ios::descriptor::descriptor(const string_view &name,
const decltype(allocator) &allocator,
const decltype(deallocator) &deallocator,
const bool &continuation)
noexcept
:name
{
name
}
,stats
{
std::make_unique<struct stats>()
}
,allocator
{
allocator?: default_allocator
}
,deallocator
{
deallocator?: default_deallocator
}
,history
(
256, {0}
)
,history_pos
{
0
}
,continuation
{
continuation
}
{
}
ircd::ios::descriptor::~descriptor()
noexcept
{
assert(!stats || stats->queued == 0);
assert(!stats || stats->allocs == stats->frees);
assert(!stats || stats->alloc_bytes == stats->free_bytes);
}
//
// descriptor::stats
//
ircd::ios::descriptor::stats::stats()
{
}
ircd::ios::descriptor::stats::~stats()
noexcept
{
}
struct ircd::ios::descriptor::stats &
ircd::ios::descriptor::stats::operator+=(const stats &o)
&
{
queued += o.queued;
calls += o.calls;
faults += o.faults;
allocs += o.allocs;
alloc_bytes += o.alloc_bytes;
frees += o.frees;
free_bytes += o.free_bytes;
slice_total += o.slice_total;
slice_last += o.slice_last;
latency_total += o.latency_total;
latency_last += o.latency_last;
return *this;
}
//
// handler
//
decltype(ircd::ios::handler::current)
thread_local
ircd::ios::handler::current;
decltype(ircd::ios::handler::epoch)
thread_local
ircd::ios::handler::epoch;
[[gnu::cold]]
bool
ircd::ios::handler::fault(handler *const &handler)
noexcept
{
assert(handler && handler->descriptor);
auto &descriptor(*handler->descriptor);
assert(descriptor.stats);
auto &stats(*descriptor.stats);
++stats.faults;
bool ret
{
false
};
if constexpr(profile_logging)
log::logf
{
log, log::level::DEBUG,
"FAULT %5u %-20s [%11lu] faults[%9lu] q:%-4lu",
descriptor.id,
trunc(descriptor.name, 20),
stats.calls,
stats.faults,
stats.queued,
};
// Our API sez if this function returns true, caller is responsible for
// calling leave(), otherwise they must not call leave().
if(likely(!ret))
leave(handler);
return ret;
}
[[gnu::hot]]
void
ircd::ios::handler::leave(handler *const &handler)
noexcept
{
assert(handler && handler->descriptor);
auto &descriptor(*handler->descriptor);
assert(descriptor.stats);
auto &stats(*descriptor.stats);
const auto slice_start
{
std::exchange(handler->ts, cycles())
};
// NOTE: will fail without constant_tsc;
// NOTE: may fail without nonstop_tsc after OS suspend mode
assert(handler->ts >= slice_start);
stats.slice_last = handler->ts - slice_start;
stats.slice_total += stats.slice_last;
if constexpr(profile_history)
{
assert(descriptor.history_pos < descriptor.history.size());
descriptor.history[descriptor.history_pos][0] = handler::epoch;
descriptor.history[descriptor.history_pos][1] = stats.slice_last;
++descriptor.history_pos;
}
if constexpr(profile_logging)
log::logf
{
log, log::level::DEBUG,
"LEAVE %5u %-20s [%11lu] cycles[%9lu] q:%-4lu",
descriptor.id,
trunc(descriptor.name, 20),
stats.calls,
stats.slice_last,
stats.queued,
};
assert(handler::current == handler);
handler::current = nullptr;
}
[[gnu::hot]]
void
ircd::ios::handler::enter(handler *const &handler)
noexcept
{
assert(!handler::current);
handler::current = handler;
++handler::epoch;
assert(handler && handler->descriptor);
auto &descriptor(*handler->descriptor);
assert(descriptor.stats);
auto &stats(*descriptor.stats);
++stats.calls;
const auto last_ts
{
std::exchange(handler->ts, cycles())
};
stats.latency_last = handler->ts - last_ts;
stats.latency_total += stats.latency_last;
if constexpr(profile_logging)
log::logf
{
log, log::level::DEBUG,
"ENTER %5u %-20s [%11lu] latent[%9lu] q:%-4lu",
descriptor.id,
trunc(descriptor.name, 20),
stats.calls,
stats.latency_last,
stats.queued,
};
}
//
// ios.h
//
namespace ircd::ios
{
extern descriptor post_desc;
extern descriptor defer_desc;
extern descriptor dispatch_desc;
}
void
ircd::ios::forking()
{
#if BOOST_VERSION >= 107000
get().context().notify_fork(asio::execution_context::fork_prepare);
#else
get().notify_fork(asio::execution_context::fork_prepare);
#endif
}
void
ircd::ios::forked_child()
{
#if BOOST_VERSION >= 107000
get().context().notify_fork(asio::execution_context::fork_child);
#else
get().notify_fork(asio::execution_context::fork_child);
#endif
}
void
ircd::ios::forked_parent()
{
#if BOOST_VERSION >= 107000
get().context().notify_fork(asio::execution_context::fork_parent);
#else
get().notify_fork(asio::execution_context::fork_parent);
#endif
}
//
// dispatch
//
decltype(ircd::ios::dispatch_desc)
ircd::ios::dispatch_desc
{
"ircd::ios dispatch"
};
[[gnu::hot]]
ircd::ios::dispatch::dispatch(std::function<void ()> function)
:dispatch
{
dispatch_desc, std::move(function)
}
{
}
ircd::ios::dispatch::dispatch(synchronous_t,
const std::function<void ()> &function)
:dispatch
{
dispatch_desc, synchronous, std::move(function)
}
{
}
ircd::ios::dispatch::dispatch(descriptor &descriptor,
synchronous_t)
:dispatch
{
dispatch_desc, synchronous, []
{
}
}
{
}
ircd::ios::dispatch::dispatch(descriptor &descriptor,
synchronous_t,
const std::function<void ()> &function)
{
const ctx::uninterruptible ui;
assert(function);
ctx::continuation
{
continuation::false_predicate, continuation::noop_interruptor, [&descriptor, &function]
(auto &yield)
{
dispatch(descriptor, [&function]
{
function();
});
}
};
}
[[gnu::hot]]
ircd::ios::dispatch::dispatch(descriptor &descriptor,
std::function<void ()> function)
{
boost::asio::dispatch(get(), handle(descriptor, std::move(function)));
}
//
// defer
//
decltype(ircd::ios::defer_desc)
ircd::ios::defer_desc
{
"ircd::ios defer",
descriptor::default_allocator,
descriptor::default_deallocator,
true, // continuation
};
[[gnu::hot]]
ircd::ios::defer::defer(std::function<void ()> function)
:defer
{
defer_desc, std::move(function)
}
{
}
ircd::ios::defer::defer(synchronous_t,
const std::function<void ()> &function)
:defer
{
defer_desc, synchronous, function
}
{
}
ircd::ios::defer::defer(descriptor &descriptor,
synchronous_t)
:defer
{
defer_desc, synchronous, []
{
}
}
{
}
ircd::ios::defer::defer(descriptor &descriptor,
synchronous_t,
const std::function<void ()> &function)
{
const ctx::uninterruptible::nothrow ui;
ctx::latch latch(1);
defer(descriptor, [&function, &latch]
{
const unwind uw{[&latch]
{
latch.count_down();
}};
function();
});
latch.wait();
}
[[gnu::hot]]
ircd::ios::defer::defer(descriptor &descriptor,
std::function<void ()> function)
{
boost::asio::defer(get(), handle(descriptor, std::move(function)));
}
//
// post
//
decltype(ircd::ios::post_desc)
ircd::ios::post_desc
{
"ircd::ios post"
};
[[gnu::hot]]
ircd::ios::post::post(std::function<void ()> function)
:post
{
post_desc, std::move(function)
}
{
}
ircd::ios::post::post(synchronous_t,
const std::function<void ()> &function)
:post
{
post_desc, synchronous, function
}
{
}
ircd::ios::post::post(descriptor &descriptor,
synchronous_t)
:post
{
descriptor, synchronous, []
{
}
}
{
}
ircd::ios::post::post(descriptor &descriptor,
synchronous_t,
const std::function<void ()> &function)
{
const ctx::uninterruptible::nothrow ui;
ctx::latch latch(1);
post(descriptor, [&function, &latch]
{
const unwind uw{[&latch]
{
latch.count_down();
}};
function();
});
latch.wait();
}
[[gnu::hot]]
ircd::ios::post::post(descriptor &descriptor,
std::function<void ()> function)
{
boost::asio::post(get(), handle(descriptor, std::move(function)));
}
bool
ircd::ios::available()
noexcept
{
return bool(main);
}