1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/src/storage/DOMStorageDBThread.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,346 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef DOMStorageDBThread_h___ 1.10 +#define DOMStorageDBThread_h___ 1.11 + 1.12 +#include "prthread.h" 1.13 +#include "prinrval.h" 1.14 +#include "nsTArray.h" 1.15 +#include "mozilla/Monitor.h" 1.16 +#include "mozilla/storage/StatementCache.h" 1.17 +#include "nsString.h" 1.18 +#include "nsCOMPtr.h" 1.19 +#include "nsClassHashtable.h" 1.20 +#include "nsIFile.h" 1.21 + 1.22 +class mozIStorageConnection; 1.23 + 1.24 +namespace mozilla { 1.25 +namespace dom { 1.26 + 1.27 +class DOMStorageCacheBridge; 1.28 +class DOMStorageUsageBridge; 1.29 +class DOMStorageUsage; 1.30 + 1.31 +typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache; 1.32 + 1.33 +// Interface used by the cache to post operations to the asynchronous 1.34 +// database thread or process. 1.35 +class DOMStorageDBBridge 1.36 +{ 1.37 +public: 1.38 + DOMStorageDBBridge(); 1.39 + virtual ~DOMStorageDBBridge() {} 1.40 + 1.41 + // Ensures the database engine is started 1.42 + virtual nsresult Init() = 0; 1.43 + 1.44 + // Releases the database and disallows its usage 1.45 + virtual nsresult Shutdown() = 0; 1.46 + 1.47 + // Asynchronously fills the cache with data from the database for first use. 1.48 + // When |aPriority| is true, the preload operation is scheduled as the first one. 1.49 + // This method is responsible to keep hard reference to the cache for the time of 1.50 + // the preload or, when preload cannot be performed, call LoadDone() immediately. 1.51 + virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false) = 0; 1.52 + 1.53 + // Asynchronously fill the |usage| object with actual usage of data by its scope. 1.54 + // The scope is eTLD+1 tops, never deeper subdomains. 1.55 + virtual void AsyncGetUsage(DOMStorageUsageBridge* aUsage) = 0; 1.56 + 1.57 + // Synchronously fills the cache, when |aForceSync| is false and cache already got some 1.58 + // data before, the method waits for the running preload to finish 1.59 + virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync = false) = 0; 1.60 + 1.61 + // Called when an existing key is modified in the storage, schedules update to the database 1.62 + virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0; 1.63 + 1.64 + // Called when an existing key is modified in the storage, schedules update to the database 1.65 + virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0; 1.66 + 1.67 + // Called when an item is removed from the storage, schedules delete of the key 1.68 + virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey) = 0; 1.69 + 1.70 + // Called when the whole storage is cleared by the DOM API, schedules delete of the scope 1.71 + virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache) = 0; 1.72 + 1.73 + // Called when chrome deletes e.g. cookies, schedules delete of the whole database 1.74 + virtual void AsyncClearAll() = 0; 1.75 + 1.76 + // Called when only a domain and its subdomains or an app data is about to clear 1.77 + virtual void AsyncClearMatchingScope(const nsACString& aScope) = 0; 1.78 + 1.79 + // Forces scheduled DB operations to be early flushed to the disk 1.80 + virtual void AsyncFlush() = 0; 1.81 + 1.82 + // Check whether the scope has any data stored on disk and is thus allowed to preload 1.83 + virtual bool ShouldPreloadScope(const nsACString& aScope) = 0; 1.84 + 1.85 + // Get the complete list of scopes having data 1.86 + virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes) = 0; 1.87 +}; 1.88 + 1.89 +// The implementation of the the database engine, this directly works 1.90 +// with the sqlite or any other db API we are based on 1.91 +// This class is resposible for collecting and processing asynchronous 1.92 +// DB operations over caches (DOMStorageCache) communicating though 1.93 +// DOMStorageCacheBridge interface class 1.94 +class DOMStorageDBThread MOZ_FINAL : public DOMStorageDBBridge 1.95 +{ 1.96 +public: 1.97 + class PendingOperations; 1.98 + 1.99 + // Representation of a singe database task, like adding and removing keys, 1.100 + // (pre)loading the whole origin data, cleaning. 1.101 + class DBOperation 1.102 + { 1.103 + public: 1.104 + typedef enum { 1.105 + // Only operation that reads data from the database 1.106 + opPreload, 1.107 + // The same as opPreload, just executed with highest priority 1.108 + opPreloadUrgent, 1.109 + 1.110 + // Load usage of a scope 1.111 + opGetUsage, 1.112 + 1.113 + // Operations invoked by the DOM content API 1.114 + opAddItem, 1.115 + opUpdateItem, 1.116 + opRemoveItem, 1.117 + opClear, 1.118 + 1.119 + // Operations invoked by chrome 1.120 + opClearAll, 1.121 + opClearMatchingScope, 1.122 + } OperationType; 1.123 + 1.124 + DBOperation(const OperationType aType, 1.125 + DOMStorageCacheBridge* aCache = nullptr, 1.126 + const nsAString& aKey = EmptyString(), 1.127 + const nsAString& aValue = EmptyString()); 1.128 + DBOperation(const OperationType aType, 1.129 + DOMStorageUsageBridge* aUsage); 1.130 + DBOperation(const OperationType aType, 1.131 + const nsACString& aScope); 1.132 + ~DBOperation(); 1.133 + 1.134 + // Executes the operation, doesn't necessarity have to be called on the I/O thread 1.135 + void PerformAndFinalize(DOMStorageDBThread* aThread); 1.136 + 1.137 + // Finalize the operation, i.e. do any internal cleanup and finish calls 1.138 + void Finalize(nsresult aRv); 1.139 + 1.140 + // The operation type 1.141 + OperationType Type() { return mType; } 1.142 + 1.143 + // The operation scope (=origin) 1.144 + const nsCString Scope(); 1.145 + 1.146 + // |Scope + key| the operation is working with 1.147 + const nsCString Target(); 1.148 + 1.149 + private: 1.150 + // The operation implementation body 1.151 + nsresult Perform(DOMStorageDBThread* aThread); 1.152 + 1.153 + friend class PendingOperations; 1.154 + OperationType mType; 1.155 + nsRefPtr<DOMStorageCacheBridge> mCache; 1.156 + nsRefPtr<DOMStorageUsageBridge> mUsage; 1.157 + nsString mKey; 1.158 + nsString mValue; 1.159 + nsCString mScope; 1.160 + }; 1.161 + 1.162 + // Encapsulation of collective and coalescing logic for all pending operations 1.163 + // except preloads that are handled separately as priority operations 1.164 + class PendingOperations { 1.165 + public: 1.166 + PendingOperations(); 1.167 + 1.168 + // Method responsible for coalescing redundant update operations with the same 1.169 + // |Target()| or clear operations with the same or matching |Scope()| 1.170 + void Add(DBOperation* aOperation); 1.171 + 1.172 + // True when there are some scheduled operations to flush on disk 1.173 + bool HasTasks(); 1.174 + 1.175 + // Moves collected operations to a local flat list to allow execution of the operation 1.176 + // list out of the thread lock 1.177 + bool Prepare(); 1.178 + 1.179 + // Executes the previously |Prepared()'ed| list of operations, retuns result, but doesn't 1.180 + // handle it in any way in case of a failure 1.181 + nsresult Execute(DOMStorageDBThread* aThread); 1.182 + 1.183 + // Finalizes the pending operation list, returns false when too many operations failed 1.184 + // to flush what indicates a long standing issue with the database access. 1.185 + bool Finalize(nsresult aRv); 1.186 + 1.187 + // true when a clear that deletes the given |scope| is among the pending operations; 1.188 + // when a preload for that scope is being scheduled, it must be finished right away 1.189 + bool IsScopeClearPending(const nsACString& aScope); 1.190 + 1.191 + // Checks whether there is a pending update (or clear, actually) operation for this scope. 1.192 + bool IsScopeUpdatePending(const nsACString& aScope); 1.193 + 1.194 + private: 1.195 + // Returns true iff new operation is of type newType and there is a pending 1.196 + // operation of type pendingType for the same key (target). 1.197 + bool CheckForCoalesceOpportunity(DBOperation* aNewOp, 1.198 + DBOperation::OperationType aPendingType, 1.199 + DBOperation::OperationType aNewType); 1.200 + 1.201 + // List of all clearing operations, executed first 1.202 + nsClassHashtable<nsCStringHashKey, DBOperation> mClears; 1.203 + 1.204 + // List of all update/insert operations, executed as second 1.205 + nsClassHashtable<nsCStringHashKey, DBOperation> mUpdates; 1.206 + 1.207 + // Collection of all tasks, valid only between Prepare() and Execute() 1.208 + nsTArray<nsAutoPtr<DBOperation> > mExecList; 1.209 + 1.210 + // Number of failing flush attempts 1.211 + uint32_t mFlushFailureCount; 1.212 + }; 1.213 + 1.214 +public: 1.215 + DOMStorageDBThread(); 1.216 + virtual ~DOMStorageDBThread() {} 1.217 + 1.218 + virtual nsresult Init(); 1.219 + virtual nsresult Shutdown(); 1.220 + 1.221 + virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false) 1.222 + { InsertDBOp(new DBOperation(aPriority ? DBOperation::opPreloadUrgent : DBOperation::opPreload, aCache)); } 1.223 + 1.224 + virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForce = false); 1.225 + 1.226 + virtual void AsyncGetUsage(DOMStorageUsageBridge * aUsage) 1.227 + { InsertDBOp(new DBOperation(DBOperation::opGetUsage, aUsage)); } 1.228 + 1.229 + virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) 1.230 + { return InsertDBOp(new DBOperation(DBOperation::opAddItem, aCache, aKey, aValue)); } 1.231 + 1.232 + virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) 1.233 + { return InsertDBOp(new DBOperation(DBOperation::opUpdateItem, aCache, aKey, aValue)); } 1.234 + 1.235 + virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey) 1.236 + { return InsertDBOp(new DBOperation(DBOperation::opRemoveItem, aCache, aKey)); } 1.237 + 1.238 + virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache) 1.239 + { return InsertDBOp(new DBOperation(DBOperation::opClear, aCache)); } 1.240 + 1.241 + virtual void AsyncClearAll() 1.242 + { InsertDBOp(new DBOperation(DBOperation::opClearAll)); } 1.243 + 1.244 + virtual void AsyncClearMatchingScope(const nsACString& aScope) 1.245 + { InsertDBOp(new DBOperation(DBOperation::opClearMatchingScope, aScope)); } 1.246 + 1.247 + virtual void AsyncFlush(); 1.248 + 1.249 + virtual bool ShouldPreloadScope(const nsACString& aScope); 1.250 + virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes); 1.251 + 1.252 +private: 1.253 + nsCOMPtr<nsIFile> mDatabaseFile; 1.254 + PRThread* mThread; 1.255 + 1.256 + // The monitor we drive the thread with 1.257 + Monitor mMonitor; 1.258 + 1.259 + // Flag to stop, protected by the monitor 1.260 + bool mStopIOThread; 1.261 + 1.262 + // Whether WAL is enabled 1.263 + bool mWALModeEnabled; 1.264 + 1.265 + // Whether DB has already been open, avoid races between main thread reads 1.266 + // and pending DB init in the background I/O thread 1.267 + bool mDBReady; 1.268 + 1.269 + // State of the database initiation 1.270 + nsresult mStatus; 1.271 + 1.272 + // List of scopes having data, for optimization purposes only 1.273 + nsTHashtable<nsCStringHashKey> mScopesHavingData; 1.274 + 1.275 + StatementCache mWorkerStatements; 1.276 + StatementCache mReaderStatements; 1.277 + 1.278 + // Connection used by the worker thread for all read and write ops 1.279 + nsCOMPtr<mozIStorageConnection> mWorkerConnection; 1.280 + 1.281 + // Connection used only on the main thread for sync read operations 1.282 + nsCOMPtr<mozIStorageConnection> mReaderConnection; 1.283 + 1.284 + // Time the first pending operation has been added to the pending operations 1.285 + // list 1.286 + PRIntervalTime mDirtyEpoch; 1.287 + 1.288 + // Flag to force immediate flush of all pending operations 1.289 + bool mFlushImmediately; 1.290 + 1.291 + // List of preloading operations, in chronological or priority order. 1.292 + // Executed prioritly over pending update operations. 1.293 + nsTArray<DBOperation*> mPreloads; 1.294 + 1.295 + // Collector of pending update operations 1.296 + PendingOperations mPendingTasks; 1.297 + 1.298 + // Counter of calls for thread priority rising. 1.299 + int32_t mPriorityCounter; 1.300 + 1.301 + // Helper to direct an operation to one of the arrays above; 1.302 + // also checks IsScopeClearPending for preloads 1.303 + nsresult InsertDBOp(DBOperation* aOperation); 1.304 + 1.305 + // Opens the database, first thing we do after start of the thread. 1.306 + nsresult OpenDatabaseConnection(); 1.307 + nsresult InitDatabase(); 1.308 + nsresult ShutdownDatabase(); 1.309 + 1.310 + // Tries to establish WAL mode 1.311 + nsresult SetJournalMode(bool aIsWal); 1.312 + nsresult TryJournalMode(); 1.313 + 1.314 + // Sets the threshold for auto-checkpointing the WAL. 1.315 + nsresult ConfigureWALBehavior(); 1.316 + 1.317 + void SetHigherPriority(); 1.318 + void SetDefaultPriority(); 1.319 + 1.320 + // Ensures we flush pending tasks in some reasonble time 1.321 + void ScheduleFlush(); 1.322 + 1.323 + // Called when flush of pending tasks is being executed 1.324 + void UnscheduleFlush(); 1.325 + 1.326 + // This method is used for two purposes: 1.327 + // 1. as a value passed to monitor.Wait() method 1.328 + // 2. as in indicator that flush has to be performed 1.329 + // 1.330 + // Return: 1.331 + // - PR_INTERVAL_NO_TIMEOUT when no pending tasks are scheduled 1.332 + // - larger then zero when tasks have been scheduled, but it is 1.333 + // still not time to perform the flush ; it is actual interval 1.334 + // time to wait until the flush has to happen 1.335 + // - 0 when it is time to do the flush 1.336 + PRIntervalTime TimeUntilFlush(); 1.337 + 1.338 + // Notifies to the main thread that flush has completed 1.339 + void NotifyFlushCompletion(); 1.340 + 1.341 + // Thread loop 1.342 + static void ThreadFunc(void* aArg); 1.343 + void ThreadFunc(); 1.344 +}; 1.345 + 1.346 +} // ::dom 1.347 +} // ::mozilla 1.348 + 1.349 +#endif /* DOMStorageDBThread_h___ */