Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
async_node.hpp
Go to the documentation of this file.
1
3
4#pragma once
5
6#include "shared_storage.hpp"
8#include <uni20/config.hpp>
10
11#include <atomic>
12#include <cstddef>
13#include <mutex>
14#include <string>
15#include <string_view>
16#include <type_traits>
17#include <unordered_map>
18
19namespace uni20::async
20{
21
24{
25 Invalid,
28};
29
30namespace detail
31{
32
34void uni20_async_debug_value();
35
38template <typename T>
39concept HasAdlAsyncDebugValue = requires(T const& value) { std::string{uni20_async_debug_value(value)}; };
40
45template <typename Extents> std::string format_shape_debug_info(Extents const& extents)
46{
47 using extents_type = std::remove_cvref_t<Extents>;
48 std::string out = "shape=(";
49 for (std::size_t axis = 0; axis < static_cast<std::size_t>(extents_type::rank()); ++axis)
50 {
51 if (axis != 0) out += ", ";
52 out += std::to_string(static_cast<unsigned long long>(extents.extent(axis)));
53 }
54 out += ")";
55 return out;
56}
57
60template <typename T>
61concept HasMdspanExtents = requires(T const& value, std::size_t axis) {
62 std::remove_cvref_t<decltype(value.mdspan().extents())>::rank();
63 value.mdspan().extents().extent(axis);
64};
65
68template <typename T>
69concept HasExtents = requires(T const& value, std::size_t axis) {
70 std::remove_cvref_t<decltype(value.extents())>::rank();
71 value.extents().extent(axis);
72};
73
77inline std::string sanitize_debug_value(std::string value)
78{
79 for (char& ch : value)
80 {
81 if (ch == '\n' || ch == '\r' || ch == '\t') ch = ' ';
82 }
83 return value;
84}
85
90template <typename T> std::string scalar_debug_info(T const& value)
91{
92 using value_type = std::remove_cvref_t<T>;
93 if constexpr (std::is_same_v<value_type, bool>)
94 {
95 return value ? "true" : "false";
96 }
97 else if constexpr (std::is_same_v<value_type, char>)
98 {
99 return std::string{"'"} + value + "'";
100 }
101 else if constexpr (std::is_integral_v<value_type>)
102 {
103 if constexpr (std::is_signed_v<value_type>)
104 return std::to_string(static_cast<long long>(value));
105 else
106 return std::to_string(static_cast<unsigned long long>(value));
107 }
108 else if constexpr (uni20::RealOrComplex<value_type>)
109 {
111 }
112 else if constexpr (std::is_enum_v<value_type>)
113 {
114 using underlying_type = std::underlying_type_t<value_type>;
115 return scalar_debug_info(static_cast<underlying_type>(value));
116 }
117 else
118 {
119 return {};
120 }
121}
122
127template <typename T> std::string default_debug_value(T const& value)
128{
129 if constexpr (HasMdspanExtents<T>)
130 {
131 auto span = value.mdspan();
132 return format_shape_debug_info(span.extents());
133 }
134 else if constexpr (HasExtents<T>)
135 {
136 return format_shape_debug_info(value.extents());
137 }
138 else
139 {
140 return scalar_debug_info(value);
141 }
142}
143
148template <typename T> std::string debug_value_for_value(T const& value)
149{
150 if constexpr (HasAdlAsyncDebugValue<T>)
151 {
152 return sanitize_debug_value(std::string{uni20_async_debug_value(value)});
153 }
154 else
155 {
156 return sanitize_debug_value(default_debug_value(value));
157 }
158}
159
160} // namespace detail
161
171class NodeInfo {
172 public:
174 NodeInfo() = delete;
175
178 [[nodiscard]] void const* address() const
179 {
180 return value_address_observer_ ? value_address_observer_(storage_address_) : fallback_value_address_;
181 }
182
185 [[nodiscard]] void const* storage_address() const { return storage_address_; }
186
189 [[nodiscard]] NodeValueState value_state() const noexcept
190 {
191 if (value_constructed_observer_)
192 {
193 if (!storage_address_) return NodeValueState::Invalid;
194 return value_constructed_observer_(storage_address_) ? NodeValueState::Constructed
196 }
197 return fallback_value_address_ ? NodeValueState::Constructed : NodeValueState::Invalid;
198 }
199
202 [[nodiscard]] std::string_view value_state_label() const noexcept
203 {
204 switch (this->value_state())
205 {
207 return "invalid";
209 return "unconstructed";
211 return "constructed";
212 }
213 return "invalid";
214 }
215
218 [[nodiscard]] bool value_constructed() const noexcept { return this->value_state() == NodeValueState::Constructed; }
219
222 [[nodiscard]] std::string value_text() const
223 {
224 auto const state = this->value_state();
225 if (state != NodeValueState::Constructed) return {};
226 if (!value_text_observer_) return {};
227 auto const* context = value_constructed_observer_ ? storage_address_ : fallback_value_address_;
228 return value_text_observer_(context);
229 }
230
237 [[nodiscard]] std::string_view type() const
238 {
239 std::lock_guard<std::mutex> lock(map_mutex_);
240 auto [it, inserted] = map_.try_emplace(type_key_, uni20::demangle::demangle(type_key_));
241 return std::string_view(it->second);
242 }
243
247 [[nodiscard]] uint64_t global_index() const { return global_index_; }
248
250#if 0 && UNI20_HAS_STACKTRACE
251 std::stacktrace const& stack() const { return stack_; }
252#endif
253
261 // Keep the diagnostic-only allocation frame visible to sanitizer symbolizers.
262 // GCC's libbacktrace can omit inlined frames even with full debug information.
263 template <typename T> [[gnu::noinline]] static NodeInfo const* create(T const* value)
264 {
265 return new NodeInfo(value);
266 }
267
275 template <typename T> [[gnu::noinline]] static NodeInfo const* create(shared_storage<T> const& storage)
276 {
277 return new NodeInfo(storage);
278 }
279
280 private:
281 using ValueAddressObserver = void const* (*)(void const*) noexcept;
282 using ValueConstructedObserver = bool (*)(void const*) noexcept;
283 using ValueTextObserver = std::string (*)(void const*);
284
285 template <typename T> static std::string text_for_raw_value(void const* value)
286 {
287 return value ? detail::debug_value_for_value(*static_cast<T const*>(value)) : std::string{};
288 }
289
290 template <typename T> static std::string text_for_storage_value(void const* control)
291 {
292 auto const* value = static_cast<T const*>(shared_storage<T>::diagnostic_value_address_from_control(control));
293 return value ? detail::debug_value_for_value(*value) : std::string{};
294 }
295
296 template <typename T>
297 NodeInfo(T const* value)
298 : fallback_value_address_(static_cast<void const*>(value)), type_key_(typeid(T).name()),
299 value_text_observer_(&NodeInfo::text_for_raw_value<T>), global_index_(next_global_++)
300#if 0 && UNI20_HAS_STACKTRACE
301 ,
302 stack_(std::stacktrace::current())
303#endif
304 {}
305
306 template <typename T>
307 explicit NodeInfo(shared_storage<T> const& storage)
308 : storage_address_(storage.control_address()), fallback_value_address_(static_cast<void const*>(storage.get())),
309 type_key_(typeid(T).name()),
310 value_address_observer_(&shared_storage<T>::diagnostic_value_address_from_control),
311 value_constructed_observer_(&shared_storage<T>::diagnostic_constructed_from_control),
312 value_text_observer_(&NodeInfo::text_for_storage_value<T>), global_index_(next_global_++)
313#if 0 && UNI20_HAS_STACKTRACE
314 ,
315 stack_(std::stacktrace::current())
316#endif
317 {}
318
319 // global data for the map of mangled name to demangled name (protected by map_mutex_), and the global index
320 inline static std::unordered_map<const char*, std::string> map_;
321 inline static std::mutex map_mutex_;
322 inline static std::atomic<uint64_t> next_global_ = 0;
323
324 // Per-instance data:
325 void const* storage_address_{nullptr};
326 void const* fallback_value_address_{nullptr};
327 char const* type_key_;
328 ValueAddressObserver value_address_observer_{};
329 ValueConstructedObserver value_constructed_observer_{};
330 ValueTextObserver value_text_observer_{};
331 uint64_t global_index_;
332#if 0 && UNI20_HAS_STACKTRACE
333 std::stacktrace stack_;
334#endif
335};
336
337} // namespace uni20::async
Records node information for DAG/debugging visualization.
Definition async_node.hpp:171
std::string value_text() const
Returns type-specific display text for the current value.
Definition async_node.hpp:222
std::string_view type() const
Returns a human-readable, demangled type name.
Definition async_node.hpp:237
void const * storage_address() const
Returns the raw address of the shared storage control block.
Definition async_node.hpp:185
NodeValueState value_state() const noexcept
Reports the current construction state when observable.
Definition async_node.hpp:189
std::string_view value_state_label() const noexcept
Returns a stable label for the current construction state.
Definition async_node.hpp:202
NodeInfo()=delete
Deleted default constructor. NodeInfo can only be constructed via create.
bool value_constructed() const noexcept
Reports whether the value is currently constructed when observable.
Definition async_node.hpp:218
uint64_t global_index() const
Returns the unique global index of this node.
Definition async_node.hpp:247
static NodeInfo const * create(T const *value)
Returns the stacktrace of where the node was created.
Definition async_node.hpp:263
static NodeInfo const * create(shared_storage< T > const &storage)
Factory method to create a NodeInfo for shared async storage.
Definition async_node.hpp:275
void const * address() const
Returns the raw address of the current constructed value when observable.
Definition async_node.hpp:178
A lightweight, thread-safe, reference-counted storage for a single object.
Definition shared_storage.hpp:94
Concept for types that are either real or complex.
Definition scalar_concepts.hpp:87
locale_t value
Definition data_table.cpp:49
std::string format_scalar(T const &value, scalar_format_options const &options={})
Format any Uni20 real or complex scalar.
Definition scalar_io.hpp:367
Definition qnum.hpp:403
This represents the reverse-mode adjoint for functions of complex variables, assuming a real-valued s...
Definition async/async.hpp:26
NodeValueState
Construction state reported for a debug DAG value node.
Definition async_node.hpp:24
@ Invalid
No storage or fallback value is available.
@ Constructed
A value is currently constructed.
@ Unconstructed
Storage exists but does not currently contain a value.
std::string demangle(const char *name)
Turn a compiler‐mangled name into a human‐readable one.
Definition demangle.hpp:22
typename optional_traits< T >::value_type value_type
Definition data_table.hpp:42
std::size_t span
Definition presentation.cpp:2017
Reference-counted optional in-place storage used by async buffers.