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
33#define Q_OHOS_NAMED_FUNC(func) (QT_PREPEND_NAMESPACE(makeQOhosNamedFunc)<decltype(func)*, func>)(QT_STRINGIFY(func))
34
35#define qOhosReportFatalErrorAndAbort(...)
36 do {
37 QT_PREPEND_NAMESPACE(QMessageLogger)(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).fatal(__VA_ARGS__);
38 std::abort();
39 } while (false)
40
41QT_BEGIN_NAMESPACE
42
43template<typename Func, typename... FuncArgs>
44using QOhosInvokeResult = decltype(std::declval<Func>()(std::declval<FuncArgs>()...));
45
46template<typename ...Ts>
47using QOhosConsumer = std::function<void(Ts...)>;
48
49template<typename T>
50using QOhosSupplier = std::function<T()>;
51
52template<typename... Args>
54{
55public:
56 template<typename F, typename = std::enable_if_t<!std::is_same<std::decay_t<F>, QOhosTaskPromise>::value>>
58 F &&callable, std::function<void()> optOnDestroyedWithoutCall,
59 std::string callerContextName = {});
60
62
65
68
69 void operator()(Args... args) const;
70
71 QOhosTaskPromise makeChained(std::string callerContextName) &&;
72
73 template<typename... ContextNames>
74 std::array<QOhosTaskPromise, sizeof...(ContextNames)> makeBranched(ContextNames &&...branchContextNames) &&;
75
76 std::pair<QOhosTaskPromise, QOhosTaskPromise> makeThenCatchBranches(std::string callerContextName) &&;
77
78 operator std::function<void(Args...)>() &&;
79
80private:
81 class Callable
82 {
83 public:
84 virtual ~Callable() = default;
85 virtual void call(Args &&...args) = 0;
86 };
87
88 template<typename Func>
89 class CallableImpl : public Callable
90 {
91 public:
92 template<typename F>
93 explicit CallableImpl(F &&f);
94
95 void call(Args &&...args) override;
96
97 private:
98 Func m_func;
99 };
100
101 struct TrackingUsageState
102 {
103 TrackingUsageState(
104 std::size_t activeCount,
105 std::shared_ptr<std::function<void()>> onDestroyedWithoutCall);
106
107 std::shared_ptr<std::function<void()>> onDestroyedWithoutCall;
108 bool used = false;
109 std::size_t activeCount = 1;
110 };
111
112 struct TrackingNode
113 {
114 TrackingNode(
115 std::string callerContext, std::shared_ptr<TrackingNode> optParent,
116 std::shared_ptr<TrackingUsageState> usage);
117
118 void markUsed(const char *callerContextString);
119 std::string rootCallerContextOrUnknown() const;
120 void logCallerContextChainAsErrors(const char *messagePrefix) const;
121
122 std::string callerContext;
123 std::shared_ptr<TrackingNode> optParent;
124 std::shared_ptr<TrackingUsageState> usage;
125 };
126
127 QOhosTaskPromise(std::shared_ptr<Callable> callable, std::shared_ptr<TrackingNode> tracking);
128
129 std::shared_ptr<Callable> m_callable;
130 std::shared_ptr<TrackingNode> m_tracking;
131};
132
133template<typename Func, Func func>
135{
136public:
137 static constexpr Func funcPtr = func;
138
139 QOhosNamedFunc(const char *funcName);
140
141 const char *name() const;
142 Func ptr() const;
143
144 operator Func () const;
145
146private:
147 static_assert(std::is_function_v<std::remove_pointer_t<Func>>);
148
149 const char *m_funcName;
150};
151
152template<typename Func, Func func>
154{
155 return {funcName};
156}
157
159{
160public:
161 template<typename ...Ts>
162 void operator()(const Ts &...) const;
163};
164
165template<typename ...Ts>
167
169
170template<typename T>
172{
173public:
175
178
181
182 template<typename ProcessFunc>
183 void processValue(ProcessFunc &&processFunc);
184
185 template<typename EvalFunc>
186 auto evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval<const T &>()));
187
188private:
189 mutable std::mutex m_valueMutex;
190 T m_value{};
191};
192
193namespace QtOhos {
194
195template<typename T>
196std::shared_ptr<T> moveToSharedPtr(T &&obj);
197
198template<typename T>
200 std::shared_ptr<T> baseSharedPtr, std::shared_ptr<void> extraData);
201
202template<typename T>
203std::weak_ptr<T> makeWeakPtr(const std::shared_ptr<T> &obj);
204
205std::shared_ptr<void> makeDestroyNotifier(std::function<void()> callOnDestroy);
206
207}
208
209template<typename Func, Func func>
210QOhosNamedFunc<Func, func>::QOhosNamedFunc(const char *funcName)
211 : m_funcName(funcName)
212{
213}
214
215template<typename Func, Func func>
216const char *QOhosNamedFunc<Func, func>::name() const
217{
218 return m_funcName;
219}
220
221template<typename Func, Func func>
222Func QOhosNamedFunc<Func, func>::ptr() const
223{
224 return func;
225}
226
227template<typename Func, Func func>
228QOhosNamedFunc<Func, func>::operator Func () const
229{
230 return func;
231}
232
233template<typename ...Ts>
234void QOhosNoOpConsumer::operator()(const Ts &...) const
235{
236}
237
238template<typename ...Ts>
240{
241 return [](const Ts &...) {
242 };
243}
244
249
250template<typename T>
252
253template<typename T>
254template<typename ProcessFunc>
255void QOhosMutexProtectedValue<T>::processValue(ProcessFunc &&processFunc)
256{
257 std::lock_guard<std::mutex> valueLock(m_valueMutex);
258 std::forward<ProcessFunc>(processFunc)(m_value);
259}
260
261template<typename T>
262template<typename EvalFunc>
263auto QOhosMutexProtectedValue<T>::evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval<const T &>()))
264{
265 std::lock_guard<std::mutex> valueLock(m_valueMutex);
266 return std::forward<EvalFunc>(evalFunc)(m_value);
267}
268
269namespace QtOhos {
270
271template<typename T>
272std::shared_ptr<T> moveToSharedPtr(T &&obj)
273{
274 return std::make_shared<T>(std::forward<T>(obj));
275}
276
277template<typename T>
279 std::shared_ptr<T> baseSharedPtr, std::shared_ptr<void> extraData)
280{
281 auto *baseRawPtr = baseSharedPtr.get();
282 return std::shared_ptr<T>(
283 moveToSharedPtr(
284 std::make_pair(
285 std::move(baseSharedPtr),
286 std::move(extraData))),
287 baseRawPtr);
288}
289
290template<typename T>
291std::weak_ptr<T> makeWeakPtr(const std::shared_ptr<T> &obj)
292{
293 return obj;
294}
295
296inline std::shared_ptr<void> makeDestroyNotifier(std::function<void()> callOnDestroy)
297{
298 class DestroyNotifier
299 {
300 public:
301 explicit DestroyNotifier(std::function<void()> callOnDestroy)
302 : callOnDestroy(std::move(callOnDestroy))
303 {
304 }
305
306 DestroyNotifier(const DestroyNotifier &) = delete;
307 DestroyNotifier(DestroyNotifier &&) = delete;
308 DestroyNotifier &operator=(const DestroyNotifier &) = delete;
309 DestroyNotifier &operator=(DestroyNotifier &&) = delete;
310
311 ~DestroyNotifier()
312 {
313 callOnDestroy();
314 };
315
316 private:
317 std::function<void()> callOnDestroy;
318 };
319
320 return std::make_shared<DestroyNotifier>(std::move(callOnDestroy));
321}
322
323}
324
325template<typename... Args>
326template<typename F, typename>
328 F &&callable, std::function<void()> optOnDestroyedWithoutCall, std::string callerContextName)
330 , m_tracking(
331 std::make_shared<TrackingNode>(
332 std::move(callerContextName), nullptr,
333 std::make_shared<TrackingUsageState>(
334 1,
335 QtOhos::moveToSharedPtr(
336 optOnDestroyedWithoutCall
337 ? std::move(optOnDestroyedWithoutCall)
338 : makeQOhosNoOpConsumer<>()))))
339{
340}
341
342template<typename... Args>
344{
345 if (!m_tracking || m_tracking->usage->used)
346 return;
347
348 --m_tracking->usage->activeCount;
349
350 if (m_tracking->usage->activeCount == 0) {
351 m_tracking->logCallerContextChainAsErrors(Q_FUNC_INFO);
352 qOhosPrintfError(
353 "%s: promise destroyed without notifying the caller: %s",
354 Q_FUNC_INFO, m_tracking->rootCallerContextOrUnknown().c_str());
355 (*m_tracking->usage->onDestroyedWithoutCall)();
356 }
357}
358
359template<typename... Args>
360QOhosTaskPromise<Args...>::QOhosTaskPromise(
361 std::shared_ptr<Callable> callable, std::shared_ptr<TrackingNode> tracking)
362 : m_callable(std::move(callable))
363 , m_tracking(std::move(tracking))
364{
365}
366
367template<typename... Args>
368void QOhosTaskPromise<Args...>::operator()(Args... args) const
369{
370 if (!m_tracking)
371 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
372
373 if (!m_callable)
374 qOhosReportFatalErrorAndAbort("%s: called on empty QOhosTaskPromise", Q_FUNC_INFO);
375
376 m_tracking->markUsed(Q_FUNC_INFO);
377
378 m_callable->call(std::forward<Args>(args)...);
379}
380
381template<typename... Args>
382QOhosTaskPromise<Args...>::operator std::function<void(Args...)>() &&
383{
384 const auto funcInfo = Q_FUNC_INFO;
385
386 if (!m_tracking)
387 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", funcInfo);
388
389 if (!m_callable)
390 return {};
391
392 m_tracking->markUsed(funcInfo);
393
394 auto callable = std::move(m_callable);
395 auto tracking = std::move(m_tracking);
396
397 auto sharedCalledFlag = std::make_shared<bool>(false);
398 auto destroyNotifier = QtOhos::makeDestroyNotifier(
399 [sharedCalledFlag, tracking, funcInfo]() {
400 if (!*sharedCalledFlag) {
401 tracking->logCallerContextChainAsErrors(funcInfo);
402 qOhosPrintfError(
403 "%s: promise (as std::function) destroyed without notifying the caller: %s",
404 funcInfo, tracking->rootCallerContextOrUnknown().c_str());
405 (*tracking->usage->onDestroyedWithoutCall)();
406 }
407 });
408
409 return [callable, sharedCalledFlag, tracking, funcInfo, destroyNotifier](Args... args) {
410 if (*sharedCalledFlag) {
412 "%s: promise (as std::function) called more than once for: %s",
413 funcInfo, tracking->rootCallerContextOrUnknown().c_str());
414 }
415 *sharedCalledFlag = true;
416 callable->call(std::forward<Args>(args)...);
417 };
418}
419
420template<typename... Args>
421QOhosTaskPromise<Args...> QOhosTaskPromise<Args...>::makeChained(std::string callerContextName) &&
422{
423 if (!m_tracking)
424 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
425
426 m_tracking->markUsed(Q_FUNC_INFO);
427
428 auto newUsage = std::make_shared<TrackingUsageState>(1, m_tracking->usage->onDestroyedWithoutCall);
429
430 return QOhosTaskPromise(
431 std::move(m_callable),
432 std::make_shared<TrackingNode>(
433 std::move(callerContextName), std::move(m_tracking), newUsage));
434}
435
436template<typename... Args>
437template<typename... ContextNames>
438std::array<QOhosTaskPromise<Args...>, sizeof...(ContextNames)>
439QOhosTaskPromise<Args...>::makeBranched(ContextNames &&...branchContextNames) &&
440{
441 if (!m_tracking)
442 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
443
444 m_tracking->markUsed(Q_FUNC_INFO);
445
446 auto sharedUsage = std::make_shared<TrackingUsageState>(
447 sizeof...(ContextNames), m_tracking->usage->onDestroyedWithoutCall);
448 return {{
450 m_callable,
451 std::make_shared<TrackingNode>(
452 std::forward<ContextNames>(branchContextNames),
453 m_tracking, sharedUsage))...
454 }};
455}
456
457template<typename... Args>
458std::pair<QOhosTaskPromise<Args...>, QOhosTaskPromise<Args...>>
459QOhosTaskPromise<Args...>::makeThenCatchBranches(std::string callerContextName) &&
460{
461 auto branches = std::move(*this).makeChained(std::move(callerContextName)).makeBranched("then", "catch");
462 return {std::move(branches[0]), std::move(branches[1])};
463}
464
465template<typename... Args>
466template<typename Func>
467template<typename F>
468QOhosTaskPromise<Args...>::CallableImpl<Func>::CallableImpl(F &&f)
469 : m_func(std::forward<F>(f))
470{
471}
472
473template<typename... Args>
474template<typename Func>
475void QOhosTaskPromise<Args...>::CallableImpl<Func>::call(Args &&...args)
476{
477 m_func(std::forward<Args>(args)...);
478}
479
480template<typename... Args>
481QOhosTaskPromise<Args...>::TrackingUsageState::TrackingUsageState(
482 std::size_t activeCount, std::shared_ptr<std::function<void()>> onDestroyedWithoutCall)
483 : onDestroyedWithoutCall(onDestroyedWithoutCall)
484 , activeCount(activeCount)
485{
486}
487
488template<typename... Args>
489QOhosTaskPromise<Args...>::TrackingNode::TrackingNode(
490 std::string callerContext, std::shared_ptr<TrackingNode> optParent,
491 std::shared_ptr<TrackingUsageState> usage)
492 : callerContext(std::move(callerContext))
493 , optParent(std::move(optParent))
494 , usage(std::move(usage))
495{
496}
497
498template<typename... Args>
499void QOhosTaskPromise<Args...>::TrackingNode::markUsed(const char *callerContextString)
500{
501 if (usage->used) {
502 logCallerContextChainAsErrors(callerContextString);
504 "%s: using a dead promise branch (%s) from this caller: %s",
505 callerContextString, callerContext.c_str(), rootCallerContextOrUnknown().c_str());
506 }
507 usage->used = true;
508}
509
510template<typename... Args>
511std::string QOhosTaskPromise<Args...>::TrackingNode::rootCallerContextOrUnknown() const
512{
513 const TrackingNode *rootContextNode = nullptr;
514 for (auto currentNode = this; currentNode != nullptr; currentNode = currentNode->optParent.get()) {
515 if (!currentNode->callerContext.empty())
516 rootContextNode = currentNode;
517 }
518 return rootContextNode != nullptr ? rootContextNode->callerContext : "<unknown>";
519}
520
521template<typename... Args>
522void QOhosTaskPromise<Args...>::TrackingNode::logCallerContextChainAsErrors(const char *messagePrefix) const
523{
524 int index = 0;
525 for (auto currentNode = this; currentNode; currentNode = currentNode->optParent.get()) {
526 if (!currentNode->callerContext.empty()) {
527 qOhosPrintfError(
528 "%s [caller context #%d]: %s",
529 messagePrefix, index, currentNode->callerContext.c_str());
530 ++index;
531 }
532 }
533}
534
535QT_END_NAMESPACE
536
537#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)