Skip to content

Commit dae2081

Browse files
[V4] Tidy promise (#76)
* todo list * todo * comment cleanups * refactor tranform error handling * drop version include * refactor stash * refactor cancel * restore parent return * fix: U -> R * todo
1 parent 21b8886 commit dae2081

3 files changed

Lines changed: 58 additions & 42 deletions

File tree

src/core/promise.cxx

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
module;
2-
#include <version>
3-
42
#include "libfork/__impl/assume.hpp"
53
#include "libfork/__impl/compiler.hpp"
64
#include "libfork/__impl/exception.hpp"
@@ -251,7 +249,7 @@ struct awaitable : std::suspend_always {
251249
* @brief In a separate function to allow it to be placed in cold block.
252250
*/
253251
template <typename T>
254-
constexpr void cleanup_and_stash(this awaitable self, coro<promise_type<T, Context>> parent) noexcept {
252+
constexpr void stash_and_resume(this awaitable self, coro<promise_type<T, Context>> parent) noexcept {
255253
// Clean-up the child that will never be resumed.
256254
self.child->handle().destroy();
257255
stash_current_exception(&parent.promise().frame);
@@ -270,8 +268,7 @@ struct awaitable : std::suspend_always {
270268

271269
if (parent.promise().frame.is_cancelled()) [[unlikely]] {
272270
// Noop if canceled, must clean-up the child that will never be resumed.
273-
self.child->handle().destroy();
274-
return parent;
271+
return self.child->handle().destroy(), parent;
275272
}
276273

277274
// Propagate parent->child relationships
@@ -288,8 +285,7 @@ struct awaitable : std::suspend_always {
288285
LF_TRY {
289286
not_null(thread_context<Context>)->push(frame_handle{key, &parent.promise().frame});
290287
} LF_CATCH_ALL {
291-
self.cleanup_and_stash(parent);
292-
return parent;
288+
return self.stash_and_resume(parent), parent;
293289
}
294290
}
295291

@@ -423,7 +419,7 @@ struct join_awaitable {
423419

424420
// =============== Frame mixin =============== //
425421

426-
template <worker_context Context>
422+
template <worker_context Ctx>
427423
struct mixin_frame {
428424

429425
// === For internal use === //
@@ -441,54 +437,60 @@ struct mixin_frame {
441437
// --- Allocation
442438

443439
static auto operator new(std::size_t sz) -> void * {
444-
void *ptr = not_null(thread_context<Context>)->allocator().push(sz);
440+
void *ptr = not_null(thread_context<Ctx>)->allocator().push(sz);
445441
LF_ASSUME(is_aligned<k_new_align>(ptr));
446442
return std::assume_aligned<k_new_align>(ptr);
447443
}
448444

449445
static auto operator delete(void *p, std::size_t sz) noexcept -> void {
450-
not_null(thread_context<Context>)->allocator().pop(p, sz);
446+
not_null(thread_context<Ctx>)->allocator().pop(p, sz);
451447
}
452448

453449
// --- Await transformations
454450

455451
template <category Cat, typename R, typename Fn, typename... Args>
456-
[[nodiscard]]
457-
constexpr auto await_transform(this auto &self, pkg<Cat, Context, R, Fn, Args...> &&pkg) noexcept
458-
-> awaitable<Cat, Context> {
452+
static constexpr auto await_transform_pkg(pkg<Cat, Ctx, R, Fn, Args...> &&pkg) -> awaitable<Cat, Ctx> {
459453

460-
using U = async_result_t<Fn, Context, Args...>;
454+
using U = async_result_t<Fn, Ctx, Args...>;
461455

462-
LF_TRY {
456+
// clang-format off
463457

464-
// clang-format off
458+
promise_type<U, Ctx> *child_promise = access::promise<Ctx>(std::move(pkg.args).apply(
459+
[&](auto &&...args) LF_HOF(std::invoke(fwd_fn<Fn>(pkg.fn), env<Ctx>{}, LF_FWD(args)...))
460+
));
465461

466-
auto *child_promise = access::promise<Context>(std::move(pkg.args).apply(
467-
[&](auto &&...args) LF_HOF(std::invoke(fwd_fn<Fn>(pkg.fn), env<Context>{}, LF_FWD(args)...))
468-
));
462+
// clang-format on
469463

470-
// clang-format on
464+
LF_ASSUME(child_promise);
471465

472-
LF_ASSUME(child_promise);
466+
// void can signal drop return.
467+
static_assert(std::same_as<R, U> || std::is_void_v<R>);
473468

474-
if constexpr (!std::is_void_v<R>) {
475-
child_promise->return_address = pkg.return_address;
476-
} else {
477-
if constexpr (!std::is_void_v<U>) {
478-
// Set child's return address to null to inhibit the return
479-
// TODO: add test for this
480-
child_promise->return_address = nullptr;
481-
}
482-
}
469+
// TODO: tests for null path
483470

484-
return {.child = &child_promise->frame};
471+
if constexpr (!std::is_void_v<R>) {
472+
child_promise->return_address = pkg.return_address;
473+
} else if constexpr (!std::is_void_v<U>) {
474+
// Set child's return address to null to inhibit the return
475+
// TODO: add test for this
476+
child_promise->return_address = nullptr;
477+
}
478+
479+
return {.child = &child_promise->frame};
480+
}
481+
482+
template <category Cat, typename R, typename Fn, typename... Args>
483+
constexpr auto
484+
await_transform(this auto &self, pkg<Cat, Ctx, R, Fn, Args...> &&pkg) noexcept -> awaitable<Cat, Ctx> {
485+
LF_TRY {
486+
return self.await_transform_pkg(std::move(pkg));
485487
} LF_CATCH_ALL {
486488
stash_current_exception(&self.frame);
487-
return {.child = nullptr};
488489
}
490+
return {.child = nullptr};
489491
}
490492

491-
constexpr auto await_transform(this auto &self, join_type) noexcept -> join_awaitable<Context> {
493+
constexpr auto await_transform(this auto &self, join_type) noexcept -> join_awaitable<Ctx> {
492494
return {.frame = &self.frame};
493495
}
494496

@@ -525,7 +527,6 @@ struct promise_type : mixin_frame<Context> {
525527
requires std::assignable_from<T &, U &&>
526528
constexpr void return_value(U &&value) noexcept(std::is_nothrow_assignable_v<T &, U &&>) {
527529
if (return_address) {
528-
// TODO: add the appropriate await_transforms
529530
*return_address = LF_FWD(value);
530531
}
531532
}

src/core/stack.cxx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ export class geometric_stack {
7171
public:
7272
[[nodiscard]]
7373
constexpr auto checkpoint() noexcept -> checkpoint_t {
74+
75+
// TODO: revisit if this + exception is worth is for no-alloc recoverability.
76+
77+
// if (!m_root) [[unlikely]] {
78+
// m_root.reset(new heap);
79+
// }
80+
7481
return checkpoint_t{m_root.get()};
7582
}
7683

todo.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
# TODO
22

3-
- Context tag in API
4-
- Context in invocability concepts
3+
- [x] Context tag in API
4+
- [x] Context in invocability concepts
5+
56
- Integrate geometric allocator (that can throw)
6-
- `-fassume-nothrow-exception-dtor`
7-
- Test nothrow allocator performance (just terminate?)
8-
- Cancellation:
9-
- Maybe in separate `co_await scope`
10-
- Integrate into join
11-
- Exception safety
7+
- [x] Initial impl
8+
- [ ] Test correct throwing spec
9+
10+
- [ ] Optimize release/resume in presence of steals (need benchmark)
11+
12+
- [ ] `-fassume-nothrow-exception-dtor`
13+
14+
- [ ] Test nothrow allocator performance (just terminate?)
15+
16+
- [ ] Cancellation:
17+
- [ ] Maybe in separate `co_await scope`
18+
- [ ] Integrate into join
19+
- [ ] Exception safety

0 commit comments

Comments
 (0)