[geary/wip/789924-network-transition-redux: 4/10] Rework how the client session manager manages the IMAP session pool.



commit 4cfeb7dd37f1a94a60c1b7055b045a33003c2480
Author: Michael James Gratton <mike vee net>
Date:   Thu Jan 18 14:59:41 2018 +1100

    Rework how the client session manager manages the IMAP session pool.
    
    * src/engine/imap/transport/imap-client-session-manager.vala
      (ClientSessionManager): Use a non-blocking queue for free sessions
      rather than a set of reserved sessions, so we can instantly pick a free
      one when needed and available. Manage establishing new connections from
      one place in check_pool (was adjust_session_pool) rather than in a few
      different places in the class. Greatly simply lock management and
      connection establishment code. Add some doc comments, clean up code
      organisation.

 .../transport/imap-client-session-manager.vala     |  510 ++++++++++----------
 1 files changed, 249 insertions(+), 261 deletions(-)
---
diff --git a/src/engine/imap/transport/imap-client-session-manager.vala 
b/src/engine/imap/transport/imap-client-session-manager.vala
index f9f08bd..7c36ab4 100644
--- a/src/engine/imap/transport/imap-client-session-manager.vala
+++ b/src/engine/imap/transport/imap-client-session-manager.vala
@@ -1,16 +1,31 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
- * Copyright 2017 Michael Gratton <mike vee net>
+ * Copyright 2017-2018 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
  * (version 2.1 or later).  See the COPYING file in this distribution.
  */
 
+/**
+ * Manages a pool of IMAP client sessions.
+ *
+ * When opened and when reachable, the manager will establish a pool
+ * of {@link ClientSession} instances that are connected to the IMAP
+ * endpoint of an account, ensuring there are at least {@link
+ * min_pool_size} available. A connected, authorised client session
+ * can be obtained from the connection pool by calling {@link
+ * claim_authorized_session_async}, and when finished with returned by
+ * calling {@link release_session_async}.
+ *
+ * This class is not thread-safe.
+ */
 public class Geary.Imap.ClientSessionManager : BaseObject {
 
+
     private const int DEFAULT_MIN_POOL_SIZE = 1;
-    private const int POOL_START_TIMEOUT_SEC = 4;
-    private const int POOL_STOP_TIMEOUT_SEC = 1;
+    private const int POOL_START_TIMEOUT_SEC = 1;
+    private const int POOL_STOP_TIMEOUT_SEC = 3;
+
 
     /** Determines if the manager has been opened. */
     public bool is_open { get; private set; default = false; }
@@ -32,7 +47,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
      * and returning to an authorized state.
      */
     public uint unselected_keepalive_sec { get; set; default = 
ClientSession.DEFAULT_UNSELECTED_KEEPALIVE_SEC; }
-    
+
     /**
      * Set to zero or negative value if keepalives should be disabled when a mailbox is selected
      * or examined.  (This is not recommended.)
@@ -40,7 +55,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
      * This only affects newly selected/examined sessions.
      */
     public uint selected_keepalive_sec { get; set; default = ClientSession.DEFAULT_SELECTED_KEEPALIVE_SEC; }
-    
+
     /**
      * Set to zero or negative value if keepalives should be disabled when a mailbox is selected
      * or examined and IDLE is supported.  (This is not recommended.)
@@ -48,7 +63,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
      * This only affects newly selected/examined sessions.
      */
     public uint selected_with_idle_keepalive_sec { get; set; default = 
ClientSession.DEFAULT_SELECTED_WITH_IDLE_KEEPALIVE_SEC; }
-    
+
     /**
      * ClientSessionManager attempts to maintain a minimum number of open sessions with the server
      * so they're immediately ready for use.
@@ -65,15 +80,19 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
 
     private AccountInformation account_information;
     private Endpoint endpoint;
-    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;
-    private bool untrusted_host = false;
+    private Gee.Set<ClientSession> all_sessions =
+        new Gee.HashSet<ClientSession>();
+    private Nonblocking.Queue<ClientSession> free_queue =
+        new Nonblocking.Queue<ClientSession>.fifo();
 
     private TimeoutManager pool_start;
     private TimeoutManager pool_stop;
+    private Cancellable? pool_cancellable = null;
+
+    private bool authentication_failed = false;
+    private bool untrusted_host = false;
 
     /**
      * Fired after when the manager has a working connection.
@@ -103,7 +122,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
 
         this.pool_start = new TimeoutManager.seconds(
             POOL_START_TIMEOUT_SEC,
-            () => { this.adjust_session_pool.begin(); }
+            () => { this.check_pool.begin(); }
         );
 
         this.pool_stop = new TimeoutManager.seconds(
@@ -114,7 +133,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
 
     ~ClientSessionManager() {
         if (is_open)
-            warning("Destroying opened ClientSessionManager");
+            warning("[%s] Destroying opened ClientSessionManager", to_string());
 
         this.endpoint.untrusted_host.disconnect(on_imap_untrusted_host);
         this.endpoint.notify[Endpoint.PROP_TRUST_UNTRUSTED_HOST].disconnect(on_imap_trust_untrusted_host);
@@ -126,11 +145,12 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
 
         this.is_open = true;
         this.authentication_failed = false;
+        this.pool_cancellable = new Cancellable();
 
                this.endpoint.connectivity.notify["is-reachable"].connect(on_connectivity_change);
         this.endpoint.connectivity.address_error_reported.connect(on_connectivity_error);
         if (this.endpoint.connectivity.is_reachable.is_certain()) {
-            this.adjust_session_pool.begin();
+            this.check_pool.begin();
         } else {
             this.endpoint.connectivity.check_reachable.begin();
         }
@@ -145,6 +165,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
 
         this.pool_start.reset();
         this.pool_stop.reset();
+        this.pool_cancellable.cancel();
 
                this.endpoint.connectivity.notify["is-reachable"].disconnect(on_connectivity_change);
         this.endpoint.connectivity.address_error_reported.disconnect(on_connectivity_error);
@@ -154,11 +175,11 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
         // TODO: This isn't the best (deterministic) way to deal with this, but it's easy and works
         // for now
         int attempts = 0;
-        while (sessions.size > 0) {
-            debug("Waiting for ClientSessions to disconnect from ClientSessionManager...");
+        while (this.all_sessions.size > 0) {
+            debug("[%s] Waiting for client sessions to disconnect...", to_string());
             Timeout.add(250, close_async.callback);
             yield;
-            
+
             // give up after three seconds
             if (++attempts > 12)
                 break;
@@ -174,199 +195,82 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
     public void credentials_updated() {
         this.authentication_failed = false;
         if (this.is_open) {
-            this.adjust_session_pool.begin();
+            this.check_pool.begin();
         }
     }
 
-    private void check_open() throws Error {
-        if (!is_open)
-            throw new EngineError.OPEN_REQUIRED("ClientSessionManager is not open");
-    }
-    
-    // TODO: Need a more thorough and bulletproof system for maintaining a pool of ready
-    // authorized sessions.
-    private async void adjust_session_pool() {
-        if (!this.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
-            && this.is_open
-            && !this.authentication_failed
-            && !this.untrusted_host
-            && this.endpoint.connectivity.is_reachable.is_certain()) {
-            this.pending_sessions++;
-            create_new_authorized_session.begin(
-                null,
-                (obj, res) => {
-                    this.pending_sessions--;
-                    try {
-                        this.create_new_authorized_session.end(res);
-                    } catch (Error err) {
-                        connection_failed(err);
-                    }
-                });
-        }
-
-        try {
-            sessions_mutex.release(ref token);
-        } catch (Error release_err) {
-            debug("Unable to release session table mutex after adjusting pool: %s", release_err.message);
-        }
-    }
+    /**
+     * Claims a free session, blocking until one becomes available.
+     *
+     * This call will fail fast if the pool is known to not in the
+     * right state (bad authorisation credentials, host not ready,
+     * etc), but then will block while attempting to obtain a
+     * connection if the free queue is empty. If an error occurs when
+     * this connection is in progress, then the call will block until
+     * another becomes available (host becomes reachable again, user
+     * enters password, etc). If this is undesirable, then the caller
+     * may cancel the call.
+     *
+     * @throws ImapError.UNAUTHENTICATED if the stored credentials are
+     * invalid.
+     * @throws ImapError.UNAVAILABLE if the IMAP endpoint is not
+     * trusted or is not reachable.
+     */
+    public async ClientSession claim_authorized_session_async(Cancellable? cancellable)
+        throws Error {
+        check_open();
+        debug("[%s] Claiming session from %d of %d free",
+              to_string(), this.free_queue.size, this.all_sessions.size);
 
-    private async ClientSession create_new_authorized_session(Cancellable? cancellable) throws Error {
-        if (authentication_failed)
+        if (this.authentication_failed)
             throw new ImapError.UNAUTHENTICATED("Invalid ClientSessionManager credentials");
 
-        if (untrusted_host)
+        if (this.untrusted_host)
             throw new ImapError.UNAVAILABLE("Untrusted host %s", endpoint.to_string());
 
         if (!this.endpoint.connectivity.is_reachable.is_certain())
             throw new ImapError.UNAVAILABLE("Host at %s is unreachable", endpoint.to_string());
 
-        ClientSession new_session = new ClientSession(endpoint);
-        
-        // 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);
-        
-        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);
-            assert(removed);
-            
-            throw err;
-        }
-        
-        try {
-            yield new_session.initiate_session_async(account_information.imap_credentials, cancellable);
-        } catch (Error err) {
-            debug("[%s] Initiate session failure: %s", new_session.to_string(), err.message);
-            
-            // need to disconnect before throwing error ... don't honor Cancellable here, it's
-            // important to disconnect the client before dropping the ref
-            try {
-                yield new_session.disconnect_async();
-            } catch (Error disconnect_err) {
-                debug("[%s] Error disconnecting due to session initiation failure, ignored: %s",
-                    new_session.to_string(), disconnect_err.message);
+        ClientSession? claimed = null;
+        while (claimed == null) {
+            // This isn't racy since this is class is not accessed by
+            // multiple threads. Don't wait for it though because we
+            // only want to kick off establishing the connection, and
+            // wait for it via the queue.
+            if (this.free_queue.size == 0) {
+                check_pool.begin();
             }
-            
-            bool removed;
-            if (sessions_mutex.is_locked())
-                removed = locked_remove_session(new_session);
-            else
-                removed = yield unlocked_remove_session_async(new_session);
-            assert(removed);
-            
-            throw err;
-        }
 
-        if (!this.is_ready) {
-            this.is_ready = true;
-            ready();
-        }
+            claimed = yield this.free_queue.receive(cancellable);
 
-        // do this after logging in
-        new_session.enable_keepalives(selected_keepalive_sec, unselected_keepalive_sec,
-            selected_with_idle_keepalive_sec);
-        
-        // since "disconnected" is used to remove the ClientSession from the sessions list, want
-        // to only connect to the signal once the object has been added to the list; otherwise it's
-        // possible a cancel during the connect or login will result in a "disconnected" signal,
-        // removing the session before it's added
-        new_session.disconnected.connect(on_disconnected);
-        
-        return new_session;
-    }
-    
-    public async ClientSession claim_authorized_session_async(Cancellable? cancellable) throws Error {
-        check_open();
-        
-        int token = yield sessions_mutex.claim_async(cancellable);
-        
-        ClientSession? found_session = null;
-        foreach (ClientSession session in sessions) {
-            MailboxSpecifier? mailbox;
-            if (!reserved_sessions.contains(session) &&
-                (session.get_protocol_state(out mailbox) == ClientSession.ProtocolState.AUTHORIZED)) {
-                found_session = session;
-                
-                break;
+            // Connection may have gone bad sitting in the queue, so
+            // check it before using it
+            if (!(yield check_session(claimed, false))) {
+                claimed = null;
             }
         }
-        
-        Error? err = null;
-        try {
-            if (found_session == null)
-                found_session = yield create_new_authorized_session(cancellable);
-        } catch (Error create_err) {
-            debug("Error creating session: %s", create_err.message);
-            err = create_err;
-        }
-        
-        // claim it now
-        if (found_session != null) {
-            bool added = reserved_sessions.add(found_session);
-            assert(added);
-        }
-        
-        try {
-            sessions_mutex.release(ref token);
-        } catch (Error release_err) {
-            debug("Error releasing sessions table mutex: %s", release_err.message);
-        }
-        
-        if (err != null)
-            throw err;
-        
-        return found_session;
+
+        return claimed;
     }
 
     public async void release_session_async(ClientSession session, Cancellable? cancellable)
         throws Error {
         // Don't check_open(), it's valid for this to be called when
         // is_open is false, that happens during mop-up
-        MailboxSpecifier? mailbox = null;
-        ClientSession.ProtocolState context = session.get_protocol_state(out mailbox);
 
-        if (context == ClientSession.ProtocolState.UNCONNECTED) {
-            // Already disconnected, so drop it on the floor
-            try {
-                yield unlocked_remove_session_async(session);
-            } catch (Error err) {
-                debug("[%s] Error removing unconnected session: %s",
-                      to_string(), err.message);
-            }
-        } else if (this.is_open && !this.discard_returned_sessions) {
-            bool free = false;
-            switch (context) {
-            case ClientSession.ProtocolState.AUTHORIZED:
-            case ClientSession.ProtocolState.CLOSING_MAILBOX:
-                // keep as-is, but add back to the free list
-                free = true;
-                break;
+        debug("[%s] Returning session with %d of %d free",
+              to_string(), this.free_queue.size, this.all_sessions.size);
 
-            case ClientSession.ProtocolState.SELECTED:
-            case ClientSession.ProtocolState.SELECTING:
+        if (!this.is_open || this.discard_returned_sessions) {
+            yield force_disconnect(session);
+        } else if (yield check_session(session, true)) {
+            bool free = true;
+            MailboxSpecifier? mailbox = null;
+            ClientSession.ProtocolState proto = session.get_protocol_state(out mailbox);
+            // If the session has a mailbox selected, close it before
+            // adding it back to the pool
+            if (proto == ClientSession.ProtocolState.SELECTED ||
+                proto == ClientSession.ProtocolState.SELECTING) {
                 debug("[%s] Closing %s for released session %s",
                       to_string(),
                       mailbox != null ? mailbox.to_string() : "(unknown)",
@@ -378,60 +282,161 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
                 } catch (ImapError imap_error) {
                     debug("[%s] Error attempting to close released session %s: %s",
                           to_string(), session.to_string(), imap_error.message);
+                    free = false;
                 }
 
-                if (session.get_protocol_state(out mailbox) == ClientSession.ProtocolState.AUTHORIZED) {
-                    // Now in authorized state, free it up for re-use
-                    free = true;
-                } else {
+                if (session.get_protocol_state(null) !=
+                    ClientSession.ProtocolState.AUTHORIZED) {
                     // Closing it didn't work, so drop it
                     yield force_disconnect(session);
+                    free = false;
                 }
-                break;
-
-            default:
-                // This class is tasked with holding onto a pool of
-                // authorized connections, so if one is released
-                // outside that state, pessimistically drop it
-                yield force_disconnect(session);
-                break;
             }
 
             if (free) {
                 debug("[%s] Unreserving session %s",
                       to_string(), session.to_string());
-                try {
-                    int token = yield sessions_mutex.claim_async(cancellable);
-                    this.reserved_sessions.remove(session);
-                    this.sessions_mutex.release(ref token);
-                } catch (Error err) {
-                    message("[%s] Unable to add %s to the free list: %s",
-                            to_string(), session.to_string(), err.message);
-                }
+                this.free_queue.send(session);
             }
-        } else {
-            // Not open, or we are discarding sessions, so close it.
-            yield force_disconnect(session);
         }
+    }
+
+    private void check_open() throws Error {
+        if (!is_open)
+            throw new EngineError.OPEN_REQUIRED("ClientSessionManager is not open");
+    }
 
-        // If we're discarding returned sessions, we don't want to
-        // create any more, so only twiddle the pool if not.
-        if (!this.discard_returned_sessions) {
-            this.adjust_session_pool.begin();
+    private async void check_pool() {
+        debug("[%s] Checking session pool with %d of %d free",
+              to_string(), this.free_queue.size, this.all_sessions.size);
+
+        while (this.is_open &&
+               !this.authentication_failed &&
+               !this.untrusted_host &&
+               this.endpoint.connectivity.is_reachable.is_certain()) {
+            // Open pool sessions serially to avoid hammering the server
+            try {
+                ClientSession free = yield this.create_new_authorized_session(
+                    this.pool_cancellable
+                );
+                yield this.sessions_mutex.execute_locked(() => {
+                        this.all_sessions.add(free);
+                    });
+
+                this.free_queue.send(free);
+            } catch (Error err) {
+                debug("[%s] Error adding free session pool: %s",
+                      to_string(),
+                      err.message);
+                break;
+            }
+
+            if (this.all_sessions.size >= this.min_pool_size) {
+                break;
+            }
         }
     }
 
+    /** Determines if a session is valid, disposing of it if not. */
+    private async bool check_session(ClientSession target, bool allow_selected) {
+        bool valid = false;
+        switch (target.get_protocol_state(null)) {
+        case ClientSession.ProtocolState.AUTHORIZED:
+        case ClientSession.ProtocolState.CLOSING_MAILBOX:
+            valid = true;
+            break;
+
+        case ClientSession.ProtocolState.SELECTED:
+        case ClientSession.ProtocolState.SELECTING:
+            if (allow_selected) {
+                valid = true;
+            } else {
+                yield force_disconnect(target);
+            }
+            break;
+
+        case ClientSession.ProtocolState.UNCONNECTED:
+            // Already disconnected, so drop it on the floor
+            try {
+                yield remove_session_async(target);
+            } catch (Error err) {
+                debug("[%s] Error removing unconnected session: %s",
+                      to_string(), err.message);
+            }
+            break;
+
+        default:
+            yield force_disconnect(target);
+            break;
+        }
+
+        return valid;
+    }
+
+    private async ClientSession create_new_authorized_session(Cancellable? cancellable) throws Error {
+        ClientSession new_session = new ClientSession(endpoint);
+
+        // Listen for auth failures early so the client is notified if
+        // there is an error, even though we won't want to keep the
+        // session around.
+        new_session.login_failed.connect(on_login_failed);
+
+        try {
+            yield new_session.connect_async(cancellable);
+        } catch (Error err) {
+            debug("[%s] Connect failure: %s", new_session.to_string(), err.message);
+            connection_failed(err);
+            throw err;
+        }
+
+        try {
+            yield new_session.initiate_session_async(account_information.imap_credentials, cancellable);
+        } catch (Error err) {
+            debug("[%s] Initiate session failure: %s", new_session.to_string(), err.message);
+
+            // need to disconnect before throwing error ... don't honor Cancellable here, it's
+            // important to disconnect the client before dropping the ref
+            try {
+                yield new_session.disconnect_async();
+            } catch (Error disconnect_err) {
+                debug("[%s] Error disconnecting due to session initiation failure, ignored: %s",
+                    new_session.to_string(), disconnect_err.message);
+            }
+
+            connection_failed(err);
+            throw err;
+        }
+
+        // Only bother tracking disconnects and enabling keeping alive
+        // now the session is properly established.
+        new_session.disconnected.connect(on_disconnected);
+        new_session.enable_keepalives(selected_keepalive_sec,
+                                      unselected_keepalive_sec,
+                                      selected_with_idle_keepalive_sec);
+
+        // We now have a good connection, so signal us as ready if not
+        // already done so.
+        if (!this.is_ready) {
+            this.is_ready = true;
+            ready();
+        }
+
+        return new_session;
+    }
+
+    /** Disconnects all sessions in the pool. */
     private async void force_disconnect_all()
         throws Error {
         debug("[%s] Dropping and disconnecting %d sessions",
-              to_string(), this.sessions.size);
+              to_string(), this.all_sessions.size);
 
         // Take a copy and work off that while scheduling disconnects,
         // since as they disconnect they'll remove themselves from the
         // sessions list and cause the loop below to explode.
-        int token = yield this.sessions_mutex.claim_async();
-        ClientSession[] to_close = this.sessions.to_array();
-        this.sessions_mutex.release(ref token);
+        ClientSession[]? to_close = null;
+        yield this.sessions_mutex.execute_locked(() => {
+                to_close = this.all_sessions.to_array();
+            });
 
         // Disconnect all existing sessions at once. Don't block
         // waiting for any since we don't want to delay closing the
@@ -445,7 +450,7 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
         debug("[%s] Dropping session %s", to_string(), session.to_string());
 
         try {
-            yield unlocked_remove_session_async(session);
+            yield remove_session_async(session);
         } catch (Error err) {
             debug("[%s] Error removing session: %s", to_string(), err.message);
         }
@@ -455,12 +460,29 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
         session.disconnect_async.begin();
     }
 
+    private async bool remove_session_async(ClientSession session) throws Error {
+        // Ensure the session isn't held on to, anywhere
+
+        this.free_queue.revoke(session);
+
+        bool removed = false;
+        yield this.sessions_mutex.execute_locked(() => {
+                removed = this.all_sessions.remove(session);
+            });
+
+        if (removed) {
+            session.disconnected.disconnect(on_disconnected);
+            session.login_failed.disconnect(on_login_failed);
+        }
+        return removed;
+    }
+
     private void on_disconnected(ClientSession session, ClientSession.DisconnectReason reason) {
-        this.unlocked_remove_session_async.begin(
+        this.remove_session_async.begin(
             session,
             (obj, res) => {
                 try {
-                    this.unlocked_remove_session_async.end(res);
+                    this.remove_session_async.end(res);
                 } catch (Error err) {
                     debug("[%s] Error removing disconnected session: %s",
                           to_string(),
@@ -477,56 +499,20 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
         session.disconnect_async.begin();
     }
 
-    // Only call with sessions mutex locked
-    private void locked_add_session(ClientSession session) {
-        sessions.add(session);
-        
-        // See create_new_authorized_session() for why the "disconnected" signal is not subscribed
-        // to here (but *is* unsubscribed to in remove_session())
-        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);
-        if (removed) {
-            session.disconnected.disconnect(on_disconnected);
-            session.login_failed.disconnect(on_login_failed);
-        }
-        
-        reserved_sessions.remove(session);
-        
-        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
         untrusted_host = true;
     }
-    
+
     private void on_imap_trust_untrusted_host() {
         // fired when the trust_untrusted_host property changes, indicating if the user has agreed
         // to ignore the trust problems and continue connecting
         if (untrusted_host && endpoint.trust_untrusted_host == Trillian.TRUE) {
             untrusted_host = false;
-            
+
             if (is_open)
-                adjust_session_pool.begin();
+                check_pool.begin();
         }
     }
 
@@ -554,7 +540,9 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
      * Use only for debugging and logging.
      */
     public string to_string() {
-        return "ClientSessionManager/%s %d sessions, %d reserved".printf(endpoint.to_string(),
-            sessions.size, reserved_sessions.size);
+        return "%s:%s".printf(
+            this.account_information.id,
+            endpoint.to_string()
+        );
     }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]