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