2828#include " threads/waitforcardthread.hpp"
2929
3030#include " utils/utils.hpp"
31+
3132#include " inputoutputmode.hpp"
3233#include " writeresponse.hpp"
3334
34- #include < QApplication >
35+ #include < QCoreApplication >
3536
3637using namespace pcsc_cpp ;
3738using namespace electronic_id ;
@@ -44,19 +45,11 @@ const QString RESP_USER_CANCEL = QStringLiteral("ERR_WEBEID_USER_CANCELLED");
4445
4546QVariantMap makeErrorObject (const QString& errorCode, const QString& errorMessage)
4647{
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+ }}};
6053}
6154
6255} // namespace
@@ -67,17 +60,19 @@ void Controller::run()
6760 const bool isInCommandLineMode = bool (command);
6861 isInStdinMode = !isInCommandLineMode;
6962
70- qInfo () << qApp->applicationName () << " app" << qApp->applicationVersion () << " running in"
63+ qInfo () << QCoreApplication::applicationName () << " app"
64+ << QCoreApplication::applicationVersion () << " running in"
7165 << (isInStdinMode ? " stdin/stdout" : " command-line" ) << " mode" ;
7266
7367 try {
7468 // TODO: cut out stdin mode separate class to avoid bugs in safari mode
7569 if (isInStdinMode) {
7670 // In stdin/stdout mode we first output the version as required by the WebExtension
7771 // 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" );
8176
8277 command = readCommandFromStdin ();
8378 }
@@ -111,11 +106,14 @@ void Controller::startCommandExecution()
111106 REQUIRE_NON_NULL (commandHandler)
112107
113108 // 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);
112+ connect (waitForCardThread, &ControllerChildThread::failure, this ,
113+ &Controller::onCriticalFailure);
115114 connect (waitForCardThread, &WaitForCardThread::statusUpdate, this , &Controller::statusUpdate);
116115 connect (waitForCardThread, &WaitForCardThread::cardsAvailable, this ,
117116 &Controller::onCardsAvailable);
118- saveChildThreadPtrAndConnectFailureFinish (waitForCardThread);
119117
120118 // UI setup.
121119 window = WebEidUI::createAndShowDialog (commandHandler->commandType ());
@@ -126,33 +124,6 @@ void Controller::startCommandExecution()
126124 waitForCardThread->start ();
127125}
128126
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-
156127void Controller::connectOkCancelWaitingForPinPad ()
157128{
158129 REQUIRE_NON_NULL (window)
@@ -192,9 +163,10 @@ void Controller::onCardsAvailable(
192163void Controller::runCommandHandler (const std::vector<ElectronicID::ptr>& availableEids)
193164{
194165 try {
195- CommandHandlerRunThread * commandHandlerRunThread =
166+ auto * commandHandlerRunThread =
196167 new CommandHandlerRunThread (this , *commandHandler, availableEids);
197- saveChildThreadPtrAndConnectFailureFinish (commandHandlerRunThread);
168+ connect (commandHandlerRunThread, &ControllerChildThread::failure, this ,
169+ &Controller::onCriticalFailure);
198170 connectRetry (commandHandlerRunThread);
199171
200172 // When the command handler run thread retrieves certificates successfully, call
@@ -213,51 +185,32 @@ void Controller::runCommandHandler(const std::vector<ElectronicID::ptr>& availab
213185
214186void Controller::onCertificatesLoaded ()
215187{
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 (this , &Controller::stopCardEventMonitorThread, cardEventMonitorThread,
190+ &CardEventMonitorThread::requestInterruption);
191+ connect (cardEventMonitorThread, &ControllerChildThread::failure, this ,
192+ &Controller::onCriticalFailure);
220193 connect (cardEventMonitorThread, &CardEventMonitorThread::cardEvent, this , &Controller::onRetry);
221194 cardEventMonitorThread->start ();
222195}
223196
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-
241197void Controller::disposeUI ()
242198{
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 ;
249201}
250202
251203void Controller::onConfirmCommandHandler (const EidCertificateAndPinInfo& certAndPinInfo)
252204{
253- stopCardEventMonitorThread ();
205+ emit stopCardEventMonitorThread ();
254206
255207 try {
256- CommandHandlerConfirmThread * commandHandlerConfirmThread =
208+ auto * commandHandlerConfirmThread =
257209 new CommandHandlerConfirmThread (this , *commandHandler, window, certAndPinInfo);
258210 connect (commandHandlerConfirmThread, &CommandHandlerConfirmThread::completed, this ,
259211 &Controller::onCommandHandlerConfirmCompleted);
260- saveChildThreadPtrAndConnectFailureFinish (commandHandlerConfirmThread);
212+ connect (commandHandlerConfirmThread, &ControllerChildThread::failure, this ,
213+ &Controller::onCriticalFailure);
261214 connectRetry (commandHandlerConfirmThread);
262215
263216 commandHandlerConfirmThread->start ();
@@ -351,6 +304,7 @@ void Controller::onPinPadCancel()
351304
352305void Controller::onCriticalFailure (const QString& error)
353306{
307+ emit stopCardEventMonitorThread ();
354308 qCritical () << " Exiting due to command" << std::string (commandType ())
355309 << " fatal error:" << error;
356310 _result =
@@ -373,18 +327,16 @@ void Controller::exit()
373327
374328void Controller::waitForChildThreads ()
375329{
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+ using namespace std ::chrono_literals;
338+ thread->wait (100ms);
339+ QCoreApplication::processEvents ();
388340 }
389341 }
390342}
0 commit comments