dispenso 1.6.2
A library for task parallelism
Loading...
Searching...
No Matches
once_function.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
15#pragma once
16
17#include <cstring>
18#include <utility>
19
20#include <dispenso/detail/once_callable_impl.h>
21#include <dispenso/platform.h>
22
23namespace dispenso {
24
25#if DISPENSO_HAS_CONCEPTS
34template <typename F>
35concept OnceCallableFunc = std::invocable<F>;
36#endif // DISPENSO_HAS_CONCEPTS
37
38namespace detail {
39template <typename Result>
40class FutureBase;
41template <typename Result>
42class FutureImplBase;
43} // namespace detail
44
55 public:
59 OnceFunction() : ops_(detail::emptyOps()) {}
60
68 template <typename F>
69 DISPENSO_REQUIRES(OnceCallableFunc<F>)
70 OnceFunction(F&& f) : ops_(detail::createOnceCallable(std::forward<F>(f), buf_)) {}
71
72 OnceFunction(const OnceFunction& other) = delete;
73
78 OnceFunction(OnceFunction&& other) noexcept {
79 moveFrom(other);
80 }
81
82 OnceFunction& operator=(OnceFunction&& other) noexcept {
83 if (this != &other) {
84#if defined DISPENSO_DEBUG
85 assert(
86 ops_->invoke == nullptr &&
87 "OnceFunction must be invoked or cleanupNotRun() before reassignment");
88#endif // DISPENSO_DEBUG
89 moveFrom(other);
90 }
91 return *this;
92 }
93
100#if defined DISPENSO_DEBUG
101 assert(ops_->invoke != nullptr && "Must not cleanup an invalid OnceFunction!");
102#endif
103 consumeOps()->invoke(buf_, false);
104 }
105
110 void operator()() const {
111#if defined DISPENSO_DEBUG
112 assert(ops_->invoke != nullptr && "Must not use OnceFunction more than once!");
113#endif // DISPENSO_DEBUG
114 consumeOps()->invoke(buf_, true);
115 }
116
117 private:
118 // 64 bytes total (one cache line): 56 bytes inline storage + 8 bytes operations ptr.
119 // Inline: buf_ holds the functor directly.
120 // Spill: buf_[0..7] holds a void* to pool-allocated storage.
121 // Moves are a 64-byte memcpy, except for inline functors that are not trivially copyable, which
122 // the operations' relocate entry move-constructs into place.
123 void moveFrom(OnceFunction& other) noexcept {
124 if (DISPENSO_EXPECT(other.ops_->relocate == nullptr, true)) {
125 std::memcpy(static_cast<void*>(this), &other, sizeof(OnceFunction));
126 } else {
127 ops_ = other.ops_;
128 ops_->relocate(other.buf_, buf_);
129 }
130 other.ops_ = detail::emptyOps();
131 }
132
133 // Marks this object consumed before the call, because the invoked functor may free the memory
134 // holding this OnceFunction. Leaves no stale relocate entry for a later move to act on.
135 const detail::OnceCallableOps* consumeOps() const {
136 const detail::OnceCallableOps* ops = ops_;
137 ops_ = detail::emptyOps();
138 return ops;
139 }
140
141 alignas(64) mutable char buf_[detail::kOnceFunctionInlineSize];
142 mutable const detail::OnceCallableOps* ops_;
143
144 template <typename Result>
145 friend class detail::FutureBase;
146 template <typename Result>
147 friend class detail::FutureImplBase;
148};
149
150} // namespace dispenso
OnceFunction(OnceFunction &&other) noexcept