0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-12-27 07:54:05 +01:00
construct/include/ircd/ios.h

274 lines
6.5 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.
#pragma once
#define HAVE_IRCD_IOS_H
// Boost headers are not exposed to our users unless explicitly included by a
// definition file. Other libircd headers may extend this namespace with more
// forward declarations.
/// Forward declarations for boost::asio because it is not included here.
namespace boost::asio
{
struct executor;
struct io_context;
template<class function>
void asio_handler_invoke(function&, ...);
}
namespace ircd
{
namespace asio = boost::asio; ///< Alias so that asio:: can be used.
extern const info::versions boost_version_api, boost_version_abi;
}
namespace ircd::ios
{
struct handler;
struct descriptor;
template<class function> struct handle;
IRCD_OVERLOAD(synchronous)
struct dispatch;
struct defer;
struct post;
extern std::thread::id main_thread_id;
extern asio::executor user;
extern asio::executor main;
const string_view &name(const descriptor &);
const string_view &name(const handler &);
bool available() noexcept;
const uint64_t &epoch() noexcept;
void forked_parent();
void forked_child();
void forking();
void init(asio::executor &&);
}
namespace ircd
{
using ios::dispatch;
using ios::defer;
using ios::post;
}
struct ircd::ios::dispatch
{
dispatch(descriptor &, std::function<void ()>);
dispatch(descriptor &, synchronous_t, const std::function<void ()> &);
dispatch(descriptor &, synchronous_t);
dispatch(std::function<void ()>);
dispatch(synchronous_t, const std::function<void ()> &);
};
struct ircd::ios::defer
{
defer(descriptor &, std::function<void ()>);
defer(descriptor &, synchronous_t, const std::function<void ()> &);
defer(descriptor &, synchronous_t);
defer(std::function<void ()>);
defer(synchronous_t, const std::function<void ()> &);
};
struct ircd::ios::post
{
post(descriptor &, std::function<void ()>);
post(descriptor &, synchronous_t, const std::function<void ()> &);
post(descriptor &, synchronous_t);
post(std::function<void ()>);
post(synchronous_t, const std::function<void ()> &);
};
struct ircd::ios::descriptor
:instance_list<descriptor>
{
struct stats;
static uint64_t ids;
static void *default_allocator(handler &, const size_t &);
static void default_deallocator(handler &, void *const &, const size_t &) noexcept;
string_view name;
uint64_t id {++ids};
std::unique_ptr<struct stats> stats;
std::function<void *(handler &, const size_t &)> allocator;
std::function<void (handler &, void *const &, const size_t &)> deallocator;
std::vector<std::array<uint64_t, 2>> history; // epoch, cycles
uint8_t history_pos {0};
bool continuation {false};
descriptor(const string_view &name,
const decltype(allocator) & = default_allocator,
const decltype(deallocator) & = default_deallocator,
const bool &continuation = false);
descriptor(descriptor &&) = delete;
descriptor(const descriptor &) = delete;
~descriptor() noexcept;
};
struct ircd::ios::descriptor::stats
{
uint64_t queued {0};
uint64_t calls {0};
uint64_t faults {0};
uint64_t allocs {0};
uint64_t alloc_bytes{0};
uint64_t frees {0};
uint64_t free_bytes{0};
uint64_t slice_total {0};
uint64_t slice_last {0};
uint64_t latency_total {0};
uint64_t latency_last {0};
stats &operator+=(const stats &) &;
stats();
~stats() noexcept;
};
struct ircd::ios::handler
{
static thread_local handler *current;
static thread_local uint64_t epoch;
static void *allocate(handler *const &, const size_t &);
static void deallocate(handler *const &, void *const &, const size_t &) noexcept;
static bool continuation(handler *const &) noexcept;
static void enter(handler *const &) noexcept;
static void leave(handler *const &) noexcept;
static bool fault(handler *const &) noexcept;
ios::descriptor *descriptor {nullptr};
uint64_t ts {0}; // last tsc sample; for profiling each phase
};
template<class function>
struct ircd::ios::handle
:handler
{
function f;
template<class... args>
void operator()(args&&... a) const;
handle(ios::descriptor &, function);
};
namespace ircd::ios
{
template<class function>
void asio_handler_deallocate(void *, size_t, handle<function> *);
template<class function>
void *asio_handler_allocate(size_t, handle<function> *);
template<class function>
bool asio_handler_is_continuation(handle<function> *);
template<class callable,
class function>
void asio_handler_invoke(callable &f, handle<function> *);
}
template<class function>
ircd::ios::handle<function>::handle(ios::descriptor &d,
function f)
:handler{&d, prof::cycles()}
,f{std::move(f)}
{
assert(d.stats);
d.stats->queued++;
}
template<class function>
template<class... args>
void
ircd::ios::handle<function>::operator()(args&&... a)
const
{
assert(descriptor && descriptor->stats);
assert(descriptor->stats->queued > 0);
descriptor->stats->queued--;
f(std::forward<args>(a)...);
}
template<class callable,
class function>
void
ircd::ios::asio_handler_invoke(callable &f,
handle<function> *const h)
try
{
handler::enter(h);
boost::asio::asio_handler_invoke(f, &h);
handler::leave(h);
}
catch(...)
{
if(handler::fault(h))
handler::leave(h);
else
throw;
}
template<class function>
bool
ircd::ios::asio_handler_is_continuation(handle<function> *const h)
{
return handler::continuation(h);
}
template<class function>
void *
__attribute__((malloc, returns_nonnull, warn_unused_result, alloc_size(1)))
ircd::ios::asio_handler_allocate(size_t size,
handle<function> *const h)
{
return handler::allocate(h, size);
}
template<class function>
void
ircd::ios::asio_handler_deallocate(void *const ptr,
size_t size,
handle<function> *const h)
{
handler::deallocate(h, ptr, size);
}
inline const ircd::string_view &
ircd::ios::name(const handler &handler)
{
assert(handler.descriptor);
return name(*handler.descriptor);
}
inline const ircd::string_view &
ircd::ios::name(const descriptor &descriptor)
{
return descriptor.name;
}
inline const uint64_t &
__attribute__((always_inline))
ircd::ios::epoch()
noexcept
{
return handler::epoch;
}