Skip to content

Commit ab09382

Browse files
author
Dmitry Malakhov
committed
Document APIs
1 parent 6547de3 commit ab09382

34 files changed

+3362
-113
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ Returning to main loop is not possible if a signal was raised by your implementa
1414

1515
And as any IO operation, this is faster and fancier when you do it asynchronously.
1616

17+
## Accessing documentation
18+
19+
This project uses doxygen for documenting it's APIs. You can read docs in source code or read an index generated by `xmake doxygen` command. There is no remote docs index currently
20+
1721
## Brief API overview
1822

1923
Library is within it's very early stage of development and interfaces may change in future

devenv.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
packages = with pkgs; [
44
llvmPackages_21.clang-tools
55
llvmPackages_19.libstdcxxClang
6-
6+
doxygen
77
xmake
88
gdb
99
];

doxyfile

Lines changed: 2947 additions & 0 deletions
Large diffs are not rendered by default.

include/corosig/Coro.hpp

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,17 @@ concept NotReactor = !std::same_as<Reactor, T>;
2525

2626
template <typename T, typename E>
2727
struct CoroutinePromiseType : CoroListNode {
28-
28+
/// @brief Construct new coroutine promise bound to reactor
29+
/// @note For more detailed explanation check
30+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
2931
CoroutinePromiseType(Reactor &reactor, NotReactor auto const &...) noexcept
3032
: m_reactor{reactor} {
3133
}
3234

35+
/// @brief Construct new coroutine promise bound to reactor. This overload is used when some
36+
/// object's method is declared as coroutine
37+
/// @note For more detailed explanation check
38+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
3339
CoroutinePromiseType(NotReactor auto const &,
3440
Reactor &reactor,
3541
NotReactor auto const &...) noexcept
@@ -43,41 +49,63 @@ struct CoroutinePromiseType : CoroListNode {
4349

4450
~CoroutinePromiseType() override = default;
4551

52+
/// @brief Allocate new coroutine frame using allocator from reactor
53+
/// @note C++20 coroutine's required method. For more detailed explanation check
54+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
4655
static void *operator new(size_t n, Reactor &reactor, NotReactor auto const &...) noexcept {
4756
return reactor.allocator().allocate(n);
4857
}
4958

59+
/// @brief Allocate new coroutine frame using allocator from reactor. This overload is used when
60+
/// some object's method is declared as coroutine
61+
/// @note C++20 coroutine's required method. For more detailed explanation check
62+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
5063
static void *operator new(size_t n,
5164
NotReactor auto const &,
5265
Reactor &reactor,
5366
NotReactor auto const &...) noexcept {
5467
return reactor.allocator().allocate(n);
5568
}
5669

70+
/// @brief Noop
71+
/// @note C++20 coroutine's required method. For more detailed explanation check
72+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
5773
static void operator delete(void *) noexcept {
5874
// nothing to do in here since reactor is not accessible. instead, a coro frame is released when
5975
// future is destroyed
6076
}
6177

78+
/// @brief Add this as a CoroListNode into reactor to be executed later
6279
void yield_to_reactor() noexcept {
63-
m_reactor.yield(*this);
80+
m_reactor.schedule(*this);
6481
}
6582

83+
/// @brief Add this PollListNode into reactor to be executed later, when event becomes awailable
6684
void poll_to_reactor(PollListNode &node) noexcept {
67-
m_reactor.poll(node);
85+
m_reactor.schedule_when_ready(node);
6886
}
6987

88+
/// @brief Call an abort. Corosig expects no exceptions to be thrown around since they are not
89+
/// safe to throw in sighandlers.
90+
/// @note C++20 coroutine's required method. For more detailed explanation check
91+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
7092
[[noreturn]] static void unhandled_exception() noexcept {
7193
std::abort();
7294
}
7395

96+
/// @note C++20 coroutine's required method. For more detailed explanation check
97+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
7498
Fut<T, E> get_return_object() noexcept;
7599
static Fut<T, E> get_return_object_on_allocation_failure() noexcept;
76100

101+
/// @note C++20 coroutine's required method. For more detailed explanation check
102+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
77103
auto initial_suspend() noexcept {
78104
return std::suspend_never{};
79105
}
80106

107+
/// @note C++20 coroutine's required method. For more detailed explanation check
108+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
81109
auto final_suspend() noexcept {
82110
struct ReturnControlToCaller {
83111
static bool await_ready() noexcept {
@@ -96,6 +124,9 @@ struct CoroutinePromiseType : CoroListNode {
96124
return ReturnControlToCaller{};
97125
}
98126

127+
/// @brief Return a value form coroutine
128+
/// @note C++20 coroutine's required method. For more detailed explanation check
129+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
99130
template <std::convertible_to<Result<T, E>> U>
100131
void return_value(U &&value) noexcept {
101132
assert(m_value.is_nothing());
@@ -117,8 +148,11 @@ struct CoroutinePromiseType : CoroListNode {
117148

118149
} // namespace detail
119150

151+
/// @brief A result, which will become available in the future
120152
template <typename T = void, typename E = AllocationError>
121153
struct [[nodiscard("forgot to await?")]] Fut {
154+
/// @note C++20 coroutine's required typedef. For more detailed explanation check
155+
/// https://en.cppreference.com/w/cpp/language/coroutines.html
122156
using promise_type = detail::CoroutinePromiseType<T, E>;
123157

124158
Fut(const Fut &) = delete;
@@ -134,10 +168,13 @@ struct [[nodiscard("forgot to await?")]] Fut {
134168
}
135169
}
136170

171+
/// @brief Tell if future is already available. General-purpose code shall not use this method
172+
/// and just simply co_await a future
137173
[[nodiscard]] bool completed() const noexcept {
138174
return m_handle.value == nullptr || !promise().m_value.is_nothing();
139175
}
140176

177+
/// @brief Run reactor's event loop until this future is ready
141178
Result<T, extend_error<E, SyscallError>> block_on() && noexcept {
142179
while (!completed()) {
143180
COROSIG_TRYV(promise().m_reactor.do_event_loop_iteration());
@@ -148,39 +185,40 @@ struct [[nodiscard("forgot to await?")]] Fut {
148185
return std::move(promise().m_value);
149186
}
150187

151-
struct Awaiter {
152-
Awaiter(const Awaiter &) = delete;
153-
Awaiter(Awaiter &&) = delete;
154-
Awaiter &operator=(const Awaiter &) = delete;
155-
Awaiter &operator=(Awaiter &&) = delete;
156-
157-
[[nodiscard]] bool await_ready() const noexcept {
158-
return m_future.completed();
159-
}
188+
/// @brief Await for result inside this future to become available
189+
auto operator co_await() && noexcept {
190+
struct Awaiter {
191+
Awaiter(const Awaiter &) = delete;
192+
Awaiter(Awaiter &&) = delete;
193+
Awaiter &operator=(const Awaiter &) = delete;
194+
Awaiter &operator=(Awaiter &&) = delete;
160195

161-
void await_suspend(std::coroutine_handle<> h) const noexcept {
162-
m_future.promise().m_waiting_coro = h;
163-
}
196+
[[nodiscard]] bool await_ready() const noexcept {
197+
return m_future.completed();
198+
}
164199

165-
Result<T, E> await_resume() const noexcept {
166-
if (m_future.m_handle.value == nullptr) {
167-
return Failure{AllocationError{}};
200+
void await_suspend(std::coroutine_handle<> h) const noexcept {
201+
m_future.promise().m_waiting_coro = h;
168202
}
169203

170-
assert(!m_future.promise().m_value.is_nothing());
171-
return std::move(m_future.promise().m_value);
172-
}
204+
Result<T, E> await_resume() const noexcept {
205+
if (m_future.m_handle.value == nullptr) {
206+
return Failure{AllocationError{}};
207+
}
173208

174-
private:
175-
friend Fut;
176-
Awaiter(Fut &future) noexcept
177-
: m_future{future} {
178-
}
209+
assert(!m_future.promise().m_value.is_nothing());
210+
return std::move(m_future.promise().m_value);
211+
}
179212

180-
Fut &m_future;
181-
};
213+
private:
214+
friend Fut;
215+
Awaiter(Fut &future) noexcept
216+
: m_future{future} {
217+
}
218+
219+
Fut &m_future;
220+
};
182221

183-
Awaiter operator co_await() && noexcept {
184222
return Awaiter{*this};
185223
}
186224

include/corosig/ErrorTypes.hpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ concept WithDescription = requires(E const &e) {
2525

2626
} // namespace detail
2727

28+
/// @brief An error which contains one of specified errors inside
2829
template <typename... TYPES>
2930
struct Error : std::variant<TYPES...> {
3031
private:
@@ -38,29 +39,36 @@ struct Error : std::variant<TYPES...> {
3839
public:
3940
using Base::Base;
4041

42+
/// @brief Return an error description. If an underlying error has method .description, it's
43+
/// result is returned. Otherwise, it's typename is returned
4144
[[nodiscard]] std::string_view description() const noexcept {
4245
return visit(Overloaded{
4346
[](detail::WithDescription auto const &e) { return e.description(); },
4447
[](auto const &e) { return std::string_view{typeid(std::decay_t<decltype(e)>).name()}; },
4548
});
4649
}
4750

51+
/// @brief Tell if an error holds a type T inside
4852
template <typename T>
4953
[[nodiscard]] bool holds() const noexcept {
5054
return std::holds_alternative<T>(*this);
5155
}
5256

57+
/// @brief Visit all possible errors inside using f as visitor
5358
template <typename F>
5459
[[nodiscard]] decltype(auto) visit(F &&f) noexcept {
5560
return std::visit(std::forward<F>(f), *this);
5661
}
5762

63+
/// @brief Visit all possible errors inside using f as visitor
5864
template <typename F>
5965
[[nodiscard]] decltype(auto) visit(F &&f) const noexcept {
6066
return std::visit(std::forward<F>(f), *this);
6167
}
6268
};
6369

70+
/// @brief An error type that represents that there is no error. Used only for metaprogramming. Any
71+
/// usage in potentially evaluated context makes program ill-formed
6472
struct NoError;
6573

6674
namespace detail {
@@ -94,25 +102,28 @@ using extend_error =
94102

95103
} // namespace detail
96104

105+
/// @brief Extend Error type with other options. If E is Error, it's underlying types are used for
106+
/// extension, otherwise E itself is used. Any duplicates in the list are automatically
107+
/// removed to form a list of unique options
97108
template <typename... E>
98109
using extend_error =
99110
boost::mp11::mp_fold<boost::mp11::mp_list<E...>, Error<>, detail::extend_error>;
100111

101-
template <typename... E>
102-
using extend_error =
103-
boost::mp11::mp_fold<boost::mp11::mp_list<E...>, Error<>, detail::extend_error>;
104-
112+
/// @brief Get an error type which may be returned by functor CALLABLE when called with ARGS
105113
template <auto CALLABLE, typename... ARGS>
106114
using error_type = std::decay_t<
107115
decltype(std::declval<std::invoke_result_t<decltype(CALLABLE), ARGS...>>().error())>;
108116

117+
/// @brief An allocation error. Does it really need a description?
109118
struct AllocationError {
110119
auto operator<=>(const AllocationError &) const noexcept = default;
111120
};
112121

122+
/// @brief An error, propagated from a syscall
113123
struct SyscallError {
114124
auto operator<=>(const SyscallError &) const noexcept = default;
115125

126+
/// @brief Get current syscall error. In POSIX this function copies an errno into value
116127
static SyscallError current() noexcept;
117128

118129
[[nodiscard]] std::string_view description() const noexcept;

include/corosig/Parallel.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
namespace corosig {
1717

18+
/// @brief Wait when all futures are ready. Return all of their results
1819
template <typename... R, typename... E>
1920
Fut<std::tuple<Result<R, E>...>> when_all(Reactor &, Fut<R, E> &&...futs) noexcept {
2021
co_return std::tuple{co_await std::move(futs)...};
@@ -44,6 +45,8 @@ using WrapVoid = std::conditional_t<std::same_as<void, T>, std::monostate, T>;
4445

4546
} // namespace detail
4647

48+
/// @brief Wait when all futures are ready. Return all of values from their results or the first
49+
/// error that occurred among them
4750
template <typename... R, typename... E>
4851
Fut<std::tuple<detail::WrapVoid<R>...>, extend_error<E...>>
4952
when_all_succeed(Reactor &, Fut<R, E> &&...futs) noexcept {

include/corosig/PollEvent.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace corosig {
1313

14+
/// @brief Await for event to occur for handle
15+
/// @note Event is only actually polled when this struct is co_awaited
1416
struct PollEvent : PollListNode {
1517
PollEvent(os::Handle handle, poll_event_e event) {
1618
this->handle = handle;

0 commit comments

Comments
 (0)