Skip to content

Commit

Permalink
comments
Browse files Browse the repository at this point in the history
  • Loading branch information
ConorWilliams committed Dec 29, 2023
1 parent 744a83b commit 9a6a762
Show file tree
Hide file tree
Showing 20 changed files with 595 additions and 219 deletions.
5 changes: 1 addition & 4 deletions docs/api/schedule.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@ Busy pool
Unit pool
-------------------

.. doxygentypedef:: lf::unit_pool
.. doxygenclass:: lf::unit_pool


Debug pool (extension)
----------------------------

.. doxygentypedef:: lf::ext::debug_pool

6 changes: 6 additions & 0 deletions include/libfork/core/co_alloc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,17 @@ struct [[nodiscard("This object should be co_awaited")]] co_new_t {
static constexpr std::size_t count = Extent; ///< The number of elements to allocate.
};

/**
* @brief An awaitable (in the context of an ``lf::task``) which triggers stack allocation.
*/
template <co_allocable T>
struct [[nodiscard("This object should be co_awaited")]] co_new_t<T, std::dynamic_extent> {
std::size_t count; ///< The number of elements to allocate.
};

/**
* @brief An awaitable (in the context of an ``lf::task``) which triggers stack deallocation.
*/
template <co_allocable T, std::size_t Extent>
struct [[nodiscard("This object should be co_awaited")]] co_delete_t : std::span<T, Extent> {};

Expand Down
6 changes: 2 additions & 4 deletions include/libfork/core/defer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@

#include <concepts>
#include <functional>
#include <libfork/core/macro.hpp>
#include <type_traits>
#include <utility>

#include "libfork/core/macro.hpp"

#include "libfork/core/impl/utility.hpp"

/**
Expand Down Expand Up @@ -69,9 +70,6 @@ class [[nodiscard("Defer will execute unless bound to a name!")]] defer : impl::
[[no_unique_address]] F m_f;
};

#define LF_CONCAT_OUTER(a, b) LF_CONCAT_INNER(a, b)
#define LF_CONCAT_INNER(a, b) a##b

/**
* @brief A macro to create an automatically named defer object.
*/
Expand Down
10 changes: 9 additions & 1 deletion include/libfork/core/eventually.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ class manual_eventually : impl::manual_lifetime<T> {
using impl::manual_lifetime<T>::destroy;
};

/**
* @brief A `lf::manual_eventually<T>` is an `lf::eventually<T>` which does not call destroy on destruction.
*
* This is useful for writing exception safe fork-join code and should be considered an expert-only feature.
*/
template <impl::non_void T>
requires impl::reference<T>
class manual_eventually<T> : eventually<T> {
Expand All @@ -155,7 +160,10 @@ class manual_eventually<T> : eventually<T> {
using eventually<T>::operator->;
using eventually<T>::operator*;

void destroy() noexcept {};
/**
* @brief Destroy the contained object (call its destructor).
*/
void destroy() noexcept { static_assert(std::is_trivially_destructible_v<T>); };
};

} // namespace core
Expand Down
14 changes: 11 additions & 3 deletions include/libfork/core/ext/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ class context : impl::immovable<context> {
/**
* @brief Submit pending/suspended tasks to the context.
*
* This is for use by implementor of the scheduler, this will trigger the notification function.
* This is for use by the implementor of the scheduler, this will trigger the notification function.
*/
void submit(intruded_list<submit_handle> jobs) noexcept {
void submit(intruded_list<submit_handle> jobs) {
m_submit.push(non_null(jobs));
m_notify();
}
Expand All @@ -93,11 +93,16 @@ class context : impl::immovable<context> {
nullary_function_t m_notify; ///< The user supplied notification function.
};

/**
* @brief Context for (user) schedulers to interact with.
*
* Additionally exposes submit and pop functions.
*/
class worker_context : public context {
public:
using context::context;

using context::submit;
using context::submit; ///< Submit an external task.

/**
* @brief Fetch a linked-list of the submitted tasks.
Expand All @@ -116,6 +121,9 @@ class worker_context : public context {

namespace impl {

/**
* @brief Context for internal use, contains full-API for push/pop.
*/
class full_context : public worker_context {
public:
using worker_context::worker_context;
Expand Down
4 changes: 4 additions & 0 deletions include/libfork/core/ext/list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,13 @@ class intrusive_list : impl::immovable<intrusive_list<T>> {

/**
* @brief Push a new node, this can be called concurrently from any number of threads.
*
* `new_node` should be an unlinked node e.g. not part of a list.
*/
constexpr void push(node *new_node) noexcept {

LF_ASSERT(new_node->m_next == nullptr);

node *stale_head = m_head.load(std::memory_order_relaxed);

for (;;) {
Expand Down
25 changes: 24 additions & 1 deletion include/libfork/core/ext/tls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,40 @@ namespace lf {

namespace impl::tls {

/**
* @brief Set when `impl::tls::thread_stack` is alive.
*/
constinit inline thread_local bool has_stack = false;
/**
* @brief A workers stack.
*
* This is wrapped in an `manual_lifetime` to make it trivially destructible/constructible such that it
* requires no construction checks to access.
*/
constinit inline thread_local manual_lifetime<stack> thread_stack = {};

/**
* @brief Set when `impl::tls::thread_stack` is alive.
*/
constinit inline thread_local bool has_context = false;
/**
* @brief A workers context.
*
* This is wrapped in an `manual_lifetime` to make it trivially destructible/constructible such that it
* requires no construction checks to access.
*/
constinit inline thread_local manual_lifetime<full_context> thread_context = {};

/**
* @brief Checked access to a workers stack.
*/
[[nodiscard]] inline auto stack() -> stack * {
LF_ASSERT(has_stack);
return thread_stack.data();
}

/**
* @brief Checked access to a workers context.
*/
[[nodiscard]] inline auto context() -> full_context * {
LF_ASSERT(has_context);
return thread_context.data();
Expand Down
66 changes: 58 additions & 8 deletions include/libfork/core/impl/awaitables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,21 @@ namespace lf::impl {

// -------------------------------------------------------- //

/**
* @brief An awaiter to explicitly transfer execution to another worker.
*
* This is generated by `await_transform` when awaiting on a pointer to a `lf::context`.
*/
struct switch_awaitable : std::suspend_always {

/**
* @brief Shortcut if already on context.
*/
auto await_ready() const noexcept { return tls::context() == dest; }

/**
* @brief Reschedule this coro onto `dest`.
*/
auto await_suspend(std::coroutine_handle<>) noexcept -> std::coroutine_handle<> {

// Schedule this coro for execution on Dest.
Expand Down Expand Up @@ -71,14 +82,23 @@ struct switch_awaitable : std::suspend_always {
return std::noop_coroutine();
}

intrusive_list<submit_handle>::node self;
context *dest;
intrusive_list<submit_handle>::node self; ///< The current coroutine's handle.
context *dest; ///< Target context.
};

// -------------------------------------------------------- //

/**
* @brief An awaiter that returns space allocated on the current fibre's stack.
*
* This never suspends the coroutine and is generated by `await_transform` when awaiting on a pointer to a
* `lf::context`.
*/
template <typename T, std::size_t E>
struct alloc_awaitable : std::suspend_never, std::span<T, E> {
/**
* @brief Return a handle to the memory.
*/
[[nodiscard]] auto await_resume() const noexcept -> std::conditional_t<E == 1, T *, std::span<T, E>> {
if constexpr (E == 1) {
return this->data();
Expand All @@ -90,8 +110,16 @@ struct alloc_awaitable : std::suspend_never, std::span<T, E> {

// -------------------------------------------------------- //

/**
* @brief An awaiter that suspends the current coroutine and transfers control to a child task.
*
* The parent task is made available for stealing. This is generated by `await_transform` when awaiting on an
* `lf::impl::quasi_awaitable`.
*/
struct fork_awaitable : std::suspend_always {

/**
* @brief Sym-transfer to child, push parent to queue.
*/
auto await_suspend(std::coroutine_handle<>) const noexcept -> std::coroutine_handle<> {
LF_LOG("Forking, push parent to context");
// Need a copy (on stack) in case *this is destructed after push.
Expand All @@ -100,22 +128,35 @@ struct fork_awaitable : std::suspend_always {
return child;
}

frame *child;
frame *parent;
frame *child; ///< The suspended child coroutine's frame.
frame *parent; ///< The calling coroutine's frame.
};

/**
* @brief An awaiter that suspends the current coroutine and transfers control to a child task.
*
* The parent task is __not__ made available for stealing. This is generated by `await_transform` when
* awaiting on an `lf::impl::quasi_awaitable`.
*/
struct call_awaitable : std::suspend_always {

/**
* @brief Sym-transfer to child.
*/
auto await_suspend(std::coroutine_handle<>) const noexcept -> std::coroutine_handle<> {
LF_LOG("Calling");
return child->self();
}

frame *child;
frame *child; ///< The suspended child coroutine's frame.
};

// -------------------------------------------------------------------------------- //

/**
* @brief An awaiter to synchronize execution of child tasks.
*
* This is generated by `await_transform` when awaiting on an `lf::impl::join_type`.
*/
struct join_awaitable {
private:
void take_stack_reset_frame() const noexcept {
Expand All @@ -128,6 +169,9 @@ struct join_awaitable {
}

public:
/**
* @brief Shortcut if children are ready.
*/
auto await_ready() const noexcept -> bool {
// If no steals then we are the only owner of the parent and we are ready to join.
if (self->load_steals() == 0) {
Expand All @@ -154,6 +198,9 @@ struct join_awaitable {
return false;
}

/**
* @brief Mark at join point then yield to scheduler or resume if children are done.
*/
auto await_suspend(std::coroutine_handle<> task) const noexcept -> std::coroutine_handle<> {
// Currently joins = k_u16_max - num_joined
// We set joins = joins() - (k_u16_max - num_steals)
Expand Down Expand Up @@ -188,6 +235,9 @@ struct join_awaitable {
return std::noop_coroutine();
}

/**
* @brief A noop in release.
*/
void await_resume() const noexcept {
LF_LOG("join resumes");
// Check we have been reset.
Expand All @@ -196,7 +246,7 @@ struct join_awaitable {
LF_ASSERT(self->stacklet() == tls::stack()->top());
}

frame *self;
frame *self; ///< The frame of the awaiting coroutine.
};

} // namespace lf::impl
Expand Down
16 changes: 14 additions & 2 deletions include/libfork/core/impl/combinate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ struct promise;

/**
* @brief Awaitable in the context of an `lf::task` coroutine.
*
* This will be transformed by an `await_transform` and trigger a fork or call.
*/
template <returnable R, return_address_for<R> I, tag Tag>
struct [[nodiscard("A quasi_awaitable MUST be immediately co_awaited!")]] quasi_awaitable {
Expand All @@ -42,6 +44,11 @@ struct [[nodiscard("A quasi_awaitable MUST be immediately co_awaited!")]] quasi_

// ---------------------------- //

/**
* @brief Call an async function with a synthesized first argument.
*
* The first argument will contain a copy of the function hence, this is a fixed-point combinator.
*/
template <quasi_pointer I, tag Tag, async_function_object F>
struct [[nodiscard("A bound function SHOULD be immediately invoked!")]] y_combinate {

Expand Down Expand Up @@ -73,15 +80,20 @@ struct [[nodiscard("A bound function SHOULD be immediately invoked!")]] y_combin
}
};

// // ---------------------------- //
// ---------------------------- //

/**
* @brief Build a combinator for `ret` and `fun`.
*/
template <tag Tag, quasi_pointer I, async_function_object F>
auto combinate(I ret, F fun) -> y_combinate<I, Tag, F> {
return {std::move(ret), std::move(fun)};
}

/**
* @brief Prevent each layer wrapping the function in another `first_arg_t`.
* @brief Build a combinator for `ret` and `fun`.
*
* This specialization prevents each layer wrapping the function in another `first_arg_t`.
*/
template <tag Tag,
tag OtherTag,
Expand Down
Loading

0 comments on commit 9a6a762

Please sign in to comment.