Skip to content

KWallet availability check can block the GUI thread for 25 s when kwalletd is waiting for wallet unlock #303

Description

@realstuffie

Since commit c7e1063 ("Use default DBus timeout for KWallet check", shipped in 0.16.0), isKwalletAvailable() performs a blocking networkWallet D-Bus call with the default 25-second timeout. That commit removed the previous iface.setTimeout(500) to fix #242, on the assumption that "If KWallet is not available the call will fail fast."

That assumption doesn't hold in one common case: kwalletd is running but busy showing its unlock dialog. While the dialog is up, kwalletd holds replies to incoming calls, so the blocking call neither succeeds nor fails fast; it blocks the calling thread for the full 25 seconds.

Backend detection runs lazily on the first job's scheduledStart(), which is typically the application's GUI thread. The result:

  • Any Qt app using QtKeychain 0.16.0+ that starts while the wallet is locked (e.g. autostarted apps at session login before the user has entered the wallet password) freezes for 25 s.
  • In our case (a Plasma applet, so QtKeychain runs inside plasmashell), the entire desktop shell froze for 25 s at every login until the wallet was unlocked.

Steps to reproduce

  1. KDE Plasma 6, wallet configured with a password, not unlocked by PAM.
  2. Start an application that issues a ReadPasswordJob at startup, while the wallet unlock dialog from another client is pending (or before unlocking).
  3. Application GUI thread blocks ~25 s in detectKeyringBackend()isKwalletAvailable().

Suggested fix

Cap the probe timeout again, but treat a timeout as "available" so the concern from #242 (slow kwalletd misdetected as absent silent libsecret fallback, cached for the process lifetime) stays fixed: a timeout proves the service exists; it's just busy.

--- a/qtkeychain/keychain_unix.cpp
+++ b/qtkeychain/keychain_unix.cpp
@@ -103,8 +103,17 @@ static bool isKwalletAvailable(const char *dbusIface, const char *dbusPath)
     // interface is activatable by making a call. Hence we check whether
     // a wallet can be opened.

+    iface.setTimeout(5000);
     QDBusMessage reply = iface.call(QLatin1String("networkWallet"));
-    return reply.type() == QDBusMessage::ReplyMessage;
+    if (reply.type() == QDBusMessage::ReplyMessage)
+        return true;
+    const QDBusError::ErrorType err = QDBusError(reply).type();
+    return err == QDBusError::NoReply || err == QDBusError::Timeout;
 }
 
 static KeyringBackend detectKeyringBackend()

This is tested and working; the login freeze is gone, and backend selection still resolves to KWallet with the wallet locked. The remaining 5 s stall could be eliminated by making detection async, but that's a much larger structural change; this keeps the current shape of the code.

Related: #242 (the issue the regressing commit fixed).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions