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