Skip to content

Commit 28535c6

Browse files
committed
asioexec::completion_token: Remove std::unique_lock From "Frame"
Reduces the size of a "frame" by removing the std::unique_lock member variable and making the "frame" itself the lock guard. This is the lock management method shown when presenting asioexec::completion_token in the CppCon 2025 talk "std::execution in Asio Codebases: Adopting Senders Without a Rewrite."
1 parent 3d8b091 commit 28535c6

1 file changed

Lines changed: 36 additions & 23 deletions

File tree

include/asioexec/completion_token.hpp

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -159,51 +159,64 @@ namespace asioexec {
159159
bool abandoned_{false};
160160

161161
class frame_ {
162-
operation_state_base& self_;
163-
std::unique_lock<std::recursive_mutex> l_;
162+
operation_state_base* self_;
164163
frame_* prev_;
165164
public:
166165
explicit frame_(operation_state_base& self) noexcept
167-
: self_(self)
168-
, l_(self.m_)
166+
: self_([&]() noexcept {
167+
self.m_.lock();
168+
return &self;
169+
}())
169170
, prev_(self.frames_) {
170171
self.frames_ = this;
171172
}
172173

174+
constexpr frame_(frame_&& other) noexcept
175+
: self_(std::exchange(other.self_, nullptr))
176+
, prev_(std::exchange(other.prev_, nullptr)) {
177+
}
178+
173179
frame_(const frame_&) = delete;
174180

175181
~frame_() noexcept {
176-
if (l_) {
177-
STDEXEC_ASSERT(self_.frames_ == this);
178-
self_.frames_ = prev_;
179-
if (!self_.frames_ && self_.abandoned_) {
182+
if (self_) {
183+
std::unique_lock l(self_->m_, std::adopt_lock);
184+
STDEXEC_ASSERT(self_->frames_ == this);
185+
self_->frames_ = prev_;
186+
if (!self_->frames_ && self_->abandoned_) {
180187
// We are the last frame and the handler is gone so it's up to us to
181188
// finalize the operation
182-
l_.unlock();
183-
self_.callback_.reset();
184-
if (self_.ex_) {
185-
::STDEXEC::set_error(static_cast<Receiver&&>(self_.r_), std::move(self_.ex_));
189+
l.unlock();
190+
self_->callback_.reset();
191+
if (self_->ex_) {
192+
::STDEXEC::set_error(static_cast<Receiver&&>(self_->r_), std::move(self_->ex_));
186193
} else {
187-
::STDEXEC::set_stopped(static_cast<Receiver&&>(self_.r_));
194+
::STDEXEC::set_stopped(static_cast<Receiver&&>(self_->r_));
188195
}
189196
}
190197
}
191198
}
192199

193200
explicit operator bool() const noexcept {
194-
return bool(l_);
201+
return bool(self_);
195202
}
196203

197204
void release() noexcept {
198-
auto ptr = this;
199-
do {
200-
STDEXEC_ASSERT(ptr->l_);
201-
STDEXEC_ASSERT(self_.frames_ == ptr);
202-
ptr = ptr->prev_;
203-
self_.frames_->l_.unlock();
204-
self_.frames_->prev_ = nullptr;
205-
self_.frames_ = ptr;
206-
} while (ptr);
205+
auto&& self = *self_;
206+
STDEXEC_ASSERT(this == self.frames_);
207+
for (;;) {
208+
STDEXEC_ASSERT(self.frames_);
209+
STDEXEC_ASSERT(self.frames_->self_ == &self);
210+
self.frames_->self_ = nullptr;
211+
const auto prev = self.frames_->prev_;
212+
self.frames_->prev_ = nullptr;
213+
self.frames_ = prev;
214+
self.m_.unlock();
215+
if (!prev) {
216+
break;
217+
}
218+
}
219+
STDEXEC_ASSERT(!self_);
207220
}
208221
};
209222

0 commit comments

Comments
 (0)