add support for closing inactive databases, r=neil, sr=standard8,
bug 723248
--- a/mail/base/content/msgMail3PaneWindow.js
+++ b/mail/base/content/msgMail3PaneWindow.js
@@ -48,16 +48,17 @@ Components.utils.import("resource:///mod
Components.utils.import("resource:///modules/jsTreeSelection.js");
Components.utils.import("resource:///modules/MailConsts.js");
Components.utils.import("resource:///modules/errUtils.js");
Components.utils.import("resource:///modules/IOUtils.js");
Components.utils.import("resource:///modules/mailnewsMigrator.js");
Components.utils.import("resource:///modules/sessionStoreManager.js");
Components.utils.import("resource:///modules/summaryFrameManager.js");
Components.utils.import("resource:///modules/mailInstrumentation.js");
+Components.utils.import("resource:///modules/msgDBCacheManager.js");
/* This is where functions related to the 3 pane window are kept */
// from MailNewsTypes.h
const nsMsgKey_None = 0xFFFFFFFF;
const nsMsgViewIndex_None = 0xFFFFFFFF;
const kMailCheckOncePrefName = "mail.startup.enabledMailCheckOnce";
@@ -377,16 +378,17 @@ function OnLoadMessenger()
gPrefBranch.addObserver("mail.pane_config.dynamic", MailPrefObserver, false);
gPrefBranch.addObserver("mail.showCondensedAddresses", MailPrefObserver,
false);
MailOfflineMgr.init();
CreateMailWindowGlobals();
GetMessagePaneWrapper().collapsed = true;
+ msgDBCacheManager.init();
// This needs to be before we throw up the account wizard on first run.
try {
mailInstrumentationManager.init();
} catch(ex) {logException(ex);}
// - initialize tabmail system
// Do this before LoadPostAccountWizard since that code selects the first
--- a/mail/base/modules/Makefile.in
+++ b/mail/base/modules/Makefile.in
@@ -48,16 +48,17 @@ EXTRA_JS_MODULES = \
MailUtils.js \
attachmentChecker.js \
dbViewWrapper.js \
mailViewManager.js \
quickFilterManager.js \
searchSpec.js \
MsgHdrSyntheticView.js \
sessionStoreManager.js \
+ msgDBCacheManager.js \
summaryFrameManager.js \
mailMigrator.js \
mailInstrumentation.js \
glodaWebSearch.js \
oauth.jsm \
http.jsm \
distribution.js \
$(NULL)
new file mode 100644
--- /dev/null
+++ b/mail/base/modules/msgDBCacheManager.js
@@ -0,0 +1,148 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Message DB Cache manager
+ */
+
+/* :::::::: Constants and Helpers ::::::::::::::: */
+
+const EXPORTED_SYMBOLS = ["msgDBCacheManager"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource:///modules/IOUtils.js");
+Cu.import("resource:///modules/iteratorUtils.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+/**
+ */
+const DBCACHE_INTERVAL_DEFAULT_MS = 60000; // 1 minute
+
+/* :::::::: The Module ::::::::::::::: */
+
+var msgDBCacheManager =
+{
+ _initialized: false,
+
+ _msgDBCacheTimer: null,
+
+ _msgDBCacheTimerIntervalMS: DBCACHE_INTERVAL_DEFAULT_MS,
+
+ /**
+ * This is called on startup
+ */
+ init: function dbcachemgr_init()
+ {
+
+ // we listen for "quit-application-granted" instead of
+ // "quit-application-requested" because other observers of the
+ // latter can cancel the shutdown.
+ var observerSvc = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ observerSvc.addObserver(this, "quit-application-granted", false);
+
+ this.startPeriodicCheck();
+
+ this._initialized = true;
+ },
+
+/* ........ Timer Callback ................*/
+
+ _dbCacheCheckTimerCallback: function dbCache_CheckTimerCallback()
+ {
+ msgDBCacheManager.checkCachedDBs();
+ },
+
+/* ........ Observer Notification Handler ................*/
+
+ observe: function dbCache_observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ // This is observed before any windows start unloading if something other
+ // than the last 3pane window closing requested the application be
+ // shutdown. For example, when the user quits via the file menu.
+ case "quit-application-granted":
+ this.stopPeriodicCheck();
+ break;
+ }
+ },
+
+/* ........ Public API ................*/
+
+ /**
+ * Stops db cache check
+ */
+ stopPeriodicCheck: function dbcache_stopPeriodicCheck()
+ {
+ if (this._dbCacheCheckTimer) {
+ this._dbCacheCheckTimer.cancel();
+
+ delete this._dbCacheCheckTimer;
+ this._dbCacheCheckTimer = null;
+ }
+ },
+
+ /**
+ * Starts periodic db cache check
+ */
+ startPeriodicCheck: function dbcache_startPeriodicCheck()
+ {
+ if (!this._dbCacheCheckTimer) {
+ this._dbCacheCheckTimer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+
+ this._dbCacheCheckTimer.initWithCallback(
+ this._dbCacheCheckTimerCallback,
+ this._msgDBCacheTimerIntervalMS,
+ Ci.nsITimer.TYPE_REPEATING_SLACK);
+ }
+ },
+ checkCachedDBs : function ()
+ {
+ const gDbService = Cc["@mozilla.org/msgDatabase/msgDBService;1"]
+ .getService(Ci.nsIMsgDBService);
+ const mailSession = Cc["@mozilla.org/messenger/services/session;1"]
+ .getService(Ci.nsIMsgMailSession);
+
+ let idleLimit = Services.prefs.getIntPref("mail.db.idle_limit");
+ let maxOpenDBs = Services.prefs.getIntPref("mail.db.max_open");
+
+ let closeThreshold = Date.now() - idleLimit;
+ const nsMsgFolderFlags = Ci.nsMsgFolderFlags;
+ let cachedDBs = gDbService.openDBs;
+ let numOpenDBs = 0;
+ for (let i = 0; i < cachedDBs.length; i++) {
+ db = cachedDBs.queryElementAt(i, Ci.nsIMsgDatabase);
+ if (mailSession.IsFolderOpenInWindow(db.folder)) {
+ numOpenDBs++;
+ continue;
+ }
+ let lruTime = db.lastUseTime / 1000;
+ if (lruTime < closeThreshold)
+ db.folder.msgDatabase = null;
+ numOpenDBs++;
+ }
+ let openDBs = gDbService.openDBs;
+ if (numOpenDBs > maxOpenDBs) {
+ let dbs = [];
+ for (let i = 0; i < openDBs.length; i++)
+ dbs.push(openDBs.queryElementAt(i, Ci.nsIMsgDatabase));
+ function sortByLastUse(a, b) {
+ return a.lastUseTime > b.lastUseTime;
+ }
+ dbs.sort(sortByLastUse);
+ let dbsToClose = maxOpenDBs - dbs.length;
+ for each (let [, db] in Iterator(dbs)) {
+ if (mailSession.IsFolderOpenInWindow(db.folder))
+ continue;
+ db.folder.msgDatabase = null;
+ if (--dbsToClose == 0)
+ break;
+ }
+ }
+ },
+};
--- a/mailnews/base/util/nsMsgDBFolder.cpp
+++ b/mailnews/base/util/nsMsgDBFolder.cpp
@@ -949,16 +949,17 @@ nsresult nsMsgDBFolder::CreateFileForDB(
NS_IMETHODIMP
nsMsgDBFolder::GetMsgDatabase(nsIMsgDatabase** aMsgDatabase)
{
NS_ENSURE_ARG_POINTER(aMsgDatabase);
GetDatabase();
if (!mDatabase)
return NS_ERROR_FAILURE;
NS_ADDREF(*aMsgDatabase = mDatabase);
+ mDatabase->SetLastUseTime(PR_Now());
return NS_OK;
}
NS_IMETHODIMP
nsMsgDBFolder::SetMsgDatabase(nsIMsgDatabase *aMsgDatabase)
{
if (mDatabase)
{
--- a/mailnews/db/msgdb/public/nsIMsgDatabase.idl
+++ b/mailnews/db/msgdb/public/nsIMsgDatabase.idl
@@ -130,17 +130,17 @@ interface nsMsgDBCommitType
[ptr] native nsMsgKeyArrayPtr(nsTArray<nsMsgKey>);
/**
* A service to open mail databases and manipulate listeners automatically.
*
* The contract ID for this component is
* <tt>\@mozilla.org/msgDatabase/msgDBService;1</tt>.
*/
-[scriptable, uuid(09f52152-a3b7-4b89-935d-4033cbaea7e0)]
+[scriptable, uuid(e4743a99-ca44-4647-87b4-d73a444226b3)]
interface nsIMsgDBService : nsISupports
{
/**
* Opens a database for a given folder.
*
* This method is preferred over nsIMsgDBService::openMailDBFromFile if the
* caller has an actual nsIMsgFolder around. If the database detects that it
* is unreadable or out of date (using nsIMsgDatabase::outOfDate) it will
@@ -260,32 +260,49 @@ interface nsIMsgDBService : nsISupports
/**
* Get the db for a folder, if already open.
*
* @param aFolder The folder to get the cached (open) db for.
*
* @returns null if the db isn't open, otherwise the db.
*/
nsIMsgDatabase cachedDBForFolder(in nsIMsgFolder aFolder);
+
+ /// an enumerator to iterate over the open dbs.
+ readonly attribute nsIArray openDBs;
};
-[scriptable, uuid(a3cd60ac-d279-4521-8880-2e7723315cba)]
+[scriptable, uuid(21122b78-12ed-4657-aa07-29e109667825)]
interface nsIMsgDatabase : nsIDBChangeAnnouncer {
void forceFolderDBClosed(in nsIMsgFolder aFolder);
void Close(in boolean aForceCommit);
void Commit(in nsMsgDBCommit commitType);
// Force closed is evil, and we should see if we can do without it.
// In 4.x, it was mainly used to remove corrupted databases.
void ForceClosed();
void clearCachedHdrs();
void resetHdrCacheSize(in unsigned long size);
readonly attribute nsIDBFolderInfo dBFolderInfo;
+ /// Folder this db was opened on.
+ readonly attribute nsIMsgFolder folder;
+ /**
+ * This is used when deciding which db's to close to free up memory
+ * and other resources in an LRU manner. It doesn't track every operation
+ * on every object from the db, but high level things like open, commit,
+ * and perhaps some of the list methods. Commit should be a proxy for all
+ * the mutation methods.
+ *
+ * I'm allowing clients to set the last use time as well, so that
+ * nsIMsgFolder.msgDatabase can set the last use time.
+ */
+ attribute PRTime lastUseTime;
+
// get a message header for the given key. Caller must release()!
nsIMsgDBHdr GetMsgHdrForKey(in nsMsgKey key);
nsIMsgDBHdr getMsgHdrForMessageID(in string messageID);
//Returns whether or not this database contains the given key
boolean ContainsKey(in nsMsgKey key);
/**
@@ -457,17 +474,17 @@ interface nsIMsgDatabase : nsIDBChangeAn
// purge unwanted message headers and/or bodies. If deleteViaFolder is
// true, we'll call nsIMsgFolder::DeleteMessages to delete the messages.
// Otherwise, we'll just delete them from the db.
void applyRetentionSettings(in nsIMsgRetentionSettings aMsgRetentionSettings,
in boolean aDeleteViaFolder);
attribute nsIMsgDownloadSettings msgDownloadSettings;
- boolean HasNew();
+ boolean HasNew();
void ClearNewList(in boolean notify);
void AddToNewList(in nsMsgKey key);
// used mainly to force the timestamp of a local mail folder db to
// match the time stamp of the corresponding berkeley mail folder,
// but also useful to tell the summary to mark itself invalid
// Also, if a local folder is being reparsed, summary will be invalid
// until the reparsing is done.
--- a/mailnews/db/msgdb/public/nsMsgDatabase.h
+++ b/mailnews/db/msgdb/public/nsMsgDatabase.h
@@ -263,17 +263,22 @@ protected:
virtual bool UseStrictThreading();
virtual bool UseCorrectThreading();
virtual nsresult ThreadNewHdr(nsMsgHdr* hdr, bool &newThread);
virtual nsresult AddNewThread(nsMsgHdr *msgHdr);
virtual nsresult AddToThread(nsMsgHdr *newHdr, nsIMsgThread *thread, nsIMsgDBHdr *pMsgHdr, bool threadInThread);
static nsTArray<nsMsgDatabase*>* m_dbCache;
static nsTArray<nsMsgDatabase*>* GetDBCache();
-
+
+ static PRTime gLastUseTime; // global last use time
+ PRTime m_lastUseTime; // last use time for this db
+ // inline to make instrumentation as cheap as possible
+ inline void RememberLastUseTime() {gLastUseTime = m_lastUseTime = PR_Now();}
+
static void AddToCache(nsMsgDatabase* pMessageDB)
{
#ifdef DEBUG_David_Bienvenu
// NS_ASSERTION(GetDBCache()->Length() < 50, "50 or more open db's");
#endif
#ifdef DEBUG
nsCOMPtr<nsIMsgDatabase> msgDB = pMessageDB->m_folder ?
dont_AddRef(FindInCache(pMessageDB->m_folder)) : nsnull;
@@ -312,24 +317,25 @@ protected:
bool keepUnreadMessagesOnly,
bool applyToFlaggedMessages,
nsIMutableArray *hdrsToDelete);
// mdb bookkeeping stuff
virtual nsresult InitExistingDB();
virtual nsresult InitNewDB();
virtual nsresult InitMDBInfo();
-
+
nsCOMPtr <nsIMsgFolder> m_folder;
nsDBFolderInfo *m_dbFolderInfo;
nsMsgKey m_nextPseudoMsgKey;
nsIMdbEnv *m_mdbEnv; // to be used in all the db calls.
nsIMdbStore *m_mdbStore;
nsIMdbTable *m_mdbAllMsgHeadersTable;
nsIMdbTable *m_mdbAllThreadsTable;
+
// Used for asynchronous db opens. If non-null, we're still opening
// the underlying mork database. If null, the db has been completely opened.
nsCOMPtr<nsIMdbThumb> m_thumb;
// used to remember the args to Open for async open.
bool m_create;
bool m_leaveInvalidDB;
nsCString m_dbName;
--- a/mailnews/db/msgdb/src/nsMsgDatabase.cpp
+++ b/mailnews/db/msgdb/src/nsMsgDatabase.cpp
@@ -75,16 +75,17 @@
#include "nsServiceManagerUtils.h"
#include "nsMemory.h"
#include "nsICollation.h"
#include "nsCollationCID.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIMsgPluggableStore.h"
#include "nsAlgorithm.h"
+#include "nsArrayEnumerator.h"
#if defined(DEBUG_sspitzer_) || defined(DEBUG_seth_)
#define DEBUG_MSGKEYSET 1
#endif
#define MSG_HASH_SIZE 512
// This will be used on discovery, since we don't know total.
@@ -99,16 +100,18 @@ const PRUint32 kInitialMsgDBCacheSize =
static const nsMsgKey kAllMsgHdrsTableKey = 1;
static const nsMsgKey kTableKeyForThreadOne = 0xfffffffe;
static const nsMsgKey kAllThreadsTableKey = 0xfffffffd;
static const nsMsgKey kFirstPseudoKey = 0xfffffff0;
static const nsMsgKey kIdStartOfFake = 0xffffff80;
static PRLogModuleInfo* DBLog;
+PRTime nsMsgDatabase::gLastUseTime;
+
NS_IMPL_ISUPPORTS1(nsMsgDBService, nsIMsgDBService)
nsMsgDBService::nsMsgDBService()
{
DBLog = PR_NewLogModule("MSGDB");
}
@@ -134,16 +137,17 @@ NS_IMETHODIMP nsMsgDBService::OpenFolder
nsMsgDatabase *cacheDB = nsMsgDatabase::FindInCache(summaryFilePath);
if (cacheDB)
{
// this db could have ended up in the folder cache w/o an m_folder pointer via
// OpenMailDBFromFile. If so, take this chance to fix the folder.
if (!cacheDB->m_folder)
cacheDB->m_folder = aFolder;
+ cacheDB->RememberLastUseTime();
*_retval = cacheDB; // FindInCache already addRefed.
// if m_thumb is set, someone is asynchronously opening the db. But our
// caller wants to synchronously open it, so just do it.
if (cacheDB->m_thumb)
return cacheDB->Open(summaryFilePath, false, aLeaveInvalidDB);
return NS_OK;
}
@@ -331,16 +335,17 @@ void nsMsgDBService::FinishDBOpen(nsIMsg
PRInt32 numMessages;
aMsgDB->m_mdbAllMsgHeadersTable->GetCount(aMsgDB->GetEnv(),
&numHdrsInTable);
aMsgDB->m_dbFolderInfo->GetNumMessages(&numMessages);
if (numMessages != (PRInt32) numHdrsInTable)
aMsgDB->SyncCounts();
}
HookupPendingListeners(aMsgDB, aFolder);
+ aMsgDB->RememberLastUseTime();
}
// This method is called when the caller is trying to create a db without
// having a corresponding nsIMsgFolder object. This happens in a few
// situations, including imap folder discovery, compacting local folders,
// and copying local folders.
NS_IMETHODIMP nsMsgDBService::OpenMailDBFromFile(nsILocalFile *aFolderName,
nsIMsgFolder *aFolder,
@@ -444,16 +449,29 @@ NS_IMETHODIMP nsMsgDBService::Unregister
NS_IMETHODIMP nsMsgDBService::CachedDBForFolder(nsIMsgFolder *aFolder, nsIMsgDatabase **aRetDB)
{
NS_ENSURE_ARG_POINTER(aFolder);
NS_ENSURE_ARG_POINTER(aRetDB);
*aRetDB = nsMsgDatabase::FindInCache(aFolder);
return NS_OK;
}
+NS_IMETHODIMP nsMsgDBService::GetOpenDBs(nsIArray **aOpenDBs)
+{
+ NS_ENSURE_ARG_POINTER(aOpenDBs);
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> openDBs(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ for (PRUint32 i = 0; i < nsMsgDatabase::m_dbCache->Length(); i++)
+ openDBs->AppendElement(nsMsgDatabase::m_dbCache->ElementAt(i), false);
+
+ openDBs.forget(aOpenDBs);
+ return NS_OK;
+}
+
static bool gGotGlobalPrefs = false;
static bool gThreadWithoutRe = true;
static bool gStrictThreading = false;
static bool gCorrectThreading = false;
void nsMsgDatabase::GetGlobalPrefs()
{
if (!gGotGlobalPrefs)
@@ -526,16 +544,29 @@ NS_IMETHODIMP nsMsgDatabase::SetMsgHdrCa
NS_IMETHODIMP nsMsgDatabase::GetMsgHdrCacheSize(PRUint32 *aSize)
{
NS_ENSURE_ARG_POINTER(aSize);
*aSize = m_cacheSize;
return NS_OK;
}
+NS_IMETHODIMP nsMsgDatabase::GetLastUseTime(PRTime *aTime)
+{
+ NS_ENSURE_ARG_POINTER(aTime);
+ *aTime = m_lastUseTime;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgDatabase::SetLastUseTime(PRTime aTime)
+{
+ gLastUseTime = m_lastUseTime = aTime;
+ return NS_OK;
+}
+
NS_IMETHODIMP nsMsgDatabase::ClearCachedHdrs()
{
ClearCachedObjects(false);
#ifdef DEBUG_bienvenu1
if (mRefCnt > 1)
{
NS_ASSERTION(false, "");
printf("someone's holding onto db - refs = %ld\n", mRefCnt);
@@ -1085,35 +1116,17 @@ nsMsgDatabase::~nsMsgDatabase()
if (m_mdbEnv)
{
m_mdbEnv->Release(); //??? is this right?
m_mdbEnv = nsnull;
}
m_ChangeListeners.Clear();
}
-NS_IMPL_ADDREF(nsMsgDatabase)
-
-NS_IMPL_RELEASE(nsMsgDatabase)
-
-NS_IMETHODIMP nsMsgDatabase::QueryInterface(REFNSIID aIID, void** aResult)
-{
- if (aResult == NULL)
- return NS_ERROR_NULL_POINTER;
-
- if (aIID.Equals(NS_GET_IID(nsIMsgDatabase)) ||
- aIID.Equals(NS_GET_IID(nsIDBChangeAnnouncer)) ||
- aIID.Equals(NS_GET_IID(nsISupports)))
- {
- *aResult = static_cast<nsIMsgDatabase*>(this);
- NS_ADDREF_THIS();
- return NS_OK;
- }
- return NS_NOINTERFACE;
-}
+NS_IMPL_ISUPPORTS2(nsMsgDatabase, nsIMsgDatabase, nsIDBChangeAnnouncer);
void nsMsgDatabase::GetMDBFactory(nsIMdbFactory ** aMdbFactory)
{
if (!mMdbFactory)
{
nsresult rv;
nsCOMPtr <nsIMdbFactoryService> mdbFactoryService = do_GetService(NS_MORK_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && mdbFactoryService)
@@ -1434,25 +1447,29 @@ NS_IMETHODIMP nsMsgDatabase::GetDBFolder
{
NS_ERROR("db must be corrupt");
return NS_ERROR_NULL_POINTER;
}
NS_ADDREF(*result = m_dbFolderInfo);
return NS_OK;
}
+NS_IMETHODIMP nsMsgDatabase::GetFolder(nsIMsgFolder **aFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_IF_ADDREF(*aFolder = m_folder);
+ return NS_OK;
+}
+
NS_IMETHODIMP nsMsgDatabase::Commit(nsMsgDBCommit commitType)
{
nsresult err = NS_OK;
- nsIMdbThumb *commitThumb = NULL;
-
-#ifdef DEBUG_seth
- printf("nsMsgDatabase::Commit(%d)\n",commitType);
-#endif
-
+ nsCOMPtr<nsIMdbThumb> commitThumb;
+
+ RememberLastUseTime();
if (commitType == nsMsgDBCommitType::kLargeCommit || commitType == nsMsgDBCommitType::kSessionCommit)
{
mdb_percent outActualWaste = 0;
mdb_bool outShould;
if (m_mdbStore) {
err = m_mdbStore->ShouldCompress(GetEnv(), 30, &outActualWaste, &outShould);
if (NS_SUCCEEDED(err) && outShould)
commitType = nsMsgDBCommitType::kCompressCommit;
@@ -1460,38 +1477,37 @@ NS_IMETHODIMP nsMsgDatabase::Commit(nsMs
}
// commitType = nsMsgDBCommitType::kCompressCommit; // ### until incremental writing works.
if (m_mdbStore)
{
switch (commitType)
{
case nsMsgDBCommitType::kLargeCommit:
- err = m_mdbStore->LargeCommit(GetEnv(), &commitThumb);
+ err = m_mdbStore->LargeCommit(GetEnv(), getter_AddRefs(commitThumb));
break;
case nsMsgDBCommitType::kSessionCommit:
- err = m_mdbStore->SessionCommit(GetEnv(), &commitThumb);
+ err = m_mdbStore->SessionCommit(GetEnv(), getter_AddRefs(commitThumb));
break;
case nsMsgDBCommitType::kCompressCommit:
- err = m_mdbStore->CompressCommit(GetEnv(), &commitThumb);
+ err = m_mdbStore->CompressCommit(GetEnv(), getter_AddRefs(commitThumb));
break;
}
}
if (commitThumb)
{
mdb_count outTotal = 0; // total somethings to do in operation
mdb_count outCurrent = 0; // subportion of total completed so far
mdb_bool outDone = false; // is operation finished?
mdb_bool outBroken = false; // is operation irreparably dead and broken?
while (!outDone && !outBroken && err == NS_OK)
{
err = commitThumb->DoMore(GetEnv(), &outTotal, &outCurrent, &outDone, &outBroken);
}
- NS_IF_RELEASE(commitThumb);
}
// ### do something with error, but clear it now because mork errors out on commits.
if (GetEnv())
GetEnv()->ClearErrors();
nsresult rv;
nsCOMPtr<nsIMsgAccountManager> accountManager =
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
@@ -1807,16 +1823,21 @@ NS_IMETHODIMP nsMsgDatabase::ContainsKey
// get a message header for the given key. Caller must release()!
NS_IMETHODIMP nsMsgDatabase::GetMsgHdrForKey(nsMsgKey key, nsIMsgDBHdr **pmsgHdr)
{
nsresult err = NS_OK;
mdb_bool hasOid;
mdbOid rowObjectId;
+ // Because this may be called a lot, and we don't want gettimeofday() to show
+ // up in trace logs, we just remember the most recent time any db was used,
+ // which should be close enough for our purposes.
+ m_lastUseTime = gLastUseTime;
+
#ifdef DEBUG_bienvenu1
NS_ASSERTION(m_folder, "folder should be set");
#endif
if (!pmsgHdr || !m_mdbAllMsgHeadersTable || !m_mdbStore)
return NS_ERROR_NULL_POINTER;
*pmsgHdr = NULL;
@@ -2922,16 +2943,17 @@ nsresult nsMsgFilteredDBEnumerator::Pref
}
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsMsgDatabase::EnumerateMessages(nsISimpleEnumerator* *result)
{
+ RememberLastUseTime();
NS_ENSURE_ARG_POINTER(result);
nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, m_mdbAllMsgHeadersTable,
nsnull, nsnull);
if (!e)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*result = e);
return NS_OK;
}
@@ -3056,16 +3078,19 @@ nsMsgDatabase::SyncCounts()
}
// resulting output array is sorted by key.
NS_IMETHODIMP nsMsgDatabase::ListAllKeys(nsIMsgKeyArray *aKeys)
{
NS_ENSURE_ARG_POINTER(aKeys);
nsresult rv = NS_OK;
nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+
+ RememberLastUseTime();
+
if (m_mdbAllMsgHeadersTable)
{
PRUint32 numMsgs = 0;
m_mdbAllMsgHeadersTable->GetCount(GetEnv(), &numMsgs);
aKeys->SetCapacity(numMsgs);
rv = m_mdbAllMsgHeadersTable->GetTableRowCursor(GetEnv(), -1,
getter_AddRefs(rowCursor));
while (NS_SUCCEEDED(rv) && rowCursor)
@@ -3298,16 +3323,17 @@ NS_IMETHODIMP nsMsgDBThreadEnumerator::H
PrefetchNext();
*aResult = !mDone;
return NS_OK;
}
NS_IMETHODIMP
nsMsgDatabase::EnumerateThreads(nsISimpleEnumerator* *result)
{
+ RememberLastUseTime();
nsMsgDBThreadEnumerator* e = new nsMsgDBThreadEnumerator(this, nsnull);
if (e == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*result = e);
return NS_OK;
}
// only return headers with a particular flag set
@@ -3318,21 +3344,23 @@ nsMsgFlagSetFilter(nsIMsgDBHdr *msg, voi
desiredFlags = * (PRUint32 *) closure;
msg->GetFlags(&msgFlags);
return (msgFlags & desiredFlags) ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
nsMsgDatabase::EnumerateMessagesWithFlag(nsISimpleEnumerator* *result, PRUint32 *pFlag)
{
- nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, m_mdbAllMsgHeadersTable, nsMsgFlagSetFilter, pFlag);
- if (e == nsnull)
- return NS_ERROR_OUT_OF_MEMORY;
- NS_ADDREF(*result = e);
- return NS_OK;
+ RememberLastUseTime();
+
+ nsMsgDBEnumerator* e = new nsMsgDBEnumerator(this, m_mdbAllMsgHeadersTable, nsMsgFlagSetFilter, pFlag);
+ if (!e)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*result = e);
+ return NS_OK;
}
NS_IMETHODIMP nsMsgDatabase::CreateNewHdr(nsMsgKey key, nsIMsgDBHdr **pnewHdr)
{
nsresult err = NS_OK;
nsIMdbRow *hdrRow;
struct mdbOid allMsgHdrsTableOID;
--- a/mailnews/local/src/nsLocalMailFolder.cpp
+++ b/mailnews/local/src/nsLocalMailFolder.cpp
@@ -337,16 +337,18 @@ NS_IMETHODIMP nsMsgLocalMailFolder::GetD
rv = OpenDatabase();
if (mDatabase)
{
mDatabase->AddListener(this);
UpdateNewMessages();
}
}
NS_IF_ADDREF(*aDatabase = mDatabase);
+ if (mDatabase)
+ mDatabase->SetLastUseTime(PR_Now());
return rv;
}
// Makes sure the database is open and exists. If the database is out of date,
// then this call will run an async url to reparse the folder. The passed in
// url listener will get called when the url is done.
NS_IMETHODIMP nsMsgLocalMailFolder::GetDatabaseWithReparse(nsIUrlListener *aReparseUrlListener, nsIMsgWindow *aMsgWindow,
--- a/mailnews/mailnews.js
+++ b/mailnews/mailnews.js
@@ -106,16 +106,20 @@ pref("mailnews.tcptimeout", 100);
pref("mailnews.headers.showSender", false);
// set to 0 if you don't want to ignore timestamp differences between
// local mail folders and the value stored in the corresponding .msf file.
// 0 was the default up to and including 1.5. I've made the default
// be greater than one hour so daylight savings time changes don't affect us.
// We will still always regenerate .msf files if the file size changes.
pref("mail.db_timestamp_leeway", 4000);
+// How long should we leave idle db's open, in seconds.
+pref("mail.db.idle_limit", 3000);
+// How many db's should we leave open? LRU db's will be closed first
+pref("mail.db.max_open", 30);
pref("mail.imap.chunk_size", 65536);
pref("mail.imap.min_chunk_size_threshold", 98304);
pref("mail.imap.chunk_fast", 2);
pref("mail.imap.chunk_ideal", 4);
pref("mail.imap.chunk_add", 8192);
pref("mail.imap.hide_other_users", false);
pref("mail.imap.hide_unused_namespaces", true);