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
244{
246 [objRef = *this, visitFunc = std::move(visitFunc)]() {
248 if (!obj.isNull())
249 visitFunc(*obj);
250 });
251}
252
253QtState::QtState() = default;
254
255QtState::~QtState() = default;
256
258{
259 getQtStateImpl().initInQtThread();
260}
261
263{
264 return getQtStateImpl();
265}
266
267void invokeInQtThread(std::function<void()> task)
268{
269 getQtStateImpl().invokeTask(std::move(task));
270}
271
272void logJsCallbackError(const QOhosCallbackInfo &cbInfo, const char *errorMessagePrefix)
273{
274 static const std::pair<const char *, bool (QNapi::Value::*)() const> errorPropsDefs[] = {
275 {"name", &QNapi::Value::IsString},
276 {"message", &QNapi::Value::IsString},
277 {"code", &QNapi::Value::IsNumber},
278 };
279
280 Napi::HandleScope funcScope(cbInfo.Env());
281
282 auto optCbArg = cbInfo.Length() != 0
283 ? cbInfo.getFirstArg<QNapi::Value>(Q_FUNC_INFO)
284 : cbInfo.Env().Undefined();
285 auto error = optCbArg.IsObject()
286 ? QNapi::checkedCast<QNapi::Object>(optCbArg)
287 : QNapi::Object::New(cbInfo.Env());
288
289 std::string errorDetailsStr;
290 for (const auto &propDef : errorPropsDefs) {
291 const auto *propName = propDef.first;
292 const auto &typeCheckMemFun = propDef.second;
293
294 auto optProp = QNapi::getPropOrUndefined(error, propName);
295 if ((optProp.*typeCheckMemFun)()) {
296 std::string propStr = optProp.ToString();
297 if (!errorDetailsStr.empty())
298 errorDetailsStr += ", ";
299 errorDetailsStr += propName;
300 errorDetailsStr += "='";
301 errorDetailsStr += propStr;
302 errorDetailsStr += "'";
303 }
304 }
305
306 std::string errorStr = errorMessagePrefix;
307 if (!errorDetailsStr.empty()) {
308 errorStr += ": ";
309 errorStr += errorDetailsStr;
310 }
311
312 qOhosPrintfError("%s", errorStr.c_str());
313}
314
315std::function<void(const QOhosCallbackInfo &)> makeErrorLoggingJsCallback(std::string callContext)
316{
317 using namespace std::string_literals;
318
319 return [callContext = std::move(callContext)](const QOhosCallbackInfo &cbInfo) {
320 auto errorMessagePrefix = "Got error from '"s + callContext + "'"s;
321 QtOhos::logJsCallbackError(cbInfo, errorMessagePrefix.c_str());
322 };
323}
324
325}
326
327QOhosJsState &QOhosCallbackInfo::jsState() const
328{
329 return QOhosJsThreadOps::instance().jsState();
330}
331
332QT_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