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