private void IdleLoop(IdleState idle) { lock (idle.Client.SyncRoot) { // Note: since the IMAP server will drop the connection after 30 minutes, we must loop sending IDLE commands that // last ~29 minutes or until the user has requested that they do not want to IDLE anymore. // For GMail, we use a 9 minute interval because they do not seem to keep the connection alive for more than ~10 minutes. while (!idle.IsCancellationRequested) { using (var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(_imapClientCanIdle ? 9 : 1))) { try { // We set the timeout source so that if the idle.DoneToken is cancelled, it can cancel the timeout idle.SetTimeoutSource(timeout); if (_imapClientCanIdle) { // The Idle() method will not return until the timeout has elapsed or idle.CancellationToken is cancelled idle.Client.Idle(timeout.Token); } else { // The IMAP server does not support IDLE, so send a NOOP command instead idle.Client.NoOp(idle.DoneToken); // Wait for the timeout to elapse or the cancellation token to be cancelled WaitHandle.WaitAny(new[] { timeout.Token.WaitHandle, idle.DoneToken.WaitHandle }); } } finally { // we will dispose timeout, dont throw an exeption because we are trying to cancel it idle.SetTimeoutSource(null); } if (timeout.IsCancellationRequested) { _config.Tracer?.TraceVerbose($"Idle timed out, looping", $"{this}"); } } } } }
static void IdleLoop(object state) { IdleState idle = (IdleState)state; lock (idle.Client.SyncRoot) { // Note: since the IMAP server will drop the connection after 30 minutes, we must loop sending IDLE commands that // last ~29 minutes or until the user has requested that they do not want to IDLE anymore. // // For GMail, we use a 9 minute interval because they do not seem to keep the connection alive for more than ~10 minutes. while (!idle.IsCancellationRequested) { using (var timeout = new CancellationTokenSource(new TimeSpan(0, 9, 0))) { try { // We set the timeout source so that if the idle.DoneToken is cancelled, it can cancel the timeout idle.SetTimeoutSource(timeout); if (idle.Client.Capabilities.HasFlag(ImapCapabilities.Idle)) { // The Idle() method will not return until the timeout has elapsed or idle.CancellationToken is cancelled idle.Client.Idle(timeout.Token, idle.CancellationToken); } else { // The IMAP server does not support IDLE, so send a NOOP command instead idle.Client.NoOp(idle.CancellationToken); // Wait for the timeout to elapse or the cancellation token to be cancelled WaitHandle.WaitAny(new[] { timeout.Token.WaitHandle, idle.CancellationToken.WaitHandle }); } } catch (OperationCanceledException) { // This means that idle.CancellationToken was cancelled, not the DoneToken nor the timeout. break; } catch (ImapProtocolException) { // The IMAP server sent garbage in a response and the ImapClient was unable to deal with it. // This should never happen in practice, but it's probably still a good idea to handle it. // // Note: an ImapProtocolException almost always results in the ImapClient getting disconnected. break; } catch (ImapCommandException) { // The IMAP server responded with "NO" or "BAD" to either the IDLE command or the NOOP command. // This should never happen... but again, we're catching it for the sake of completeness. break; } finally { // We're about to Dispose() the timeout source, so set it to null. idle.SetTimeoutSource(null); } } } } }