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

ircd::ctx: Split this_ctx devices into files; minor interface tweaks.

This commit is contained in:
Jason Volk 2018-11-11 14:13:37 -08:00
parent e90f1729f0
commit bbe676a392
9 changed files with 227 additions and 130 deletions

View file

@ -0,0 +1,37 @@
// 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_CTX_CRITICAL_ASSERTION_H
namespace ircd::ctx::this_ctx
{
struct critical_assertion; // Assert no yielding for a section
}
/// An instance of critical_assertion detects an attempt to context switch.
///
/// For when the developer specifically does not want any yielding in a
/// section or anywhere up the stack from it. This device does not prevent
/// a switch and may carry no meaning outside of debug-mode compilation. It is
/// good practice to use this device even when it appears obvious the
/// section's callgraph has no chance of yielding: code changes, and everything
/// up the graph can change without taking notice of your section.
///
class ircd::ctx::this_ctx::critical_assertion
{
#ifndef NDEBUG
bool theirs;
public:
critical_assertion();
~critical_assertion() noexcept;
#endif
};

View file

@ -0,0 +1,38 @@
// 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_CTX_CRITICAL_INDICATOR_H
namespace ircd::ctx::this_ctx
{
struct critical_indicator; // Indicates if yielding happened for a section
}
/// An instance of critical_indicator reports if context switching happened.
///
/// A critical_indicator remains true after construction until a context switch
/// has occurred. It then becomes false. This is not an assertion and is
/// available in optimized builds for real use. For example, a context may
/// want to recompute some value after a context switch and opportunistically
/// skip this effort when this indicator shows no switch occurred.
///
class ircd::ctx::this_ctx::critical_indicator
{
uint64_t state;
public:
uint64_t count() const { return yields(cur()) - state; }
operator bool() const { return yields(cur()) == state; }
critical_indicator()
:state{yields(cur())}
{}
};

View file

@ -55,9 +55,9 @@ namespace ircd::ctx
const int64_t &notes(const ctx &); // Peeks at internal semaphore count const int64_t &notes(const ctx &); // Peeks at internal semaphore count
const uint64_t &yields(const ctx &); // Context switching counter const uint64_t &yields(const ctx &); // Context switching counter
const ulong &cycles(const ctx &); // Accumulated tsc (not counting cur slice) const ulong &cycles(const ctx &); // Accumulated tsc (not counting cur slice)
bool interruptible(const ctx &); // Context can throw at interruption point bool interruptible(const ctx &) noexcept; // Context can throw at interruption point
bool interruption(const ctx &); // Context was marked for interruption bool interruption(const ctx &) noexcept; // Context was marked for interruption
bool termination(const ctx &); // Context was marked for termination bool termination(const ctx &) noexcept; // Context was marked for termination
bool finished(const ctx &); // Context function returned (or exception). bool finished(const ctx &); // Context function returned (or exception).
bool started(const ctx &); // Context was ever entered. bool started(const ctx &); // Context was ever entered.
bool running(const ctx &); // Context is the currently running ctx. bool running(const ctx &); // Context is the currently running ctx.
@ -73,6 +73,11 @@ namespace ircd::ctx
} }
#include "this_ctx.h" #include "this_ctx.h"
#include "stack_usage_assertion.h"
#include "critical_assertion.h"
#include "critical_indicator.h"
#include "exception_handler.h"
#include "uninterruptible.h"
#include "prof.h" #include "prof.h"
#include "list.h" #include "list.h"
#include "dock.h" #include "dock.h"

View file

@ -0,0 +1,38 @@
// 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_CTX_EXCEPTION_HANDLER_H
namespace ircd::ctx::this_ctx
{
struct exception_handler;
}
/// An instance of exception_handler must be present to allow a context
/// switch inside a catch block. This is due to ABI limitations that stack
/// exceptions with thread-local assumptions and don't expect catch blocks
/// on the same thread to interleave when we switch the stack.
///
/// We first increment the refcount for the caught exception so it remains
/// intuitively accessible for the rest of the catch block. Then the presence
/// of this object makes the ABI believe the catch block has ended.
///
/// The exception cannot then be rethrown. DO NOT RETHROW THE EXCEPTION.
///
struct ircd::ctx::this_ctx::exception_handler
:std::exception_ptr
{
exception_handler() noexcept;
exception_handler(exception_handler &&) = delete;
exception_handler(const exception_handler &) = delete;
exception_handler &operator=(exception_handler &&) = delete;
exception_handler &operator=(const exception_handler &) = delete;
};

View file

@ -39,6 +39,9 @@ namespace ircd::ctx::prof
void mark(const event &); void mark(const event &);
} }
/// Profiling events for marking. These are currently used internally at the
/// appropriate point to mark(): the user of ircd::ctx has no reason to mark()
/// these events; this interface is not quite developed for general use yet.
enum class ircd::ctx::prof::event enum class ircd::ctx::prof::event
{ {
SPAWN, // Context spawn requested SPAWN, // Context spawn requested

View file

@ -0,0 +1,35 @@
// 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_CTX_STACK_USAGE_ASSERTION_H
namespace ircd::ctx::this_ctx
{
struct stack_usage_assertion;
size_t stack_at_here() __attribute__((noinline));
}
/// An instance of stack_usage_assertion is placed on a ctx stack where one
/// wants to test the stack usage at both construction and destruction points
/// to ensure it is less than the value set in ctx::prof::settings which is
/// generally some engineering safety factor of 2-3 etc. This should not be
/// entirely relied upon except during debug builds, however we may try to
/// provide an optimized build mode enabling these to account for any possible
/// differences in the stack between the environments.
///
struct ircd::ctx::this_ctx::stack_usage_assertion
{
#ifndef NDEBUG
stack_usage_assertion();
~stack_usage_assertion() noexcept;
#endif
};

View file

@ -15,28 +15,18 @@ namespace ircd::ctx {
/// Interface to the currently running context /// Interface to the currently running context
inline namespace this_ctx inline namespace this_ctx
{ {
struct critical_indicator; // Indicates if yielding happened for a section
struct critical_assertion; // Assert no yielding for a section
struct exception_handler; // Must be present to yield in a handler
struct uninterruptible; // Scope convenience for interruptible()
struct ctx &cur(); ///< Assumptional reference to *current struct ctx &cur(); ///< Assumptional reference to *current
const uint64_t &id(); // Unique ID for cur ctx const uint64_t &id(); // Unique ID for cur ctx
string_view name(); // Optional label for cur ctx string_view name(); // Optional label for cur ctx
ulong cycles_here(); // misc profiling related
bool interruption_requested(); // interruption(cur())
void interruption_point(); // throws if interruption_requested()
void wait(); // Returns when context is woken up. void wait(); // Returns when context is woken up.
void yield(); // Allow other contexts to run before returning. void yield(); // Allow other contexts to run before returning.
void interruption_point(); // throws if interruption_requested()
bool interruption_requested(); // interruption(cur())
void interruptible(const bool &); // interruptible(cur(), bool) +INTERRUPTION POINT
void interruptible(const bool &, std::nothrow_t) noexcept;
struct stack_usage_assertion; // Assert safety factor (see ctx/prof.h)
size_t stack_at_here() __attribute__((noinline));
ulong cycles_here();
// Return remaining time if notified; or <= 0 if not, and timeout thrown on throw overloads // Return remaining time if notified; or <= 0 if not, and timeout thrown on throw overloads
microseconds wait(const microseconds &, const std::nothrow_t &); microseconds wait(const microseconds &, const std::nothrow_t &);
template<class E, class duration> nothrow_overload<E, duration> wait(const duration &); template<class E, class duration> nothrow_overload<E, duration> wait(const duration &);
@ -59,116 +49,6 @@ namespace ircd::ctx
extern __thread ctx *current; extern __thread ctx *current;
} }
/// An instance of stack_usage_assertion is placed on a ctx stack where one
/// wants to test the stack usage at both construction and destruction points
/// to ensure it is less than the value set in ctx::prof::settings which is
/// generally some engineering safety factor of 2-3 etc. This should not be
/// entirely relied upon except during debug builds, however we may try to
/// provide an optimized build mode enabling these to account for any possible
/// differences in the stack between the environments.
///
struct ircd::ctx::this_ctx::stack_usage_assertion
{
#ifndef NDEBUG
stack_usage_assertion();
~stack_usage_assertion() noexcept;
#endif
};
/// An instance of critical_assertion detects an attempt to context switch.
///
/// For when the developer specifically does not want any yielding in a
/// section or anywhere up the stack from it. This device does not prevent
/// a switch and may carry no meaning outside of debug-mode compilation. It is
/// good practice to use this device even when it appears obvious the
/// section's callgraph has no chance of yielding: code changes, and everything
/// up the graph can change without taking notice of your section.
///
class ircd::ctx::this_ctx::critical_assertion
{
#ifndef NDEBUG
bool theirs;
public:
critical_assertion();
~critical_assertion() noexcept;
#endif
};
/// An instance of critical_indicator reports if context switching happened.
///
/// A critical_indicator remains true after construction until a context switch
/// has occurred. It then becomes false. This is not an assertion and is
/// available in optimized builds for real use. For example, a context may
/// want to recompute some value after a context switch and opportunistically
/// skip this effort when this indicator shows no switch occurred.
///
class ircd::ctx::this_ctx::critical_indicator
{
uint64_t state;
public:
uint64_t count() const { return yields(cur()) - state; }
operator bool() const { return yields(cur()) == state; }
critical_indicator()
:state{yields(cur())}
{}
};
/// An instance of exception_handler must be present to allow a context
/// switch inside a catch block. This is due to ABI limitations that stack
/// exceptions with thread-local assumptions and don't expect catch blocks
/// on the same thread to interleave when we switch the stack.
///
/// We first increment the refcount for the caught exception so it remains
/// intuitively accessible for the rest of the catch block. Then the presence
/// of this object makes the ABI believe the catch block has ended.
///
/// The exception cannot then be rethrown. DO NOT RETHROW THE EXCEPTION.
///
struct ircd::ctx::this_ctx::exception_handler
:std::exception_ptr
{
exception_handler() noexcept;
exception_handler(exception_handler &&) = delete;
exception_handler(const exception_handler &) = delete;
exception_handler &operator=(exception_handler &&) = delete;
exception_handler &operator=(const exception_handler &) = delete;
};
/// An instance of uninterruptible will suppress interrupts sent to the
/// context for the scope. Suppression does not discard any interrupt,
/// it merely ignores it at all interruption points until the suppression
/// ends, after which it will be thrown.
///
struct ircd::ctx::this_ctx::uninterruptible
{
struct nothrow;
bool theirs;
uninterruptible();
uninterruptible(uninterruptible &&) = delete;
uninterruptible(const uninterruptible &) = delete;
~uninterruptible() noexcept(false);
};
/// A variant of uinterruptible for users that must guarantee the ending of
/// the suppression scope will not be an interruption point. The default
/// behavior for uninterruptible is to throw, even from its destructor, to
/// fulfill the interruption request without any more delay.
///
struct ircd::ctx::this_ctx::uninterruptible::nothrow
{
bool theirs;
nothrow() noexcept;
nothrow(nothrow &&) = delete;
nothrow(const nothrow &) = delete;
~nothrow() noexcept;
};
/// This overload matches ::sleep() and acts as a drop-in for ircd contexts. /// This overload matches ::sleep() and acts as a drop-in for ircd contexts.
/// interruption point. /// interruption point.
inline void inline void

View file

@ -0,0 +1,53 @@
// 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_CTX_UNINTERRUPTIBLE_H
namespace ircd::ctx::this_ctx
{
struct uninterruptible;
bool interruptible() noexcept;
void interruptible(const bool &);
void interruptible(const bool &, std::nothrow_t) noexcept;
}
/// An instance of uninterruptible will suppress interrupts sent to the
/// context for the scope. Suppression does not discard any interrupt,
/// it merely ignores it at all interruption points until the suppression
/// ends, after which it will be thrown.
///
struct ircd::ctx::this_ctx::uninterruptible
{
struct nothrow;
bool theirs;
uninterruptible();
uninterruptible(uninterruptible &&) = delete;
uninterruptible(const uninterruptible &) = delete;
~uninterruptible() noexcept(false);
};
/// A variant of uinterruptible for users that must guarantee the ending of
/// the suppression scope will not be an interruption point. The default
/// behavior for uninterruptible is to throw, even from its destructor, to
/// fulfill the interruption request without any more delay.
///
struct ircd::ctx::this_ctx::uninterruptible::nothrow
{
bool theirs;
nothrow() noexcept;
nothrow(nothrow &&) = delete;
nothrow(const nothrow &) = delete;
~nothrow() noexcept;
};

View file

@ -423,6 +423,7 @@ ircd::ctx::finished(const ctx &ctx)
/// Indicates if `ctx` was terminated; does not clear the flag /// Indicates if `ctx` was terminated; does not clear the flag
bool bool
ircd::ctx::termination(const ctx &c) ircd::ctx::termination(const ctx &c)
noexcept
{ {
return c.flags & context::TERMINATED; return c.flags & context::TERMINATED;
} }
@ -430,6 +431,7 @@ ircd::ctx::termination(const ctx &c)
/// Indicates if `ctx` was interrupted; does not clear the flag /// Indicates if `ctx` was interrupted; does not clear the flag
bool bool
ircd::ctx::interruption(const ctx &c) ircd::ctx::interruption(const ctx &c)
noexcept
{ {
return c.flags & context::INTERRUPTED; return c.flags & context::INTERRUPTED;
} }
@ -437,6 +439,7 @@ ircd::ctx::interruption(const ctx &c)
/// Indicates if `ctx` will suppress any interrupts. /// Indicates if `ctx` will suppress any interrupts.
bool bool
ircd::ctx::interruptible(const ctx &c) ircd::ctx::interruptible(const ctx &c)
noexcept
{ {
return c.flags & ~context::NOINTERRUPT; return c.flags & ~context::NOINTERRUPT;
} }
@ -607,8 +610,6 @@ ircd::ctx::this_ctx::interruptible(const bool &b)
interruption_point(); interruption_point();
} }
/// Throws interrupted if the currently running context was interrupted
/// and clears the interrupt flag.
void void
ircd::ctx::this_ctx::interruptible(const bool &b, ircd::ctx::this_ctx::interruptible(const bool &b,
std::nothrow_t) std::nothrow_t)
@ -617,6 +618,13 @@ noexcept
interruptible(cur(), b); interruptible(cur(), b);
} }
bool
ircd::ctx::this_ctx::interruptible()
noexcept
{
return interruptible(cur());
}
/// Throws interrupted if the currently running context was interrupted /// Throws interrupted if the currently running context was interrupted
/// and clears the interrupt flag. /// and clears the interrupt flag.
void void