// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // 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 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 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); dispatch(descriptor &, synchronous_t, const std::function &); dispatch(descriptor &, synchronous_t); dispatch(std::function); dispatch(synchronous_t, const std::function &); }; struct ircd::ios::defer { defer(descriptor &, std::function); defer(descriptor &, synchronous_t, const std::function &); defer(descriptor &, synchronous_t); defer(std::function); defer(synchronous_t, const std::function &); }; struct ircd::ios::post { post(descriptor &, std::function); post(descriptor &, synchronous_t, const std::function &); post(descriptor &, synchronous_t); post(std::function); post(synchronous_t, const std::function &); }; struct ircd::ios::descriptor :instance_list { 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 stats; std::function allocator; std::function deallocator; std::vector> 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 struct ircd::ios::handle :handler { function f; template void operator()(args&&... a) const; handle(ios::descriptor &, function); }; namespace ircd::ios { template void asio_handler_deallocate(void *, size_t, handle *); template void *asio_handler_allocate(size_t, handle *); template bool asio_handler_is_continuation(handle *); template void asio_handler_invoke(callable &f, handle *); } template ircd::ios::handle::handle(ios::descriptor &d, function f) :handler{&d, prof::cycles()} ,f{std::move(f)} { assert(d.stats); d.stats->queued++; } template template void ircd::ios::handle::operator()(args&&... a) const { assert(descriptor && descriptor->stats); assert(descriptor->stats->queued > 0); descriptor->stats->queued--; f(std::forward(a)...); } template void ircd::ios::asio_handler_invoke(callable &f, handle *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 bool ircd::ios::asio_handler_is_continuation(handle *const h) { return handler::continuation(h); } template void * __attribute__((malloc, returns_nonnull, warn_unused_result, alloc_size(1))) ircd::ios::asio_handler_allocate(size_t size, handle *const h) { return handler::allocate(h, size); } template void ircd::ios::asio_handler_deallocate(void *const ptr, size_t size, handle *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; }