63template<
typename Task>
64class PreQueuingTasksExecutor
67 void setUnderlyingExecutor(
68 std::function<
void(Task)> executor,
69 std::function<
void(Task)> optSyncFlushExecutor =
nullptr);
70 void invokeTask(Task task);
73 std::recursive_mutex m_executorMutex;
74 std::function<
void(Task)> m_optUnderlyingExecutor;
75 std::deque<Task> m_pendingTasks;
78template<
typename Task>
79void PreQueuingTasksExecutor<Task>::setUnderlyingExecutor(
80 std::function<
void(Task)> executor,
std::function<
void(Task)> optSyncFlushExecutor)
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));
89 m_optUnderlyingExecutor =
std::move(executor);
92template<
typename Task>
93void PreQueuingTasksExecutor<Task>::invokeTask(Task task)
95 std::lock_guard<
std::recursive_mutex> executorLock(m_executorMutex);
96 if (m_optUnderlyingExecutor)
97 m_optUnderlyingExecutor(
std::move(task));
99 m_pendingTasks.push_back(std::move(task));
102QOhosConsumer<std::function<
void()>> makeQtThreadTasksExecutor()
104 auto qobj = std::make_shared<QObject>();
105 return [qobj](
std::function<
void()> task) {
106 auto sharedTask = moveToSharedPtr(
std::move(task));
107 QMetaObject::invokeMethod(
112 Qt::QueuedConnection);
116class QtStateImpl :
public QtState
119 QtStateImpl() =
default;
121 void initInQtThread();
123 bool isQtThread()
const override;
125 void invokeTask(
std::function<
void()> &&task)
override;
128 PreQueuingTasksExecutor<
std::function<
void()>> m_tasksExecutor;
130 QOhosMutexProtectedValue<pthread_t> m_qtThread;
133void QtStateImpl::initInQtThread()
135 m_qtThread.processValue(
137 qtThread = pthread_self();
140 m_tasksExecutor.setUnderlyingExecutor(
141 makeQtThreadTasksExecutor(),
147bool QtStateImpl::isQtThread()
const
149 return m_qtThread.evalWithValue(
150 [](
const auto &qtThread) {
151 return qtThread == pthread_self();
155void QtStateImpl::invokeTask(
std::function<
void()> &&task)
157 m_tasksExecutor.invokeTask(std::move(task));
160QtStateImpl &getQtStateImpl()
162 static QtStateImpl qtStateImpl;
223 return !(*
this ==
other);
237 qOhosPrintfError(
"QObjectThreadSafeRef: accessing pointer from wrong thread");
262 getQtStateImpl().initInQtThread();
267 return getQtStateImpl();
272 getQtStateImpl().invokeTask(
273 [sharedTask = takeFunctionToSharedPtr(task)]() {
274 const auto takenTask = std::exchange(*sharedTask,
nullptr);
281 Napi::HandleScope funcScope(cbInfo.Env());
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))
291 std::string errorDetailsStr;
292 auto appendErrorDetail = [&](
const char *propName,
const std::optional<
std::string> &optPropStr) {
295 if (!errorDetailsStr.empty())
296 errorDetailsStr +=
", ";
297 errorDetailsStr += propName;
298 errorDetailsStr +=
"='";
299 errorDetailsStr += *optPropStr;
300 errorDetailsStr +=
"'";
302 appendErrorDetail(
"name", errorInfo.optName);
303 appendErrorDetail(
"message", errorInfo.optMessage);
304 appendErrorDetail(
"code", optCodeStr);
306 std::string errorStr = errorMessagePrefix;
307 if (!errorDetailsStr.empty()) {
309 errorStr += errorDetailsStr;
312 qOhosPrintfError(
"%s", errorStr.c_str());
317std::optional<
std::int64_t> tryMapJsErrorCodeToInt64(QNapi::Value optCodeProp)
319 if (optCodeProp.IsNumber())
320 return QNapi::checkedCast<QNapi::Number>(optCodeProp).Int64Value();
322 if (!optCodeProp.IsString())
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);
330 return parseResult.ec ==
std::errc() && parseResult.ptr == codeStrEnd
331 ? std::optional(code)
339 if (jsErrorOrEmpty.IsEmpty() || !jsErrorOrEmpty.IsObject())
342 Napi::HandleScope funcScope(jsErrorOrEmpty.Env());
344 auto optNameProp = QNapi::getPropOrUndefined(jsErrorOrEmpty,
"name");
345 auto optMessageProp = QNapi::getPropOrUndefined(jsErrorOrEmpty,
"message");
346 auto optCodeProp = QNapi::getPropOrUndefined(jsErrorOrEmpty,
"code");
348 return QOhosJsErrorInfo {
349 .optName = optNameProp.IsString()
350 ? std::optional(optNameProp.ToString().Utf8Value())
352 .optMessage = optMessageProp.IsString()
353 ? std::optional(optMessageProp.ToString().Utf8Value())
355 .optCode = tryMapJsErrorCodeToInt64(optCodeProp),
361 using namespace std::string_literals;
363 return [callContext = std::move(callContext)](
const QOhosCallbackInfo &cbInfo) {
364 auto errorMessagePrefix =
"Got error from '"s + callContext +
"'"s;