Skip to content

Commit 4ca66a7

Browse files
committed
Add (untested) event Qt thread pool.
1 parent 5b7dd5d commit 4ca66a7

6 files changed

Lines changed: 157 additions & 5 deletions

File tree

Common/Qt/QtThreadPool.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,64 @@ void QtWorkerThreadPool::run_and_wait(std::function<void()> lambda){
124124

125125

126126

127+
128+
129+
130+
131+
132+
133+
QtEventThreadPool::~QtEventThreadPool(){
134+
stop();
135+
}
136+
void QtEventThreadPool::stop(){
137+
m_threads.clear();
138+
m_available_threads.clear();
139+
}
140+
141+
142+
143+
144+
145+
146+
QObject* QtEventThreadPool::add_object(std::function<std::unique_ptr<QObject>()> factory){
147+
QtEventThread& thread = get_thread();
148+
QObject* ret = thread.add_object(std::move(factory));
149+
try{
150+
m_objects[ret] = &thread;
151+
}catch (...){
152+
thread.remove_object();
153+
std::lock_guard<Mutex> lg(m_lock);
154+
m_available_threads.emplace_back(&thread);
155+
throw;
156+
}
157+
return ret;
158+
}
159+
void QtEventThreadPool::remove_object(QObject* object) noexcept{
160+
std::lock_guard<Mutex> lg(m_lock);
161+
auto iter = m_objects.find(object);
162+
if (iter == m_objects.end()){
163+
return;
164+
}
165+
iter->second->remove_object();
166+
m_available_threads.emplace_back(iter->second);
167+
m_objects.erase(iter);
168+
}
169+
170+
QtEventThread& QtEventThreadPool::get_thread(){
171+
std::lock_guard<Mutex> lg(m_lock);
172+
if (m_available_threads.empty()){
173+
m_available_threads.reserve(m_threads.size() + 1);
174+
auto& new_thread = m_threads.emplace_back();
175+
m_available_threads.emplace_back(&new_thread);
176+
}
177+
QtEventThread* ret = m_available_threads.back();
178+
m_available_threads.pop_back();
179+
return *ret;
180+
}
181+
182+
183+
184+
185+
186+
127187
}

Common/Qt/QtThreadPool.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#define PokemonAutomation_QtThreadPool_H
99

1010
#include <vector>
11+
#include <deque>
12+
#include <map>
1113
#include <QThread>
1214
#include "Common/Cpp/Concurrency/Mutex.h"
1315
#include "Common/Cpp/Concurrency/ConditionVariable.h"
@@ -16,6 +18,7 @@ namespace PokemonAutomation{
1618

1719

1820
class QtWorkerThread;
21+
class QtEventThread;
1922

2023

2124

@@ -42,6 +45,72 @@ class QtWorkerThreadPool{
4245

4346

4447

48+
class QtEventThread : public QThread{
49+
Q_OBJECT
50+
51+
public:
52+
QtEventThread(){
53+
start();
54+
}
55+
~QtEventThread(){
56+
quit();
57+
wait();
58+
}
59+
60+
QObject* add_object(std::function<std::unique_ptr<QObject>()> factory){
61+
m_pending_factory = std::move(factory);
62+
emit add_object_internal();
63+
std::unique_lock<Mutex> lg(m_lock);
64+
m_cv.wait(lg, [this]{ return m_pending_factory == nullptr; });
65+
return m_object.get();
66+
}
67+
void remove_object(){
68+
emit remove_object_internal();
69+
std::unique_lock<Mutex> lg(m_lock);
70+
m_cv.wait(lg, [this]{ return m_object == nullptr; });
71+
}
72+
73+
public slots:
74+
void add_object_internal(){
75+
m_object = m_pending_factory();
76+
}
77+
void remove_object_internal(){
78+
m_object.reset();
79+
}
80+
81+
private:
82+
Mutex m_lock;
83+
ConditionVariable m_cv;
84+
85+
std::function<std::unique_ptr<QObject>()> m_pending_factory;
86+
std::unique_ptr<QObject> m_object;
87+
};
88+
89+
90+
91+
class QtEventThreadPool : public QObject{
92+
public:
93+
~QtEventThreadPool();
94+
void stop();
95+
96+
QObject* add_object(std::function<std::unique_ptr<QObject>()> factory);
97+
void remove_object(QObject* object) noexcept;
98+
99+
100+
private:
101+
QtEventThread& get_thread();
102+
103+
104+
private:
105+
std::map<QObject*, QtEventThread*> m_objects;
106+
107+
Mutex m_lock;
108+
109+
bool m_stopping = false;
110+
std::deque<QtEventThread> m_threads;
111+
std::vector<QtEventThread*> m_available_threads;
112+
};
113+
45114

46115

47116

SerialPrograms/Source/CommonFramework/AudioPipeline/Backends/AudioPassthroughPairQt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#define PokemonAutomation_AudioPipeline_AudioPassthroughPairQt_H
99

1010
#include <memory>
11-
#include <set>
11+
//#include <set>
1212
#include <QObject>
1313
#include "Common/Cpp/ListenerSet.h"
1414
#include "Common/Cpp/LifetimeSanitizer.h"

SerialPrograms/Source/CommonFramework/Main.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,18 @@ int run_program(int argc, char *argv[]){
106106
QDir().mkpath(QString::fromStdString(SETTINGS_PATH()));
107107
QDir().mkpath(QString::fromStdString(SCREENSHOTS_PATH()));
108108

109+
110+
109111
// Preload all the cameras now so we don't hang the UI later on.
110-
ScopeExit cameras([]{ GlobalMediaServices::instance().stop(); });
112+
ScopeExit cameras([]{
113+
GlobalMediaServices::instance().stop();
114+
});
111115
get_all_cameras();
112116

117+
// Force all the Qt thread pools to be constructed now on the main thread.
118+
GlobalThreadPools::qt_worker_threadpool();
119+
GlobalThreadPools::qt_event_threadpool();
120+
113121
// Several novice developers struggled to build and run the program due to missing Resources folder.
114122
// Add this check to pop a message box when Resources folder is missing.
115123
if (!check_resource_folder(logger)){
@@ -226,15 +234,20 @@ int main(int argc, char *argv[]){
226234
global_watchdog().stop();
227235
static_cast<FileWindowLogger&>(global_logger_raw()).stop();
228236

237+
// When we actually migrate to Qt 6.9+, we may need to move the exit(0)
238+
// call here since joining *any* threads may hang.
239+
229240
// Force stop the thread pools.
241+
// This is where all the threads in the program are joined.
230242
PokemonAutomation::GlobalThreadPools::computation_realtime().stop();
231243
PokemonAutomation::GlobalThreadPools::computation_normal().stop();
232244
PokemonAutomation::GlobalThreadPools::unlimited_realtime().stop();
233245
PokemonAutomation::GlobalThreadPools::unlimited_normal().stop();
234-
GlobalThreadPools::qt_worker_threadpool().stop();
235-
236246
PokemonAutomation::global_dispatcher.stop();
237247

248+
GlobalThreadPools::qt_worker_threadpool().stop();
249+
GlobalThreadPools::qt_event_threadpool().stop();
250+
238251
cout << "Exiting main()..." << endl;
239252

240253

SerialPrograms/Source/CommonFramework/Tools/GlobalThreadPoolsQt.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ QtWorkerThreadPool& qt_worker_threadpool(){
1717
}
1818

1919

20+
QtEventThreadPool& qt_event_threadpool(){
21+
static QtEventThreadPool pool;
22+
return pool;
23+
}
24+
25+
2026

2127
}
2228
}

SerialPrograms/Source/CommonFramework/Tools/GlobalThreadPoolsQt.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ namespace PokemonAutomation{
1818
namespace GlobalThreadPools{
1919

2020

21-
21+
// Temporary and only used for audio template loading.
22+
// Hopefully we can remove this in the near future.
2223
QtWorkerThreadPool& qt_worker_threadpool();
2324

2425

26+
QtEventThreadPool& qt_event_threadpool();
27+
28+
2529

2630
}
2731
}

0 commit comments

Comments
 (0)