Skip to content

Commit e085875

Browse files
committed
exec::asio::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 exec::asio::completion_token in the CppCon 2025 talk "std::execution in Asio Codebases: Adopting Senders Without a Rewrite."
1 parent cf2541c commit e085875

1 file changed

Lines changed: 28 additions & 24 deletions

File tree

include/exec/asio/completion_token.hpp

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,11 @@ namespace experimental::execution::asio
187187

188188
class frame_
189189
{
190-
operation_state_base& self_;
191-
std::unique_lock<std::recursive_mutex> l_;
192-
frame_* prev_;
190+
operation_state_base* self_;
191+
frame_* prev_;
193192
public:
194193
explicit frame_(operation_state_base& self) noexcept
195-
: self_(self)
196-
, l_(self.m_)
194+
: self_((self.m_.lock(), &self))
197195
, prev_(self.frames_)
198196
{
199197
self.frames_ = this;
@@ -203,46 +201,52 @@ namespace experimental::execution::asio
203201

204202
~frame_() noexcept
205203
{
206-
if (l_)
204+
if (self_)
207205
{
208-
STDEXEC_ASSERT(self_.frames_ == this);
209-
self_.frames_ = prev_;
210-
if (!self_.frames_ && self_.abandoned_)
206+
std::unique_lock l(self_->m_, std::adopt_lock);
207+
STDEXEC_ASSERT(self_->frames_ == this);
208+
self_->frames_ = prev_;
209+
if (!self_->frames_ && self_->abandoned_)
211210
{
212211
// We are the last frame and the handler is gone so it's up to us to
213212
// finalize the operation
214-
l_.unlock();
215-
self_.callback_.reset();
216-
if (self_.ex_)
213+
l.unlock();
214+
self_->callback_.reset();
215+
if (self_->ex_)
217216
{
218-
::STDEXEC::set_error(static_cast<Receiver&&>(self_.r_), std::move(self_.ex_));
217+
::STDEXEC::set_error(static_cast<Receiver&&>(self_->r_), std::move(self_->ex_));
219218
}
220219
else
221220
{
222-
::STDEXEC::set_stopped(static_cast<Receiver&&>(self_.r_));
221+
::STDEXEC::set_stopped(static_cast<Receiver&&>(self_->r_));
223222
}
224223
}
225224
}
226225
}
227226

228227
explicit operator bool() const noexcept
229228
{
230-
return bool(l_);
229+
return bool(self_);
231230
}
232231

233232
void release() noexcept
234233
{
235-
auto ptr = this;
236-
do
234+
auto&& self = *self_;
235+
STDEXEC_ASSERT(this == self.frames_);
236+
for (;;)
237237
{
238-
STDEXEC_ASSERT(ptr->l_);
239-
STDEXEC_ASSERT(self_.frames_ == ptr);
240-
ptr = ptr->prev_;
241-
self_.frames_->l_.unlock();
242-
self_.frames_->prev_ = nullptr;
243-
self_.frames_ = ptr;
238+
STDEXEC_ASSERT(self.frames_);
239+
STDEXEC_ASSERT(self.frames_->self_ == &self);
240+
auto const current = std::exchange(self.frames_, self.frames_->prev_);
241+
current->self_ = nullptr;
242+
current->prev_ = nullptr;
243+
self.m_.unlock();
244+
if (!self.frames_)
245+
{
246+
break;
247+
}
244248
}
245-
while (ptr);
249+
STDEXEC_ASSERT(!self_);
246250
}
247251
};
248252

0 commit comments

Comments
 (0)