@@ -56,7 +56,8 @@ public:
5656 this, SLOT(handleMessage(pam_handle *, QString, QLightDM::Greeter::MessageType)));
5757 // This next connect is how we pass ResponseFutures between threads
5858 connect(this, SIGNAL(showPrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
59− this, SLOT(handlePrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)));
59+ this, SLOT(handlePrompt(pam_handle *, QString, QLightDM::Greeter::PromptType, QLightDM::GreeterImpl::ResponseFuture)),
60+ Qt::BlockingQueuedConnection);
6061 }
6162
6263 ~GreeterImpl()
@@ -68,6 +69,13 @@ public:
6869 {
6970 // Clear out any existing PAM interactions first
7071 cancelPam();
72+ if (pamHandle != nullptr) {
73+ // While we were cancelling pam above, we processed Qt events.
74+ // Which may have allowed someone to call start() on us again.
75+ // In which case, we'll bail on our current start() call.
76+ // This isn't racy because it's all in the same thread.
77+ return;
78+ }
7179
7280 AppData *appData = new AppData();
7381 appData->impl = this;
@@ -79,7 +87,7 @@ public:
7987
8088 if (pam_start("lightdm", username.toUtf8(), &conversation, &pamHandle) == PAM_SUCCESS) {
8189 appData->handle = pamHandle;
82− futureWatcher.setFuture(QtConcurrent::run(authenticateWithPam, pamHandle));
90+ futureWatcher.setFuture(QtConcurrent::mapped(QList<pam_handle*>() << pamHandle, authenticateWithPam));
8391 } else {
8492 delete appData;
8593 greeterPrivate->authenticated = false;
@@ -88,7 +96,7 @@ public:
8896 }
8997 }
9098
91− static int authenticateWithPam(pam_handle* pamHandle)
99+ static int authenticateWithPam(pam_handle* const& pamHandle)
92100 {
93101 int pamStatus = pam_authenticate(pamHandle, 0);
94102 if (pamStatus == PAM_SUCCESS) {
@@ -242,11 +250,22 @@ private Q_SLOTS:
242250private:
243251 void cancelPam()
244252 {
245− // Unfortunately we can't simply cancel our QFuture because QtConcurrent::run doesn't support cancel
246253 if (pamHandle != nullptr) {
254+ QFuture<int> pamFuture = futureWatcher.future();
247255 pam_handle *handle = pamHandle;
248256 pamHandle = nullptr; // to disable normal finishPam() handling
249− while (respond(QString())); // clear our local queue of QFutures
257+ pamFuture.cancel();
258+
259+ // Note the empty loop, we just want to clear the futures queue.
260+ // Any further prompts from the pam thread will be immediately
261+ // responded to/dismissed in handlePrompt().
262+ while (respond(QString()));
263+
264+ // Now let signal/slot handling happen so the thread can finish
265+ while (!pamFuture.isFinished()) {
266+ QCoreApplication::processEvents();
267+ }
268+
250269 pam_end(handle, PAM_CONV_ERR);
251270 }
252271 }
@@ -266,6 +285,11 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
266285{
267286}
268287
288+GreeterPrivate::~GreeterPrivate()
289+{
290+ delete m_impl;
291+}
292+
269293void GreeterPrivate::handleAuthenticate()
270294{
271295 m_impl->start(authenticationUser);