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 <charconv>
8#include <deque>
9#include <exception>
10#include <mutex>
11#include <stdexcept>
12#include <system_error>
13#include <napi.h>
14#include <tuple>
15
16QT_BEGIN_NAMESPACE
17
18QOhosJsState::~QOhosJsState() = default;
19
20QOhosJsState::QOhosJsState() = default;
21
22QOhosJsThreadOps::~QOhosJsThreadOps() = default;
23
24QOhosJsThreadOps::QOhosJsThreadOps() = default;
25
27
28void QOhosJsThreadOps::registerInstance(QOhosJsThreadOps *ops)
29{
30 qOhosJsThreadOpsInstance = ops;
31}
32
33QOhosJsThreadOps &QOhosJsThreadOps::instance()
34{
35 Q_ASSERT(qOhosJsThreadOpsInstance != nullptr);
36 return *qOhosJsThreadOpsInstance;
37}
38
39void QOhosJsThreadGateway::invoke(std::function<void(QOhosJsState &)> &&task)
40{
41 QOhosJsThreadOps::instance().invoke(std::move(task));
42}
43
45 std::function<void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
46 std::string callerContextName)
47{
48 QOhosJsThreadOps::instance().invokeAndWaitForContinue(
49 std::move(task), std::move(callerContextName));
50}
51
53 const std::function<void(QOhosJsState &)> &task,
54 std::string callerContextName)
55{
56 QOhosJsThreadOps::instance().runAndWait(task, std::move(callerContextName));
57}
58
59namespace QtOhos {
60
61namespace {
62
63template<typename Task>
64class PreQueuingTasksExecutor
65{
66public:
67 void setUnderlyingExecutor(
68 std::function<void(Task)> executor,
69 std::function<void(Task)> optSyncFlushExecutor = nullptr);
70 void invokeTask(Task task);
71
72private:
73 std::recursive_mutex m_executorMutex;
74 std::function<void(Task)> m_optUnderlyingExecutor;
75 std::deque<Task> m_pendingTasks;
76};
77
78template<typename Task>
79void PreQueuingTasksExecutor<Task>::setUnderlyingExecutor(
80 std::function<void(Task)> executor, std::function<void(Task)> optSyncFlushExecutor)
81{
82 std::lock_guard<std::recursive_mutex> executorLock(m_executorMutex);
83 auto &flushExecutor = optSyncFlushExecutor ? optSyncFlushExecutor : executor;
84 while (!m_pendingTasks.empty()) {
85 auto task = std::move(m_pendingTasks.front());
86 m_pendingTasks.pop_front();
87 flushExecutor(std::move(task));
88 }
89 m_optUnderlyingExecutor = std::move(executor);
90}
91
92template<typename Task>
93void PreQueuingTasksExecutor<Task>::invokeTask(Task task)
94{
95 std::lock_guard<std::recursive_mutex> executorLock(m_executorMutex);
96 if (m_optUnderlyingExecutor)
97 m_optUnderlyingExecutor(std::move(task));
98 else
99 m_pendingTasks.push_back(std::move(task));
100}
101
102QOhosConsumer<std::function<void()>> makeQtThreadTasksExecutor()
103{
104 auto qobj = std::make_shared<QObject>();
105 return [qobj](std::function<void()> task) {
106 auto sharedTask = moveToSharedPtr(std::move(task));
107 QMetaObject::invokeMethod(
108 qobj.get(),
109 [sharedTask]() {
110 (*sharedTask)();
111 },
112 Qt::QueuedConnection);
113 };
114}
115
116class QtStateImpl : public QtState
117{
118public:
119 QtStateImpl() = default;
120
121 void initInQtThread();
122
123 bool isQtThread() const override;
124
125 void invokeTask(std::function<void()> &&task) override;
126
127private:
128 PreQueuingTasksExecutor<std::function<void()>> m_tasksExecutor;
129
130 QOhosMutexProtectedValue<pthread_t> m_qtThread;
131};
132
133void QtStateImpl::initInQtThread()
134{
135 m_qtThread.processValue(
136 [](auto &qtThread) {
137 qtThread = pthread_self();
138 });
139
140 m_tasksExecutor.setUnderlyingExecutor(
141 makeQtThreadTasksExecutor(),
142 [](auto task) {
143 task();
144 });
145}
146
147bool QtStateImpl::isQtThread() const
148{
149 return m_qtThread.evalWithValue(
150 [](const auto &qtThread) {
151 return qtThread == pthread_self();
152 });
153}
154
155void QtStateImpl::invokeTask(std::function<void()> &&task)
156{
157 m_tasksExecutor.invokeTask(std::move(task));
158}
159
160QtStateImpl &getQtStateImpl()
161{
162 static QtStateImpl qtStateImpl;
163 return qtStateImpl;
164}
165
166}
167
169
174
177{
178 using namespace std::string_literals;
179
180 if (obj.isNull())
181 return;
182
184
185 auto refIter = refsMap->find(obj);
186 if (refIter == refsMap->end()) {
187 auto refName =
188 std::to_string(reinterpret_cast<std::size_t>(obj.data()))
191
192 QObjectRef ref = {
193 .obj = obj,
194 .refName = refName,
195 };
197
200 [](QObject *obj) {
201 if (refsMap.isDestroyed())
202 return;
204 refsMap->erase(obj);
205 });
206 }
207
210}
211
213
215
217{
218 return m_refName == other.m_refName;
219}
220
222{
223 return !(*this == other);
224}
225
227{
228 return m_refName;
229}
230
232{
233 if (m_refName.empty())
234 return nullptr;
235
236 if (::pthread_equal(::pthread_self(), m_creatorThread) == 0) {
237 qOhosPrintfError("QObjectThreadSafeRef: accessing pointer from wrong thread");
238 return nullptr;
239 }
240
241 auto objRef = m_weakObjRef.lock();
242 return objRef ? objRef->obj : nullptr;
243}
244
255
256QtState::QtState() = default;
257
258QtState::~QtState() = default;
259
261{
262 getQtStateImpl().initInQtThread();
263}
264
266{
267 return getQtStateImpl();
268}
269
270void invokeInQtThread(std::function<void()> &&task)
271{
272 getQtStateImpl().invokeTask(
273 [sharedTask = takeFunctionToSharedPtr(task)]() {
274 const auto takenTask = std::exchange(*sharedTask, nullptr);
275 takenTask();
276 });
277}
278
279void logJsCallbackError(const QOhosCallbackInfo &cbInfo, const char *errorMessagePrefix)
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 errorInfo = tryExtractErrorInfoFromJsError(optCbArg);
287 auto optCodeStr = errorInfo.optCode
288 ? std::optional(std::to_string(*errorInfo.optCode))
289 : std::nullopt;
290
291 std::string errorDetailsStr;
292 auto appendErrorDetail = [&](const char *propName, const std::optional<std::string> &optPropStr) {
293 if (!optPropStr)
294 return;
295 if (!errorDetailsStr.empty())
296 errorDetailsStr += ", ";
297 errorDetailsStr += propName;
298 errorDetailsStr += "='";
299 errorDetailsStr += *optPropStr;
300 errorDetailsStr += "'";
301 };
302 appendErrorDetail("name", errorInfo.optName);
303 appendErrorDetail("message", errorInfo.optMessage);
304 appendErrorDetail("code", optCodeStr);
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
315namespace {
316
317std::optional<std::int64_t> tryMapJsErrorCodeToInt64(QNapi::Value optCodeProp)
318{
319 if (optCodeProp.IsNumber())
320 return QNapi::checkedCast<QNapi::Number>(optCodeProp).Int64Value();
321
322 if (!optCodeProp.IsString())
323 return std::nullopt;
324
325 std::string codeStr = optCodeProp.ToString();
326 const auto *codeStrEnd = codeStr.data() + codeStr.size();
327 std::int64_t code = 0;
328 auto parseResult = std::from_chars(codeStr.data(), codeStrEnd, code);
329
330 return parseResult.ec == std::errc() && parseResult.ptr == codeStrEnd
331 ? std::optional(code)
332 : std::nullopt;
333}
334
335}
336
338{
339 if (jsErrorOrEmpty.IsEmpty() || !jsErrorOrEmpty.IsObject())
340 return {};
341
342 Napi::HandleScope funcScope(jsErrorOrEmpty.Env());
343
344 auto optNameProp = QNapi::getPropOrUndefined(jsErrorOrEmpty, "name");
345 auto optMessageProp = QNapi::getPropOrUndefined(jsErrorOrEmpty, "message");
346 auto optCodeProp = QNapi::getPropOrUndefined(jsErrorOrEmpty, "code");
347
348 return QOhosJsErrorInfo {
349 .optName = optNameProp.IsString()
350 ? std::optional(optNameProp.ToString().Utf8Value())
351 : std::nullopt,
352 .optMessage = optMessageProp.IsString()
353 ? std::optional(optMessageProp.ToString().Utf8Value())
354 : std::nullopt,
355 .optCode = tryMapJsErrorCodeToInt64(optCodeProp),
356 };
357}
358
359std::function<void(const QOhosCallbackInfo &)> makeErrorLoggingJsCallback(std::string callContext)
360{
361 using namespace std::string_literals;
362
363 return [callContext = std::move(callContext)](const QOhosCallbackInfo &cbInfo) {
364 auto errorMessagePrefix = "Got error from '"s + callContext + "'"s;
365 QtOhos::logJsCallbackError(cbInfo, errorMessagePrefix.c_str());
366 };
367}
368
369}
370
371QOhosJsState &QOhosCallbackInfo::jsState() const
372{
373 return QOhosJsThreadOps::instance().jsState();
374}
375
376QT_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)
QOhosJsErrorInfo tryExtractErrorInfoFromJsError(QNapi::Value jsErrorOrEmpty)
void invokeInQtThread(std::function< void()> &&task)
static QOhosJsThreadOps * qOhosJsThreadOpsInstance