Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohoscommon_p.h
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QOHOSCOMMON_H
5#define QOHOSCOMMON_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/private/qohoslogger_p.h>
19#include <QtCore/qglobal.h>
20#include <QtCore/qlogging.h>
21#include <QtCore/qdebug.h>
22#include <array>
23#include <cstdlib>
24#include <functional>
25#include <memory>
26#include <mutex>
27#include <string>
28#ifndef QT_NO_EXCEPTIONS
29#include <stdexcept>
30#endif
31#include <type_traits>
32#include <utility>
33
34#define Q_OHOS_NAMED_FUNC(func) (QT_PREPEND_NAMESPACE(makeQOhosNamedFunc)<decltype(func)*, func>)(QT_STRINGIFY(func))
35
36#define qOhosReportFatalErrorAndAbort(...)
37 do {
38 QT_PREPEND_NAMESPACE(QMessageLogger)(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).fatal(__VA_ARGS__);
39 std::abort();
40 } while (false)
41
42QT_BEGIN_NAMESPACE
43
44template<typename Func, typename... FuncArgs>
45using QOhosInvokeResult = decltype(std::declval<Func>()(std::declval<FuncArgs>()...));
46
47template<typename ...Ts>
48using QOhosConsumer = std::function<void(Ts...)>;
49
50template<typename T>
51using QOhosSupplier = std::function<T()>;
52
53template<typename... Args>
55{
56public:
57 template<typename F, typename = std::enable_if_t<!std::is_same<std::decay_t<F>, QOhosTaskPromise>::value>>
59 F &&callable, std::function<void()> optOnDestroyedWithoutCall,
60 std::string callerContextName = {});
61
63
66
69
70 void operator()(Args... args) const;
71
72 QOhosTaskPromise makeChained(std::string callerContextName) &&;
73
74 template<typename... ContextNames>
75 std::array<QOhosTaskPromise, sizeof...(ContextNames)> makeBranched(ContextNames &&...branchContextNames) &&;
76
77 std::pair<QOhosTaskPromise, QOhosTaskPromise> makeThenCatchBranches(std::string callerContextName) &&;
78
79 operator std::function<void(Args...)>() &&;
80
81private:
82 class Callable
83 {
84 public:
85 virtual ~Callable() = default;
86 virtual void call(Args &&...args) = 0;
87 };
88
89 template<typename Func>
90 class CallableImpl : public Callable
91 {
92 public:
93 template<typename F>
94 explicit CallableImpl(F &&f);
95
96 void call(Args &&...args) override;
97
98 private:
99 Func m_func;
100 };
101
102 struct TrackingUsageState
103 {
104 TrackingUsageState(
105 std::size_t activeCount,
106 std::shared_ptr<std::function<void()>> onDestroyedWithoutCall);
107
108 std::shared_ptr<std::function<void()>> onDestroyedWithoutCall;
109 bool used = false;
110 std::size_t activeCount = 1;
111 };
112
113 struct TrackingNode
114 {
115 TrackingNode(
116 std::string callerContext, std::shared_ptr<TrackingNode> optParent,
117 std::shared_ptr<TrackingUsageState> usage);
118
119 void markUsed(const char *callerContextString);
120 std::string rootCallerContextOrUnknown() const;
121 void logCallerContextChainAsErrors(const char *messagePrefix) const;
122
123 std::string callerContext;
124 std::shared_ptr<TrackingNode> optParent;
125 std::shared_ptr<TrackingUsageState> usage;
126 };
127
128 QOhosTaskPromise(std::shared_ptr<Callable> callable, std::shared_ptr<TrackingNode> tracking);
129
130 std::shared_ptr<Callable> m_callable;
131 std::shared_ptr<TrackingNode> m_tracking;
132};
133
134template<typename Func, Func func>
136{
137public:
138 static constexpr Func funcPtr = func;
139
140 QOhosNamedFunc(const char *funcName);
141
142 const char *name() const;
143 Func ptr() const;
144
145 operator Func () const;
146
147private:
148 static_assert(std::is_function_v<std::remove_pointer_t<Func>>);
149
150 const char *m_funcName;
151};
152
153template<typename Func, Func func>
155{
156 return {funcName};
157}
158
160{
161public:
162 template<typename ...Ts>
163 void operator()(const Ts &...) const;
164};
165
166template<typename ...Ts>
168
170
171template<typename T>
173{
174public:
176
179
182
183 template<typename ProcessFunc>
184 void processValue(ProcessFunc &&processFunc);
185
186 template<typename EvalFunc>
187 auto evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval<const T &>()));
188
189private:
190 mutable std::mutex m_valueMutex;
191 T m_value{};
192};
193
194namespace QtOhos {
195
196template<typename T>
197std::shared_ptr<T> moveToSharedPtr(T &&obj);
198
199template<typename T>
201 std::shared_ptr<T> baseSharedPtr, std::shared_ptr<void> extraData);
202
203template<typename T>
204std::weak_ptr<T> makeWeakPtr(const std::shared_ptr<T> &obj);
205
206std::shared_ptr<void> makeDestroyNotifier(std::function<void()> callOnDestroy);
207
208}
209
210template<typename Func, Func func>
211QOhosNamedFunc<Func, func>::QOhosNamedFunc(const char *funcName)
212 : m_funcName(funcName)
213{
214}
215
216template<typename Func, Func func>
217const char *QOhosNamedFunc<Func, func>::name() const
218{
219 return m_funcName;
220}
221
222template<typename Func, Func func>
223Func QOhosNamedFunc<Func, func>::ptr() const
224{
225 return func;
226}
227
228template<typename Func, Func func>
229QOhosNamedFunc<Func, func>::operator Func () const
230{
231 return func;
232}
233
234template<typename ...Ts>
235void QOhosNoOpConsumer::operator()(const Ts &...) const
236{
237}
238
239template<typename ...Ts>
241{
242 return [](const Ts &...) {
243 };
244}
245
250
251template<typename T>
253
254template<typename T>
255template<typename ProcessFunc>
256void QOhosMutexProtectedValue<T>::processValue(ProcessFunc &&processFunc)
257{
258 std::lock_guard<std::mutex> valueLock(m_valueMutex);
259 std::forward<ProcessFunc>(processFunc)(m_value);
260}
261
262template<typename T>
263template<typename EvalFunc>
264auto QOhosMutexProtectedValue<T>::evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval<const T &>()))
265{
266 std::lock_guard<std::mutex> valueLock(m_valueMutex);
267 return std::forward<EvalFunc>(evalFunc)(m_value);
268}
269
270namespace QtOhos {
271
272template<typename T>
273std::shared_ptr<T> moveToSharedPtr(T &&obj)
274{
275 return std::make_shared<T>(std::forward<T>(obj));
276}
277
278template<typename T>
280 std::shared_ptr<T> baseSharedPtr, std::shared_ptr<void> extraData)
281{
282 auto *baseRawPtr = baseSharedPtr.get();
283 return std::shared_ptr<T>(
284 moveToSharedPtr(
285 std::make_pair(
286 std::move(baseSharedPtr),
287 std::move(extraData))),
288 baseRawPtr);
289}
290
291template<typename T>
292std::weak_ptr<T> makeWeakPtr(const std::shared_ptr<T> &obj)
293{
294 return obj;
295}
296
297inline std::shared_ptr<void> makeDestroyNotifier(std::function<void()> callOnDestroy)
298{
299 class DestroyNotifier
300 {
301 public:
302 explicit DestroyNotifier(std::function<void()> callOnDestroy)
303 : callOnDestroy(std::move(callOnDestroy))
304 {
305 }
306
307 DestroyNotifier(const DestroyNotifier &) = delete;
308 DestroyNotifier(DestroyNotifier &&) = delete;
309 DestroyNotifier &operator=(const DestroyNotifier &) = delete;
310 DestroyNotifier &operator=(DestroyNotifier &&) = delete;
311
312 ~DestroyNotifier()
313 {
314 callOnDestroy();
315 };
316
317 private:
318 std::function<void()> callOnDestroy;
319 };
320
321 return std::make_shared<DestroyNotifier>(std::move(callOnDestroy));
322}
323
324}
325
326template<typename... Args>
327template<typename F, typename>
329 F &&callable, std::function<void()> optOnDestroyedWithoutCall, std::string callerContextName)
331 , m_tracking(
332 std::make_shared<TrackingNode>(
333 std::move(callerContextName), nullptr,
334 std::make_shared<TrackingUsageState>(
335 1,
336 QtOhos::moveToSharedPtr(
337 optOnDestroyedWithoutCall
338 ? std::move(optOnDestroyedWithoutCall)
339 : makeQOhosNoOpConsumer<>()))))
340{
341}
342
343template<typename... Args>
345{
346 if (!m_tracking || m_tracking->usage->used)
347 return;
348
349 --m_tracking->usage->activeCount;
350
351 if (m_tracking->usage->activeCount == 0) {
352 m_tracking->logCallerContextChainAsErrors(Q_FUNC_INFO);
353 qOhosPrintfError(
354 "%s: promise destroyed without notifying the caller: %s",
355 Q_FUNC_INFO, m_tracking->rootCallerContextOrUnknown().c_str());
356 (*m_tracking->usage->onDestroyedWithoutCall)();
357 }
358}
359
360template<typename... Args>
361QOhosTaskPromise<Args...>::QOhosTaskPromise(
362 std::shared_ptr<Callable> callable, std::shared_ptr<TrackingNode> tracking)
363 : m_callable(std::move(callable))
364 , m_tracking(std::move(tracking))
365{
366}
367
368template<typename... Args>
369void QOhosTaskPromise<Args...>::operator()(Args... args) const
370{
371 if (!m_tracking)
372 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
373
374 if (!m_callable)
375 qOhosReportFatalErrorAndAbort("%s: called on empty QOhosTaskPromise", Q_FUNC_INFO);
376
377 m_tracking->markUsed(Q_FUNC_INFO);
378
379 m_callable->call(std::forward<Args>(args)...);
380}
381
382template<typename... Args>
383QOhosTaskPromise<Args...>::operator std::function<void(Args...)>() &&
384{
385 const auto funcInfo = Q_FUNC_INFO;
386
387 if (!m_tracking)
388 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", funcInfo);
389
390 if (!m_callable)
391 return {};
392
393 m_tracking->markUsed(funcInfo);
394
395 auto callable = std::move(m_callable);
396 auto tracking = std::move(m_tracking);
397
398 auto sharedCalledFlag = std::make_shared<bool>(false);
399 auto destroyNotifier = QtOhos::makeDestroyNotifier(
400 [sharedCalledFlag, tracking, funcInfo]() {
401 if (!*sharedCalledFlag) {
402 tracking->logCallerContextChainAsErrors(funcInfo);
403 qOhosPrintfError(
404 "%s: promise (as std::function) destroyed without notifying the caller: %s",
405 funcInfo, tracking->rootCallerContextOrUnknown().c_str());
406 (*tracking->usage->onDestroyedWithoutCall)();
407 }
408 });
409
410 return [callable, sharedCalledFlag, tracking, funcInfo, destroyNotifier](Args... args) {
411 if (*sharedCalledFlag) {
413 "%s: promise (as std::function) called more than once for: %s",
414 funcInfo, tracking->rootCallerContextOrUnknown().c_str());
415 }
416 *sharedCalledFlag = true;
417 callable->call(std::forward<Args>(args)...);
418 };
419}
420
421template<typename... Args>
422QOhosTaskPromise<Args...> QOhosTaskPromise<Args...>::makeChained(std::string callerContextName) &&
423{
424 if (!m_tracking)
425 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
426
427 m_tracking->markUsed(Q_FUNC_INFO);
428
429 auto newUsage = std::make_shared<TrackingUsageState>(1, m_tracking->usage->onDestroyedWithoutCall);
430
431 return QOhosTaskPromise(
432 std::move(m_callable),
433 std::make_shared<TrackingNode>(
434 std::move(callerContextName), std::move(m_tracking), newUsage));
435}
436
437template<typename... Args>
438template<typename... ContextNames>
439std::array<QOhosTaskPromise<Args...>, sizeof...(ContextNames)>
440QOhosTaskPromise<Args...>::makeBranched(ContextNames &&...branchContextNames) &&
441{
442 if (!m_tracking)
443 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
444
445 m_tracking->markUsed(Q_FUNC_INFO);
446
447 auto sharedUsage = std::make_shared<TrackingUsageState>(
448 sizeof...(ContextNames), m_tracking->usage->onDestroyedWithoutCall);
449 return {{
451 m_callable,
452 std::make_shared<TrackingNode>(
453 std::forward<ContextNames>(branchContextNames),
454 m_tracking, sharedUsage))...
455 }};
456}
457
458template<typename... Args>
459std::pair<QOhosTaskPromise<Args...>, QOhosTaskPromise<Args...>>
460QOhosTaskPromise<Args...>::makeThenCatchBranches(std::string callerContextName) &&
461{
462 auto branches = std::move(*this).makeChained(std::move(callerContextName)).makeBranched("then", "catch");
463 return {std::move(branches[0]), std::move(branches[1])};
464}
465
466template<typename... Args>
467template<typename Func>
468template<typename F>
469QOhosTaskPromise<Args...>::CallableImpl<Func>::CallableImpl(F &&f)
470 : m_func(std::forward<F>(f))
471{
472}
473
474template<typename... Args>
475template<typename Func>
476void QOhosTaskPromise<Args...>::CallableImpl<Func>::call(Args &&...args)
477{
478 m_func(std::forward<Args>(args)...);
479}
480
481template<typename... Args>
482QOhosTaskPromise<Args...>::TrackingUsageState::TrackingUsageState(
483 std::size_t activeCount, std::shared_ptr<std::function<void()>> onDestroyedWithoutCall)
484 : onDestroyedWithoutCall(onDestroyedWithoutCall)
485 , activeCount(activeCount)
486{
487}
488
489template<typename... Args>
490QOhosTaskPromise<Args...>::TrackingNode::TrackingNode(
491 std::string callerContext, std::shared_ptr<TrackingNode> optParent,
492 std::shared_ptr<TrackingUsageState> usage)
493 : callerContext(std::move(callerContext))
494 , optParent(std::move(optParent))
495 , usage(std::move(usage))
496{
497}
498
499template<typename... Args>
500void QOhosTaskPromise<Args...>::TrackingNode::markUsed(const char *callerContextString)
501{
502 if (usage->used) {
503 logCallerContextChainAsErrors(callerContextString);
505 "%s: using a dead promise branch (%s) from this caller: %s",
506 callerContextString, callerContext.c_str(), rootCallerContextOrUnknown().c_str());
507 }
508 usage->used = true;
509}
510
511template<typename... Args>
512std::string QOhosTaskPromise<Args...>::TrackingNode::rootCallerContextOrUnknown() const
513{
514 const TrackingNode *rootContextNode = nullptr;
515 for (auto currentNode = this; currentNode != nullptr; currentNode = currentNode->optParent.get()) {
516 if (!currentNode->callerContext.empty())
517 rootContextNode = currentNode;
518 }
519 return rootContextNode != nullptr ? rootContextNode->callerContext : "<unknown>";
520}
521
522template<typename... Args>
523void QOhosTaskPromise<Args...>::TrackingNode::logCallerContextChainAsErrors(const char *messagePrefix) const
524{
525 int index = 0;
526 for (auto currentNode = this; currentNode; currentNode = currentNode->optParent.get()) {
527 if (!currentNode->callerContext.empty()) {
528 qOhosPrintfError(
529 "%s [caller context #%d]: %s",
530 messagePrefix, index, currentNode->callerContext.c_str());
531 ++index;
532 }
533 }
534}
535
536QT_END_NAMESPACE
537
538#endif
auto evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval< const T & >()))
QOhosMutexProtectedValue & operator=(const QOhosMutexProtectedValue< T > &other)=delete
void processValue(ProcessFunc &&processFunc)
QOhosMutexProtectedValue & operator=(QOhosMutexProtectedValue< T > &&other)=delete
QOhosMutexProtectedValue(const QOhosMutexProtectedValue< T > &other)=delete
QOhosMutexProtectedValue(QOhosMutexProtectedValue< T > &&other)=delete
operator Func() const
Func ptr() const
QOhosNamedFunc(const char *funcName)
const char * name() const
static constexpr Func funcPtr
void operator()(const Ts &...) const
std::array< QOhosTaskPromise, sizeof...(ContextNames)> makeBranched(ContextNames &&...branchContextNames) &&
void operator()(Args... args) const
QOhosTaskPromise(F &&callable, std::function< void()> optOnDestroyedWithoutCall, std::string callerContextName={})
QOhosTaskPromise makeChained(std::string callerContextName) &&
operator std::function< void(Args...)>() &&
QOhosTaskPromise & operator=(const QOhosTaskPromise &)=delete
QOhosTaskPromise & operator=(QOhosTaskPromise &&)=default
std::pair< QOhosTaskPromise, QOhosTaskPromise > makeThenCatchBranches(std::string callerContextName) &&
QOhosTaskPromise(const QOhosTaskPromise &)=delete
QOhosTaskPromise(QOhosTaskPromise &&)=default
std::shared_ptr< T > makeSharedPtrWithAttachedExtraData(std::shared_ptr< T > baseSharedPtr, std::shared_ptr< void > extraData)
std::shared_ptr< T > moveToSharedPtr(T &&obj)
std::weak_ptr< T > makeWeakPtr(const std::shared_ptr< T > &obj)
std::shared_ptr< void > makeDestroyNotifier(std::function< void()> callOnDestroy)
#define qOhosReportFatalErrorAndAbort(...)
QOhosNoOpConsumer makeQOhosNoOpConsumer()
std::function< T()> QOhosSupplier
std::function< void(Ts...)> QOhosConsumer
QOhosConsumer< Ts... > makeQOhosNoOpConsumer()
QOhosNamedFunc< Func, func > makeQOhosNamedFunc(const char *funcName)