Fix IMAP deadlock when the network becomes unreachable (bug 410747), patch=Emre Birol <bugmil.ebirol@gmail.com>, r=bienvenu, sr=dmose, a=blocking-3.0a1+

This commit is contained in:
dmose%mozilla.org 2008-04-30 01:25:36 +00:00
Родитель c5ad605034
Коммит dd2c44c1e9
4 изменённых файлов: 103 добавлений и 27 удалений

Просмотреть файл

@ -54,6 +54,7 @@ XPIDLSRCS = \
nsIImapService.idl \
nsIImapServerSink.idl \
nsIImapMessageSink.idl \
nsIImapProtocolSink.idl \
nsIImapIncomingServer.idl \
nsIImapFlagAndUidState.idl \
nsIImapMockChannel.idl \

Просмотреть файл

@ -0,0 +1,50 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla Messaging, Inc.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Emre Birol <ebirol@gmail.com> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
/**
* Helper interface that contains operations MUST be proxied
* over UI thread.
*/
[scriptable, uuid(72187a30-dad7-4800-b989-adfcbdc9a650)]
interface nsIImapProtocolSink : nsISupports {
/**
* Does general cleanup for the imap protocol object.
*/
void closeStreams();
};

Просмотреть файл

@ -108,6 +108,7 @@ PRLogModuleInfo *IMAP;
#include "nsIProxyInfo.h"
#include "nsISSLSocketControl.h"
#include "nsProxyRelease.h"
#include "nsDebug.h"
#define ONE_SECOND ((PRUint32)1000) // one second
@ -300,6 +301,7 @@ NS_INTERFACE_MAP_BEGIN(nsImapProtocol)
NS_INTERFACE_MAP_ENTRY(nsIImapProtocol)
NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIImapProtocolSink)
NS_INTERFACE_MAP_END_THREADSAFE
static PRInt32 gTooFastTime = 2;
@ -630,6 +632,17 @@ nsImapProtocol::SetupSinkProxy()
getter_AddRefs(m_imapServerSink));
NS_ASSERTION(NS_SUCCEEDED(res), "couldn't get proxies");
}
if (!m_imapProtocolSink)
{
nsCOMPtr<nsIImapProtocolSink> anImapProxyHelper(do_QueryInterface(NS_ISUPPORTS_CAST(nsIImapProtocolSink*, this), &res));
if (NS_SUCCEEDED(res) && anImapProxyHelper)
res = proxyManager->GetProxyForObject( m_sinkEventTarget,
NS_GET_IID(nsIImapProtocolSink),
anImapProxyHelper,
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(m_imapProtocolSink));
NS_ASSERTION(NS_SUCCEEDED(res), "couldn't get proxies");
}
}
else
NS_ASSERTION(PR_FALSE, "can't get proxy service");
@ -946,30 +959,30 @@ private:
NS_IMETHODIMP nsImapProtocol::Run()
{
nsImapProtocol *me = this;
NS_ASSERTION(me, "Yuk, me is null.\n");
PR_CEnterMonitor(this);
NS_ASSERTION(me->m_imapThreadIsRunning == PR_FALSE,
NS_ASSERTION(m_imapThreadIsRunning == PR_FALSE,
"Oh. oh. thread is already running. What's wrong here?");
if (me->m_imapThreadIsRunning)
if (m_imapThreadIsRunning)
{
PR_CExitMonitor(me);
PR_CExitMonitor(this);
return NS_OK;
}
me->m_imapThreadIsRunning = PR_TRUE;
PR_CExitMonitor(me);
m_imapThreadIsRunning = PR_TRUE;
PR_CExitMonitor(this);
// call the platform specific main loop ....
me->ImapThreadMainLoop();
ImapThreadMainLoop();
me->m_runningUrl = nsnull;
CloseStreams();
me->m_sinkEventTarget = nsnull;
me->m_imapMailFolderSink = nsnull;
me->m_imapMessageSink = nsnull;
m_runningUrl = nsnull;
// close streams via UI thread if it's not already done
if (m_imapProtocolSink)
m_imapProtocolSink->CloseStreams();
m_sinkEventTarget = nsnull;
m_imapMailFolderSink = nsnull;
m_imapMessageSink = nsnull;
// shutdown this thread, but do it from the main thread
nsCOMPtr<nsIRunnable> ev = new nsImapThreadShutdownEvent(m_iThread);
@ -979,11 +992,14 @@ NS_IMETHODIMP nsImapProtocol::Run()
return NS_OK;
}
// called from UI thread.
// XXXbz except this is called from TellThreadToDie, which can get called on
// either the UI thread or the IMAP protocol thread, per comments.
void nsImapProtocol::CloseStreams()
//
// Must be called from UI thread only
//
NS_IMETHODIMP nsImapProtocol::CloseStreams()
{
// make sure that it is called by the UI thread
NS_ABORT_IF_FALSE(NS_IsMainThread(), "CloseStreams() should not be called from an off UI thread");
PR_CEnterMonitor(this);
if (m_transport)
{
@ -994,15 +1010,11 @@ void nsImapProtocol::CloseStreams()
}
m_inputStream = nsnull;
m_outputStream = nsnull;
// XXXbz given that this can get called from off the UI thread, does the
// release of m_channelListener need to be proxied?
m_channelListener = nsnull;
m_channelContext = nsnull;
if (m_mockChannel)
{
m_mockChannel->Close();
// XXXbz given that this can get called from off the UI thread, does the
// release of m_mockChannel need to be proxied?
m_mockChannel = nsnull;
}
m_channelInputStream = nsnull;
@ -1024,6 +1036,7 @@ void nsImapProtocol::CloseStreams()
me_server = nsnull;
}
m_server = nsnull;
return NS_OK;
}
@ -1089,7 +1102,12 @@ nsImapProtocol::TellThreadToDie(PRBool isSafeToClose)
Logout(PR_TRUE, connectionIdle);
}
}
CloseStreams();
// close streams via UI thread
if (m_imapProtocolSink)
{
m_imapProtocolSink->CloseStreams();
m_imapProtocolSink = nsnull;
}
Log("TellThreadToDie", nsnull, "close socket connection");
PR_EnterMonitor(m_threadDeathMonitor);

Просмотреть файл

@ -61,6 +61,9 @@
#include "nsIImapServerSink.h"
#include "nsIImapMessageSink.h"
// UI Thread proxy helper
#include "nsIImapProtocolSink.h"
#include "nsImapServerResponseParser.h"
#include "nsImapFlagAndUidState.h"
#include "nsIMAPNamespace.h"
@ -150,7 +153,7 @@ public:
#define IMAP_ISSUED_LANGUAGE_REQUEST 0x00000020 // make sure we only issue the language request once per connection...
class nsImapProtocol : public nsIImapProtocol, public nsIRunnable, public nsIInputStreamCallback,
public nsSupportsWeakReference, public nsMsgProtocol
public nsSupportsWeakReference, public nsMsgProtocol, public nsIImapProtocolSink
{
public:
@ -170,8 +173,11 @@ public:
//////////////////////////////////////////////////////////////////////////////////
NS_DECL_NSIIMAPPROTOCOL
void CloseStreams();
//////////////////////////////////////////////////////////////////////////////////
// we support the nsIImapProtocolSink interface
//////////////////////////////////////////////////////////////////////////////////
NS_DECL_NSIIMAPPROTOCOLSINK
// message id string utilities.
PRUint32 CountMessagesInIdString(const char *idString);
static PRBool HandlingMultipleMessages(const nsCString &messageIdString);
@ -372,6 +378,7 @@ private:
nsCOMPtr<nsIImapMailFolderSink> m_imapMailFolderSink;
nsCOMPtr<nsIImapMessageSink> m_imapMessageSink;
nsCOMPtr<nsIImapServerSink> m_imapServerSink;
nsCOMPtr<nsIImapProtocolSink> m_imapProtocolSink;
// helper function to setup imap sink interface proxies
void SetupSinkProxy();