Skip to content

Commit 4ed2411

Browse files
committed
add debug_async_scope
`v1::debug_async_scope` and `v2::debug_async_scope` wrap the corresponding `async_scope`s and can be used as a direct replacement for debugging purposes. They keep track of nested operation states, allowing to identify the 'stuck' operations especially in case of a deadlock.
1 parent 1067303 commit 4ed2411

File tree

5 files changed

+538
-0
lines changed

5 files changed

+538
-0
lines changed

.lldbinit

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
type summary add --summary-string "Open: ${var.opState_._M_i[0]%B} Ops: ${var.opState_._M_i[1-10]%u}" unifex::v2::_async_scope::async_scope
2+
type summary add --summary-string "${var.demangled}" unifex::detail::_debug_async_scope::op_base
+253
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
5+
* (the "License"); you may not use this file except in compliance with
6+
* the License. You may obtain a copy of the License at
7+
*
8+
* https://llvm.org/LICENSE.txt
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#pragma once
17+
18+
#include <unifex/detail/intrusive_list.hpp>
19+
#if __has_include(<cxxabi.h>)
20+
# define UNIFEX_NO_DEMANGLE 0
21+
# include <cxxabi.h>
22+
#else
23+
// TODO
24+
// https://learn.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-undecoratesymbolname?redirectedfrom=MSDN
25+
# define UNIFEX_NO_DEMANGLE 1
26+
#endif
27+
#include <mutex>
28+
#include <typeinfo>
29+
30+
#include <unifex/detail/prologue.hpp>
31+
32+
namespace unifex::detail {
33+
namespace _debug_async_scope {
34+
35+
struct op_base {
36+
explicit op_base(const std::type_info& concreteType) noexcept
37+
: concreteType(concreteType) {
38+
demangled = demangle(concreteType);
39+
}
40+
41+
const std::type_info& concreteType;
42+
const char* demangled;
43+
bool shouldFree{false};
44+
45+
const char* demangle(const std::type_info& concreteType) noexcept {
46+
#if !UNIFEX_NO_DEMANGLE
47+
int status = -1;
48+
auto result = abi::__cxa_demangle(concreteType.name(), nullptr, 0, &status);
49+
if (status == 0) {
50+
shouldFree = true;
51+
return result;
52+
}
53+
return concreteType.name();
54+
#else
55+
return concreteType.name();
56+
#endif
57+
}
58+
59+
op_base* next{nullptr};
60+
op_base* prev{nullptr};
61+
62+
~op_base() {
63+
if (shouldFree) {
64+
std::free((void*)demangled);
65+
}
66+
}
67+
};
68+
69+
using debug_ops_t = intrusive_list<op_base, &op_base::next, &op_base::prev>;
70+
71+
struct debug_op_list final {
72+
std::mutex mutex_;
73+
debug_ops_t ops_;
74+
75+
void register_debug_operation(op_base* op) noexcept {
76+
std::lock_guard lock{mutex_};
77+
ops_.push_back(op);
78+
}
79+
80+
void deregister_debug_operation(op_base* op) noexcept {
81+
std::lock_guard lock{mutex_};
82+
ops_.remove(op);
83+
}
84+
};
85+
86+
template <typename Receiver>
87+
struct _operation final {
88+
struct type;
89+
};
90+
91+
template <typename Receiver>
92+
struct _operation<Receiver>::type : op_base {
93+
template <typename Receiver2>
94+
explicit type(
95+
const std::type_info& t,
96+
debug_op_list* ops,
97+
Receiver2&& receiver) noexcept(std::
98+
is_nothrow_constructible_v<
99+
Receiver,
100+
Receiver2>)
101+
: op_base{t}
102+
, ops_(ops)
103+
, receiver_(static_cast<Receiver2&&>(receiver)) {}
104+
105+
type(type&&) = delete;
106+
107+
debug_op_list* ops_;
108+
UNIFEX_NO_UNIQUE_ADDRESS Receiver receiver_;
109+
110+
template <typename Func>
111+
void complete(Func func) noexcept {
112+
ops_->deregister_debug_operation(this);
113+
func(std::move(receiver_));
114+
}
115+
};
116+
117+
template <typename Receiver>
118+
struct _receiver final {
119+
struct type;
120+
};
121+
122+
template <typename Receiver>
123+
struct _receiver<Receiver>::type final {
124+
typename _operation<Receiver>::type* op_;
125+
126+
template <typename... Values>
127+
void set_value(Values&&... values) noexcept {
128+
op_->complete([&](Receiver&& receiver) noexcept {
129+
UNIFEX_TRY {
130+
unifex::set_value(
131+
std::move(receiver), static_cast<Values&&>(values)...);
132+
}
133+
UNIFEX_CATCH(...) {
134+
unifex::set_error(std::move(receiver), std::current_exception());
135+
}
136+
});
137+
}
138+
139+
template <typename E>
140+
void set_error(E&& e) noexcept {
141+
op_->complete([&e](Receiver&& receiver) noexcept {
142+
unifex::set_error(std::move(receiver), static_cast<E&&>(e));
143+
});
144+
}
145+
146+
void set_done() noexcept { op_->complete(unifex::set_done); }
147+
};
148+
149+
template <typename Sender, typename Receiver>
150+
struct _operation_impl final {
151+
struct type;
152+
};
153+
154+
template <typename Sender, typename Receiver>
155+
using debug_op =
156+
typename _operation_impl<Sender, remove_cvref_t<Receiver>>::type;
157+
158+
template <typename Sender, typename Receiver>
159+
struct _operation_impl<Sender, Receiver>::type final
160+
: _operation<Receiver>::type {
161+
using base_op_t = typename _operation<Receiver>::type;
162+
using receiver_t = typename _receiver<Receiver>::type;
163+
164+
template <typename Sender2, typename Receiver2>
165+
explicit type(
166+
debug_op_list* ops,
167+
Sender2&& sender,
168+
Receiver2&& receiver) noexcept(std::
169+
is_nothrow_constructible_v<
170+
base_op_t,
171+
const std::type_info&,
172+
debug_op_list*,
173+
Receiver2>&&
174+
is_nothrow_connectable_v<
175+
Sender2,
176+
receiver_t>)
177+
: base_op_t(
178+
typeid(connect_result_t<Sender, Receiver>),
179+
ops,
180+
static_cast<Receiver2&&>(receiver))
181+
, op_(unifex::connect(static_cast<Sender2&&>(sender), receiver_t{this})) {}
182+
183+
type(type&&) = delete;
184+
185+
friend void tag_invoke(tag_t<start>, type& op) noexcept {
186+
op.ops_->register_debug_operation(&op);
187+
unifex::start(op.op_);
188+
}
189+
190+
private:
191+
using op_t = connect_result_t<Sender, receiver_t>;
192+
op_t op_;
193+
};
194+
195+
template <typename Sender>
196+
struct _debug_scope_sender final {
197+
struct type;
198+
};
199+
200+
template <typename Sender>
201+
struct _debug_scope_sender<Sender>::type final {
202+
template <
203+
template <typename...>
204+
typename Variant,
205+
template <typename...>
206+
typename Tuple>
207+
using value_types = sender_value_types_t<Sender, Variant, Tuple>;
208+
209+
template <template <typename...> typename Variant>
210+
using error_types = typename concat_type_lists_unique_t<
211+
sender_error_types_t<Sender, type_list>,
212+
type_list<std::exception_ptr>>::template apply<Variant>;
213+
214+
static constexpr bool sends_done = sender_traits<Sender>::sends_done;
215+
216+
template <typename Sender2>
217+
explicit type(Sender2&& sender, debug_op_list* ops) noexcept(
218+
std::is_nothrow_constructible_v<Sender, Sender2>)
219+
: ops_(ops)
220+
, sender_(static_cast<Sender2&&>(sender)) {}
221+
222+
template(typename Self, typename Receiver) //
223+
(requires same_as<type, remove_cvref_t<Self>>) //
224+
friend debug_op<Sender, Receiver> tag_invoke(
225+
tag_t<connect>,
226+
Self&& self,
227+
Receiver&& receiver) noexcept(std::
228+
is_nothrow_constructible_v<
229+
debug_op<Sender, Receiver>,
230+
debug_op_list*,
231+
member_t<Self, Sender>,
232+
Receiver>) {
233+
return debug_op<Sender, Receiver>{
234+
static_cast<Self&&>(self).ops_,
235+
static_cast<Self&&>(self).sender_,
236+
static_cast<Receiver&&>(receiver)};
237+
}
238+
239+
private:
240+
debug_op_list* ops_;
241+
UNIFEX_NO_UNIQUE_ADDRESS Sender sender_;
242+
};
243+
244+
} // namespace _debug_async_scope
245+
246+
using _debug_async_scope::debug_op_list;
247+
template <typename Sender>
248+
using debug_scope_sender =
249+
typename _debug_async_scope::_debug_scope_sender<Sender>::type;
250+
251+
} // namespace unifex::detail
252+
253+
#include <unifex/detail/epilogue.hpp>

0 commit comments

Comments
 (0)