28
28
#include " threads/waitforcardthread.hpp"
29
29
30
30
#include " utils/utils.hpp"
31
+
31
32
#include " inputoutputmode.hpp"
32
33
#include " writeresponse.hpp"
33
34
34
- #include < QApplication >
35
+ #include < QCoreApplication >
35
36
36
37
using namespace pcsc_cpp ;
37
38
using namespace electronic_id ;
@@ -44,19 +45,11 @@ const QString RESP_USER_CANCEL = QStringLiteral("ERR_WEBEID_USER_CANCELLED");
44
45
45
46
QVariantMap makeErrorObject (const QString& errorCode, const QString& errorMessage)
46
47
{
47
- const auto errorBody = QVariantMap {
48
- {QStringLiteral (" code" ), errorCode},
49
- {QStringLiteral (" message" ), errorMessage},
50
- };
51
- return {{QStringLiteral (" error" ), errorBody}};
52
- }
53
-
54
- void interruptThread (QThread* thread)
55
- {
56
- qDebug () << " Interrupting thread" << uintptr_t (thread);
57
- thread->disconnect ();
58
- thread->requestInterruption ();
59
- ControllerChildThread::waitForControllerNotify.wakeAll ();
48
+ return {{QStringLiteral (" error" ),
49
+ QVariantMap {
50
+ {QStringLiteral (" code" ), errorCode},
51
+ {QStringLiteral (" message" ), errorMessage},
52
+ }}};
60
53
}
61
54
62
55
} // namespace
@@ -67,17 +60,19 @@ void Controller::run()
67
60
const bool isInCommandLineMode = bool (command);
68
61
isInStdinMode = !isInCommandLineMode;
69
62
70
- qInfo () << qApp->applicationName () << " app" << qApp->applicationVersion () << " running in"
63
+ qInfo () << QCoreApplication::applicationName () << " app"
64
+ << QCoreApplication::applicationVersion () << " running in"
71
65
<< (isInStdinMode ? " stdin/stdout" : " command-line" ) << " mode" ;
72
66
73
67
try {
74
68
// TODO: cut out stdin mode separate class to avoid bugs in safari mode
75
69
if (isInStdinMode) {
76
70
// In stdin/stdout mode we first output the version as required by the WebExtension
77
71
// and then wait for the actual command.
78
- writeResponseToStdOut (isInStdinMode,
79
- {{QStringLiteral (" version" ), qApp->applicationVersion ()}},
80
- " get-version" );
72
+ writeResponseToStdOut (
73
+ isInStdinMode,
74
+ {{QStringLiteral (" version" ), QCoreApplication::applicationVersion ()}},
75
+ " get-version" );
81
76
82
77
command = readCommandFromStdin ();
83
78
}
@@ -111,11 +106,14 @@ void Controller::startCommandExecution()
111
106
REQUIRE_NON_NULL (commandHandler)
112
107
113
108
// Reader monitor thread setup.
114
- WaitForCardThread* waitForCardThread = new WaitForCardThread (this );
109
+ auto * waitForCardThread = new WaitForCardThread (this );
110
+ connect (this , &Controller::stopCardEventMonitorThread, waitForCardThread,
111
+ &WaitForCardThread::requestInterruption);
115
112
connect (waitForCardThread, &WaitForCardThread::statusUpdate, this , &Controller::statusUpdate);
116
113
connect (waitForCardThread, &WaitForCardThread::cardsAvailable, this ,
117
114
&Controller::onCardsAvailable);
118
- saveChildThreadPtrAndConnectFailureFinish (waitForCardThread);
115
+ connect (waitForCardThread, &ControllerChildThread::failure, this ,
116
+ &Controller::onCriticalFailure);
119
117
120
118
// UI setup.
121
119
window = WebEidUI::createAndShowDialog (commandHandler->commandType ());
@@ -126,33 +124,6 @@ void Controller::startCommandExecution()
126
124
waitForCardThread->start ();
127
125
}
128
126
129
- void Controller::saveChildThreadPtrAndConnectFailureFinish (ControllerChildThread* childThread)
130
- {
131
- REQUIRE_NON_NULL (childThread)
132
- // Save the thread pointer in child thread tracking map to request interruption and wait for
133
- // it to quit in waitForChildThreads().
134
- childThreads[uintptr_t (childThread)] = childThread;
135
-
136
- connect (childThread, &ControllerChildThread::failure, this , &Controller::onCriticalFailure);
137
-
138
- // When the thread is finished, remove the pointer from the tracking map and call deleteLater()
139
- // on it to free the thread object. Although the thread objects are freed through the Qt object
140
- // tree ownership system anyway, it is better to delete them immediately when they finish.
141
- connect (childThread, &ControllerChildThread::finished, this , [this , childThread]() {
142
- QScopedPointer<ControllerChildThread, QScopedPointerDeleteLater> deleteLater {childThread};
143
-
144
- const auto threadPtrAddress = uintptr_t (childThread);
145
- if (childThreads.count (threadPtrAddress) && childThreads[threadPtrAddress]) {
146
- childThreads[threadPtrAddress] = nullptr ;
147
- childThread->wait ();
148
- qDebug () << " Thread" << threadPtrAddress << " finished" ;
149
- } else {
150
- qWarning () << " Controller child thread" << childThread
151
- << " is missing or null in finish slot" ;
152
- }
153
- });
154
- }
155
-
156
127
void Controller::connectOkCancelWaitingForPinPad ()
157
128
{
158
129
REQUIRE_NON_NULL (window)
@@ -192,9 +163,10 @@ void Controller::onCardsAvailable(
192
163
void Controller::runCommandHandler (const std::vector<ElectronicID::ptr>& availableEids)
193
164
{
194
165
try {
195
- CommandHandlerRunThread * commandHandlerRunThread =
166
+ auto * commandHandlerRunThread =
196
167
new CommandHandlerRunThread (this , *commandHandler, availableEids);
197
- saveChildThreadPtrAndConnectFailureFinish (commandHandlerRunThread);
168
+ connect (commandHandlerRunThread, &ControllerChildThread::failure, this ,
169
+ &Controller::onCriticalFailure);
198
170
connectRetry (commandHandlerRunThread);
199
171
200
172
// When the command handler run thread retrieves certificates successfully, call
@@ -213,51 +185,32 @@ void Controller::runCommandHandler(const std::vector<ElectronicID::ptr>& availab
213
185
214
186
void Controller::onCertificatesLoaded ()
215
187
{
216
- CardEventMonitorThread* cardEventMonitorThread =
217
- new CardEventMonitorThread (this , std::string (commandType ()));
218
- saveChildThreadPtrAndConnectFailureFinish (cardEventMonitorThread);
219
- cardEventMonitorThreadKey = uintptr_t (cardEventMonitorThread);
188
+ auto * cardEventMonitorThread = new CardEventMonitorThread (this , commandType ());
189
+ connect (cardEventMonitorThread, &ControllerChildThread::failure, this ,
190
+ &Controller::onCriticalFailure);
191
+ connect (this , &Controller::stopCardEventMonitorThread, cardEventMonitorThread,
192
+ &WaitForCardThread::requestInterruption);
220
193
connect (cardEventMonitorThread, &CardEventMonitorThread::cardEvent, this , &Controller::onRetry);
221
194
cardEventMonitorThread->start ();
222
195
}
223
196
224
- void Controller::stopCardEventMonitorThread ()
225
- {
226
- if (cardEventMonitorThreadKey) {
227
- try {
228
- auto cardEventMonitorThread = childThreads.at (cardEventMonitorThreadKey);
229
- cardEventMonitorThreadKey = 0 ;
230
- if (cardEventMonitorThread) {
231
- interruptThread (cardEventMonitorThread);
232
- }
233
- } catch (const std::out_of_range&) {
234
- qWarning () << " Card event monitor thread" << cardEventMonitorThreadKey
235
- << " is missing from childThreads map in stopCardEventMonitorThread()" ;
236
- cardEventMonitorThreadKey = 0 ;
237
- }
238
- }
239
- }
240
-
241
197
void Controller::disposeUI ()
242
198
{
243
- if (window) {
244
- window->disconnect ();
245
- // As the Qt::WA_DeleteOnClose flag is set, the dialog is deleted automatically.
246
- window->close ();
247
- window = nullptr ;
248
- }
199
+ delete window;
200
+ window = nullptr ;
249
201
}
250
202
251
203
void Controller::onConfirmCommandHandler (const EidCertificateAndPinInfo& certAndPinInfo)
252
204
{
253
- stopCardEventMonitorThread ();
205
+ emit stopCardEventMonitorThread ();
254
206
255
207
try {
256
- CommandHandlerConfirmThread * commandHandlerConfirmThread =
208
+ auto * commandHandlerConfirmThread =
257
209
new CommandHandlerConfirmThread (this , *commandHandler, window, certAndPinInfo);
258
210
connect (commandHandlerConfirmThread, &CommandHandlerConfirmThread::completed, this ,
259
211
&Controller::onCommandHandlerConfirmCompleted);
260
- saveChildThreadPtrAndConnectFailureFinish (commandHandlerConfirmThread);
212
+ connect (commandHandlerConfirmThread, &ControllerChildThread::failure, this ,
213
+ &Controller::onCriticalFailure);
261
214
connectRetry (commandHandlerConfirmThread);
262
215
263
216
commandHandlerConfirmThread->start ();
@@ -351,6 +304,7 @@ void Controller::onPinPadCancel()
351
304
352
305
void Controller::onCriticalFailure (const QString& error)
353
306
{
307
+ emit stopCardEventMonitorThread ();
354
308
qCritical () << " Exiting due to command" << std::string (commandType ())
355
309
<< " fatal error:" << error;
356
310
_result =
@@ -373,18 +327,15 @@ void Controller::exit()
373
327
374
328
void Controller::waitForChildThreads ()
375
329
{
376
- // Waiting for child threads must not happen in destructor.
377
- // See https://tombarta.wordpress.com/2008/07/10/gcc-pure-virtual-method-called/ for details.
378
- for (const auto & childThread : childThreads) {
379
- auto thread = childThread.second ;
380
- if (thread) {
381
- interruptThread (thread);
382
- // Waiting for PIN input on PIN pad may take a long time, call processEvents() so that
383
- // the UI doesn't freeze.
384
- while (thread->isRunning ()) {
385
- thread->wait (100 ); // in milliseconds
386
- QCoreApplication::processEvents ();
387
- }
330
+ for (auto * thread : findChildren<QThread*>()) {
331
+ qDebug () << " Interrupting thread" << uintptr_t (thread);
332
+ thread->disconnect ();
333
+ thread->requestInterruption ();
334
+ ControllerChildThread::waitForControllerNotify.wakeAll ();
335
+ // Call processEvents() so that the UI doesn't freeze.
336
+ while (thread->isRunning ()) {
337
+ thread->wait (100ms);
338
+ QCoreApplication::processEvents ();
388
339
}
389
340
}
390
341
}
0 commit comments