diff options
Diffstat (limited to 'mailnews/base/util/msgDBCacheManager.js')
-rw-r--r-- | mailnews/base/util/msgDBCacheManager.js | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/mailnews/base/util/msgDBCacheManager.js b/mailnews/base/util/msgDBCacheManager.js new file mode 100644 index 000000000..267962375 --- /dev/null +++ b/mailnews/base/util/msgDBCacheManager.js @@ -0,0 +1,175 @@ +/* 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 ::::::::::::::: */ + +this.EXPORTED_SYMBOLS = ["msgDBCacheManager"]; + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; + +Cu.import("resource:///modules/mailServices.js"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource:///modules/gloda/log4moz.js"); +var log = Log4Moz.getConfiguredLogger("mailnews.database.dbcache"); + +/** + */ +var DBCACHE_INTERVAL_DEFAULT_MS = 60000; // 1 minute + +/* :::::::: The Module ::::::::::::::: */ + +var msgDBCacheManager = +{ + _initialized: false, + + _msgDBCacheTimer: null, + + _msgDBCacheTimerIntervalMS: DBCACHE_INTERVAL_DEFAULT_MS, + + _dbService: null, + + /** + * This is called on startup + */ + init: function dbcachemgr_init() + { + if (this._initialized) + return; + + this._dbService = Cc["@mozilla.org/msgDatabase/msgDBService;1"] + .getService(Ci.nsIMsgDBService); + + // we listen for "quit-application-granted" instead of + // "quit-application-requested" because other observers of the + // latter can cancel the shutdown. + Services.obs.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": + Services.obs.removeObserver(this, "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); + } + }, + + /** + * Checks if any DBs need to be closed due to inactivity or too many of them open. + */ + checkCachedDBs: function() + { + let idleLimit = Services.prefs.getIntPref("mail.db.idle_limit"); + let maxOpenDBs = Services.prefs.getIntPref("mail.db.max_open"); + + // db.lastUseTime below is in microseconds while Date.now and idleLimit pref + // is in milliseconds. + let closeThreshold = (Date.now() - idleLimit) * 1000; + let cachedDBs = this._dbService.openDBs; + log.info("Periodic check of cached folder databases (DBs), count=" + cachedDBs.length); + // Count databases that are already closed or get closed now due to inactivity. + let numClosing = 0; + // Count databases whose folder is open in a window. + let numOpenInWindow = 0; + let dbs = []; + for (let i = 0; i < cachedDBs.length; i++) { + let db = cachedDBs.queryElementAt(i, Ci.nsIMsgDatabase); + if (!db.folder.databaseOpen) { + // The DB isn't really open anymore. + log.debug("Skipping, DB not open for folder: " + db.folder.name); + numClosing++; + continue; + } + + if (MailServices.mailSession.IsFolderOpenInWindow(db.folder)) { + // The folder is open in a window so this DB must not be closed. + log.debug("Skipping, DB open in window for folder: " + db.folder.name); + numOpenInWindow++; + continue; + } + + if (db.lastUseTime < closeThreshold) + { + // DB open too log without activity. + log.debug("Closing expired DB for folder: " + db.folder.name); + db.folder.msgDatabase = null; + numClosing++; + continue; + } + + // Database eligible for closing. + dbs.push(db); + } + log.info("DBs open in a window: " + numOpenInWindow + ", DBs open: " + dbs.length + ", DBs already closing: " + numClosing); + let dbsToClose = Math.max(dbs.length - Math.max(maxOpenDBs - numOpenInWindow, 0), 0); + if (dbsToClose > 0) { + // Close some DBs so that we do not have more than maxOpenDBs. + // However, we skipped DBs for folders that are open in a window + // so if there are so many windows open, it may be possible for + // more than maxOpenDBs folders to stay open after this loop. + log.info("Need to close " + dbsToClose + " more DBs"); + // Order databases by lowest lastUseTime (oldest) at the end. + dbs.sort((a, b) => b.lastUseTime - a.lastUseTime); + while (dbsToClose > 0) { + let db = dbs.pop(); + log.debug("Closing DB for folder: " + db.folder.name); + db.folder.msgDatabase = null; + dbsToClose--; + } + } + }, +}; |