[geary/wip/745561-hang] Potential fix to hang problem
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/745561-hang] Potential fix to hang problem
- Date: Tue, 31 Mar 2015 00:19:40 +0000 (UTC)
commit 82fd668043c19f642a45ae1d3068a7ec55360208
Author: Jim Nelson <jim yorba org>
Date: Mon Mar 30 17:18:26 2015 -0700
Potential fix to hang problem
* Geary.Endpoint uses a 30s connect timeout and an app-specific
activity timeout (10m in case of IMAP).
* ClientSessionManager is refactored to avoid locking issues with
sessions mutex.
src/engine/api/geary-endpoint.vala | 20 +++-
.../transport/imap-client-session-manager.vala | 109 ++++++++------------
2 files changed, 59 insertions(+), 70 deletions(-)
---
diff --git a/src/engine/api/geary-endpoint.vala b/src/engine/api/geary-endpoint.vala
index f9be15a..bf07ffc 100644
--- a/src/engine/api/geary-endpoint.vala
+++ b/src/engine/api/geary-endpoint.vala
@@ -12,6 +12,11 @@
public class Geary.Endpoint : BaseObject {
public const string PROP_TRUST_UNTRUSTED_HOST = "trust-untrusted-host";
+ /**
+ * Recommended { link connect_timeout_sec}.
+ */
+ public const uint DEFAULT_CONNECT_TIMEOUT_SEC = 30;
+
[Flags]
public enum Flags {
NONE = 0,
@@ -42,7 +47,8 @@ public class Geary.Endpoint : BaseObject {
public NetworkAddress remote_address { get; private set; }
public Flags flags { get; private set; }
- public uint timeout_sec { get; private set; }
+ public uint connect_timeout_sec { get; private set; default = DEFAULT_CONNECT_TIMEOUT_SEC; }
+ public uint activity_timeout_sec { get; private set; }
public TlsCertificateFlags tls_validation_flags { get; set; default = TlsCertificateFlags.VALIDATE_ALL; }
public bool force_ssl3 { get; set; default = false; }
@@ -114,10 +120,10 @@ public class Geary.Endpoint : BaseObject {
*/
public signal void untrusted_host(SecurityType security, TlsConnection cx);
- public Endpoint(string host_specifier, uint16 default_port, Flags flags, uint timeout_sec) {
+ public Endpoint(string host_specifier, uint16 default_port, Flags flags, uint activity_timeout_sec) {
this.remote_address = new NetworkAddress(host_specifier, default_port);
this.flags = flags;
- this.timeout_sec = timeout_sec;
+ this.activity_timeout_sec = activity_timeout_sec;
}
private SocketClient get_socket_client() {
@@ -132,7 +138,9 @@ public class Geary.Endpoint : BaseObject {
socket_client.event.connect(on_socket_client_event);
}
- socket_client.set_timeout(timeout_sec);
+ // Use the connect_timeout_sec for SocketClient, then set the activity timeout when the
+ // Socket is available
+ socket_client.set_timeout(connect_timeout_sec);
return socket_client;
}
@@ -144,6 +152,10 @@ public class Geary.Endpoint : BaseObject {
if (tcp != null)
tcp.set_graceful_disconnect(flags.is_all_set(Flags.GRACEFUL_DISCONNECT));
+ Socket? socket = cx.get_socket();
+ if (socket != null)
+ socket.set_timeout(activity_timeout_sec);
+
return cx;
}
diff --git a/src/engine/imap/transport/imap-client-session-manager.vala
b/src/engine/imap/transport/imap-client-session-manager.vala
index c4f577d..b4f596b 100644
--- a/src/engine/imap/transport/imap-client-session-manager.vala
+++ b/src/engine/imap/transport/imap-client-session-manager.vala
@@ -58,7 +58,6 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
private Endpoint endpoint;
private NetworkMonitor network_monitor;
private Gee.HashSet<ClientSession> sessions = new Gee.HashSet<ClientSession>();
- private int pending_sessions = 0;
private Nonblocking.Mutex sessions_mutex = new Nonblocking.Mutex();
private Gee.HashSet<ClientSession> reserved_sessions = new Gee.HashSet<ClientSession>();
private bool authentication_failed = false;
@@ -159,48 +158,48 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
if (!is_open)
return;
- int token;
- try {
- token = yield sessions_mutex.claim_async();
- } catch (Error claim_err) {
- debug("Unable to claim session table mutex for adjusting pool: %s", claim_err.message);
-
- return;
- }
-
- while ((sessions.size + pending_sessions) < min_pool_size
+ bool try_again = true;
+ while (sessions.size < min_pool_size
&& !authentication_failed
&& is_open
&& !untrusted_host
- && is_endpoint_reachable) {
- pending_sessions++;
- create_new_authorized_session.begin(null, on_created_new_authorized_session);
- }
-
- try {
- sessions_mutex.release(ref token);
- } catch (Error release_err) {
- debug("Unable to release session table mutex after adjusting pool: %s", release_err.message);
- }
- }
-
- private void on_created_new_authorized_session(Object? source, AsyncResult result) {
- pending_sessions--;
-
- try {
- create_new_authorized_session.end(result);
- } catch (Error err) {
- debug("Unable to create authorized session to %s: %s", endpoint.to_string(), err.message);
-
- // try again after a slight delay and bump up delay
- if (authorized_session_error_retry_timeout_id != 0)
- Source.remove(authorized_session_error_retry_timeout_id);
+ && is_endpoint_reachable
+ && try_again) {
+ // acquire sessions table lock for each session creation ... this means we can't launch
+ // several connects at startup, but it's also problematic to keep the session table
+ // locked in that situation, so need to serialize this
+ int token;
+ try {
+ token = yield sessions_mutex.claim_async();
+ } catch (Error claim_err) {
+ debug("Unable to claim session table mutex for adjusting pool: %s", claim_err.message);
+
+ break;
+ }
- authorized_session_error_retry_timeout_id = Timeout.add_seconds(
- authorized_session_retry_sec, on_authorized_session_error_retry_timeout);
+ try {
+ yield create_new_authorized_session(null);
+ } catch (Error err) {
+ debug("Unable to create authorized session to %s: %s", endpoint.to_string(), err.message);
+
+ // try again after a slight delay and bump up delay
+ if (authorized_session_error_retry_timeout_id != 0)
+ Source.remove(authorized_session_error_retry_timeout_id);
+
+ authorized_session_error_retry_timeout_id = Timeout.add_seconds(
+ authorized_session_retry_sec, on_authorized_session_error_retry_timeout);
+
+ authorized_session_retry_sec = (authorized_session_retry_sec * 2).clamp(
+ AUTHORIZED_SESSION_ERROR_MIN_RETRY_TIMEOUT_SEC,
AUTHORIZED_SESSION_ERROR_MAX_RETRY_TIMEOUT_SEC);
+
+ try_again = false;
+ }
- authorized_session_retry_sec = (authorized_session_retry_sec * 2).clamp(
- AUTHORIZED_SESSION_ERROR_MIN_RETRY_TIMEOUT_SEC,
AUTHORIZED_SESSION_ERROR_MAX_RETRY_TIMEOUT_SEC);
+ try {
+ sessions_mutex.release(ref token);
+ } catch (Error release_err) {
+ debug("Unable to release session table mutex after adjusting pool: %s", release_err.message);
+ }
}
}
@@ -212,7 +211,10 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
return false;
}
+ // This *must* be called with the sessions_mutex locked
private async ClientSession create_new_authorized_session(Cancellable? cancellable) throws Error {
+ assert(sessions_mutex.is_locked());
+
if (authentication_failed)
throw new ImapError.UNAUTHENTICATED("Invalid ClientSessionManager credentials");
@@ -226,21 +228,14 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
// add session to pool before launching all the connect activity so error cases can properly
// back it out
- if (sessions_mutex.is_locked())
- locked_add_session(new_session);
- else
- yield unlocked_add_session_async(new_session);
+ locked_add_session(new_session);
try {
yield new_session.connect_async(cancellable);
} catch (Error err) {
debug("[%s] Connect failure: %s", new_session.to_string(), err.message);
- bool removed;
- if (sessions_mutex.is_locked())
- removed = locked_remove_session(new_session);
- else
- removed = yield unlocked_remove_session_async(new_session);
+ bool removed = locked_remove_session(new_session);
assert(removed);
throw err;
@@ -260,11 +255,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
new_session.to_string(), disconnect_err.message);
}
- bool removed;
- if (sessions_mutex.is_locked())
- removed = locked_remove_session(new_session);
- else
- removed = yield unlocked_remove_session_async(new_session);
+ bool removed = locked_remove_session(new_session);
assert(removed);
throw err;
@@ -459,12 +450,6 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
session.login_failed.connect(on_login_failed);
}
- private async void unlocked_add_session_async(ClientSession session) throws Error {
- int token = yield sessions_mutex.claim_async();
- locked_add_session(session);
- sessions_mutex.release(ref token);
- }
-
// Only call with sessions mutex locked
private bool locked_remove_session(ClientSession session) {
bool removed = sessions.remove(session);
@@ -478,14 +463,6 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
return removed;
}
- private async bool unlocked_remove_session_async(ClientSession session) throws Error {
- int token = yield sessions_mutex.claim_async();
- bool removed = locked_remove_session(session);
- sessions_mutex.release(ref token);
-
- return removed;
- }
-
private void on_imap_untrusted_host() {
// this is called any time trust issues are detected, so immediately clutch in to stop
// retries
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]