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
qohossinglethreadexecutor.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 <QtCore/private/qohoslogger_p.h>
5#include <condition_variable>
6#include <cstring>
7#include <memory>
8#include <mutex>
9#include <pthread.h>
10#include <qohossinglethreadexecutor.h>
11#include <queue>
12
13QT_BEGIN_NAMESPACE
14
15namespace QtOhos {
16
17namespace {
18
19std::shared_ptr<::pthread_attr_t> makePthreadAttr()
20{
21 auto threadAttrStorage = std::make_shared<::pthread_attr_t>();
22 auto threadAttr = std::shared_ptr<::pthread_attr_t>(
23 threadAttrStorage.get(),
24 [threadAttrStorage](::pthread_attr_t *attr) {
25 ::pthread_attr_destroy(attr);
26 });
27
28 int initResult = ::pthread_attr_init(threadAttr.get());
29 if (initResult != 0)
30 qOhosReportFatalErrorAndAbort("pthread_attr_init() failed: %s", std::strerror(initResult));
31
32 return threadAttr;
33}
34
35std::shared_ptr<void> startNewThread(
36 std::function<void()> threadFunction, const ::pthread_attr_t &threadAttributes)
37{
38 struct Context
39 {
40 std::function<void()> threadFunction;
41 std::optional<::pthread_t> optThreadId;
42 };
43
44 auto context = std::make_shared<Context>();
45 context->threadFunction = std::move(threadFunction);
46
47 auto threadHandle = makeDestroyNotifier(
48 [context]() {
49 if (context->optThreadId.has_value())
50 ::pthread_join(context->optThreadId.value(), nullptr);
51 });
52
53 auto pthreadStartRoutineFunc = [](void *arg) -> void * {
54 auto *context = static_cast<Context *>(arg);
55 context->threadFunction();
56 return nullptr;
57 };
58
59 ::pthread_t threadId;
60 int createResult = ::pthread_create(&threadId, &threadAttributes, pthreadStartRoutineFunc, context.get());
61 if (createResult != 0) {
62 qOhosReportFatalErrorAndAbort(
63 "%s: pthread_create() failed: %s", Q_FUNC_INFO, std::strerror(createResult));
64 }
65
66 context->optThreadId = threadId;
67
68 return threadHandle;
69}
70
71std::shared_ptr<::pthread_attr_t> createSingleThreadExecutorThreadAttributes(
72 const SingleThreadExecutorConfig &config)
73{
74 auto threadAttributes = makePthreadAttr();
75
76 if (config.threadPreferredStackSize.has_value()) {
77 int setStackSizeResult = ::pthread_attr_setstacksize(
78 threadAttributes.get(), config.threadPreferredStackSize.value());
79 if (setStackSizeResult != 0) {
80 qOhosPrintfWarning(
81 "%s: pthread_attr_setstacksize() failed: %s",
82 Q_FUNC_INFO, std::strerror(setStackSizeResult));
83 }
84 }
85
86 return threadAttributes;
87}
88
89class SingleThreadExecutor
90{
91public:
92 SingleThreadExecutor(const SingleThreadExecutorConfig &config);
93
94 ~SingleThreadExecutor();
95
96 void enqueueTask(std::function<void()> task);
97
98private:
99 std::shared_ptr<void> m_workerThreadHandle;
100 std::mutex m_tasksQueueMutex;
101 std::queue<std::function<void()>> m_tasksQueue;
102 std::condition_variable m_tasksQueueNonEmptyCv;
103};
104
105SingleThreadExecutor::SingleThreadExecutor(const SingleThreadExecutorConfig &config)
106{
107 m_workerThreadHandle = startNewThread(
108 [this]() {
109 while (true) {
110 std::function<void()> task;
111
112 {
113 std::unique_lock<std::mutex> tasksQueueLock(m_tasksQueueMutex);
114 m_tasksQueueNonEmptyCv.wait(
115 tasksQueueLock,
116 [&]() {
117 return !m_tasksQueue.empty();
118 });
119
120 task = std::move(m_tasksQueue.front());
121 m_tasksQueue.pop();
122 }
123
124 if (!task)
125 break;
126
127 task();
128 }
129 },
130 *createSingleThreadExecutorThreadAttributes(config));
131}
132
133SingleThreadExecutor::~SingleThreadExecutor()
134{
135 enqueueTask({});
136 m_workerThreadHandle.reset();
137}
138
139void SingleThreadExecutor::enqueueTask(std::function<void()> task)
140{
141 std::lock_guard<std::mutex> tasksQueueLock(m_tasksQueueMutex);
142 m_tasksQueue.push(std::move(task));
143 m_tasksQueueNonEmptyCv.notify_one();
144}
145
146}
147
149{
150 auto executor = std::make_shared<SingleThreadExecutor>(config);
151 return [executor](std::function<void()> task) {
152 executor->enqueueTask(std::move(task));
153 };
154}
155
156}
157
158QT_END_NAMESPACE
QOhosConsumer< std::function< void()> > makeSingleThreadExecutor(const SingleThreadExecutorConfig &config={})