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
qcore_ohos.cpp
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#include "qcore_ohos_p.h"
5#include <QtCore/qmetaobject.h>
6#include <QtCore/private/qohoslogger_p.h>
7#include <deque>
8#include <exception>
9#include <mutex>
10#include <stdexcept>
11#include <napi.h>
12#include <tuple>
13#include <utility>
14
15QT_BEGIN_NAMESPACE
16
17QOhosJsState::~QOhosJsState() = default;
18
19QOhosJsState::QOhosJsState() = default;
20
21QOhosJsThreadOps::~QOhosJsThreadOps() = default;
22
23QOhosJsThreadOps::QOhosJsThreadOps() = default;
24
26
27void QOhosJsThreadOps::registerInstance(QOhosJsThreadOps *ops)
28{
29 qOhosJsThreadOpsInstance = ops;
30}
31
32QOhosJsThreadOps &QOhosJsThreadOps::instance()
33{
34 Q_ASSERT(qOhosJsThreadOpsInstance != nullptr);
35 return *qOhosJsThreadOpsInstance;
36}
37
38void QOhosJsThreadGateway::invoke(std::function<void(QOhosJsState &)> task)
39{
40 QOhosJsThreadOps::instance().invoke(std::move(task));
41}
42
44 std::function<void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
45 std::string callerContextName)
46{
47 QOhosJsThreadOps::instance().invokeAndWaitForContinue(
48 std::move(task), std::move(callerContextName));
49}
50
52 const std::function<void(QOhosJsState &)> &task,
53 std::string callerContextName)
54{
55 QOhosJsThreadOps::instance().runAndWait(task, std::move(callerContextName));
56}
57
58namespace QtOhos {
59
60namespace {
61
62template<typename Task>
63class PreQueuingTasksExecutor
64{
65public:
66 void setUnderlyingExecutor(
67 std::function<void(Task)> executor,
68 std::function<void(Task)> optSyncFlushExecutor = nullptr);
69 void invokeTask(Task task);
70
71private:
72 std::recursive_mutex m_executorMutex;
73 std::function<void(Task)> m_optUnderlyingExecutor;
74 std::deque<Task> m_pendingTasks;
75};
76
77template<typename Task>
78void PreQueuingTasksExecutor<Task>::setUnderlyingExecutor(
79 std::function<void(Task)> executor, std::function<void(Task)> optSyncFlushExecutor)
80{
81 std::lock_guard<std::recursive_mutex> executorLock(m_executorMutex);
82 auto &flushExecutor = optSyncFlushExecutor ? optSyncFlushExecutor : executor;
83 while (!m_pendingTasks.empty()) {
84 auto task = std::move(m_pendingTasks.front());
85 m_pendingTasks.pop_front();
86 flushExecutor(std::move(task));
87 }
88 m_optUnderlyingExecutor = std::move(executor);
89}
90
91template<typename Task>
92void PreQueuingTasksExecutor<Task>::invokeTask(Task task)
93{
94 std::lock_guard<std::recursive_mutex> executorLock(m_executorMutex);
95 if (m_optUnderlyingExecutor)
96 m_optUnderlyingExecutor(std::move(task));
97 else
98 m_pendingTasks.push_back(std::move(task));
99}
100
101QOhosConsumer<std::function<void()>> makeQtThreadTasksExecutor()
102{
103 auto qobj = std::make_shared<QObject>();
104 return [qobj](std::function<void()> task) {
105 auto sharedTask = moveToSharedPtr(std::move(task));
106 QMetaObject::invokeMethod(
107 qobj.get(),
108 [sharedTask]() {
109 (*sharedTask)();
110 },
111 Qt::QueuedConnection);
112 };
113}
114
115class QtStateImpl : public QtState
116{
117public:
118 QtStateImpl() = default;
119
120 void initInQtThread();
121
122 bool isQtThread() const override;
123
124 void invokeTask(std::function<void()> &&task) override;
125
126private:
127 PreQueuingTasksExecutor<std::function<void()>> m_tasksExecutor;
128
129 QOhosMutexProtectedValue<pthread_t> m_qtThread;
130};
131
132void QtStateImpl::initInQtThread()
133{
134 m_qtThread.processValue(
135 [](auto &qtThread) {
136 qtThread = pthread_self();
137 });
138
139 m_tasksExecutor.setUnderlyingExecutor(
140 makeQtThreadTasksExecutor(),
141 [](auto task) {
142 task();
143 });
144}
145
146bool QtStateImpl::isQtThread() const
147{
148 return m_qtThread.evalWithValue(
149 [](const auto &qtThread) {
150 return qtThread == pthread_self();
151 });
152}
153
154void QtStateImpl::invokeTask(std::function<void()> &&task)
155{
156 m_tasksExecutor.invokeTask(std::move(task));
157}
158
159QtStateImpl &getQtStateImpl()
160{
161 static QtStateImpl qtStateImpl;
162 return qtStateImpl;
163}
164
165}
166
168
173
176{
177 using namespace std::string_literals;
178
179 if (obj.isNull())
180 return;
181
183
184 auto refIter = refsMap->find(obj);
185 if (refIter == refsMap->end()) {
186 auto refName =
187 std::to_string(reinterpret_cast<std::size_t>(obj.data()))
190
191 QObjectRef ref = {
192 .obj = obj,
193 .refName = refName,
194 };
196
199 [](QObject *obj) {
200 if (refsMap.isDestroyed())
201 return;
203 refsMap->erase(obj);
204 });
205 }
206
209}
210
212
214
216{
217 return m_refName == other.m_refName;
218}
219
221{
222 return !(*this == other);
223}
224
226{
227 return m_refName;
228}
229
231{
232 if (m_refName.empty())
233 return nullptr;
234
235 if (::pthread_equal(::pthread_self(), m_creatorThread) == 0) {
236 qOhosPrintfError("QObjectThreadSafeRef: accessing pointer from wrong thread");
237 return nullptr;
238 }
239
240 auto objRef = m_weakObjRef.lock();
241 return objRef ? objRef->obj : nullptr;
242}
243
245{
247 [objRef = *this, visitFunc = std::move(visitFunc)]() {
249 if (!obj.isNull())
250 visitFunc(*obj);
251 });
252}
253
254QtState::QtState() = default;
255
256QtState::~QtState() = default;
257
259{
260 getQtStateImpl().initInQtThread();
261}
262
264{
265 return getQtStateImpl();
266}
267
268void invokeInQtThread(std::function<void()> task)
269{
270 getQtStateImpl().invokeTask(std::move(task));
271}
272
273void logJsCallbackError(const QOhosCallbackInfo &cbInfo, const char *errorMessagePrefix)
274{
275 static const std::pair<const char *, bool (QNapi::Value::*)() const> errorPropsDefs[] = {
276 {"name", &QNapi::Value::IsString},
277 {"message", &QNapi::Value::IsString},
278 {"code", &QNapi::Value::IsNumber},
279 };
280
281 Napi::HandleScope funcScope(cbInfo.Env());
282
283 auto optCbArg = cbInfo.Length() != 0
284 ? cbInfo.getFirstArg<QNapi::Value>(Q_FUNC_INFO)
285 : cbInfo.Env().Undefined();
286 auto error = optCbArg.IsObject()
287 ? QNapi::checkedCast<QNapi::Object>(optCbArg)
288 : QNapi::Object::New(cbInfo.Env());
289
290 std::string errorDetailsStr;
291 for (const auto &propDef : errorPropsDefs) {
292 const auto *propName = propDef.first;
293 const auto &typeCheckMemFun = propDef.second;
294
295 auto optProp = QNapi::getPropOrUndefined(error, propName);
296 if ((optProp.*typeCheckMemFun)()) {
297 std::string propStr = optProp.ToString();
298 if (!errorDetailsStr.empty())
299 errorDetailsStr += ", ";
300 errorDetailsStr += propName;
301 errorDetailsStr += "='";
302 errorDetailsStr += propStr;
303 errorDetailsStr += "'";
304 }
305 }
306
307 std::string errorStr = errorMessagePrefix;
308 if (!errorDetailsStr.empty()) {
309 errorStr += ": ";
310 errorStr += errorDetailsStr;
311 }
312
313 qOhosPrintfError("%s", errorStr.c_str());
314}
315
316std::function<void(const QOhosCallbackInfo &)> makeErrorLoggingJsCallback(std::string callContext)
317{
318 using namespace std::string_literals;
319
320 return [callContext = std::move(callContext)](const QOhosCallbackInfo &cbInfo) {
321 auto errorMessagePrefix = "Got error from '"s + callContext + "'"s;
322 QtOhos::logJsCallbackError(cbInfo, errorMessagePrefix.c_str());
323 };
324}
325
326}
327
328QOhosJsState &QOhosCallbackInfo::jsState() const
329{
330 return QOhosJsThreadOps::instance().jsState();
331}
332
333QT_END_NAMESPACE
virtual ~QtState()
Q_CORE_EXPORT void invoke(std::function< void(QOhosJsState &)> task)
Q_CORE_EXPORT void runAndWait(const std::function< void(QOhosJsState &)> &task, std::string callerContextName={})
Q_CORE_EXPORT void invokeAndWaitForContinue(std::function< void(QOhosJsState &, QOhosTaskPromise<>)> &&task, std::string callerContextName={})
void invokeInQtThread(std::function< void()> task)
void initQtThreadState()
void logJsCallbackError(const QOhosCallbackInfo &cbInfo, const char *errorMessagePrefix)
QtState & getQtState()
std::function< void(const QOhosCallbackInfo &)> makeErrorLoggingJsCallback(std::string callContext)
static QOhosJsThreadOps * qOhosJsThreadOpsInstance