Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #ifndef DOMStorageDBThread_h___ |
michael@0 | 7 | #define DOMStorageDBThread_h___ |
michael@0 | 8 | |
michael@0 | 9 | #include "prthread.h" |
michael@0 | 10 | #include "prinrval.h" |
michael@0 | 11 | #include "nsTArray.h" |
michael@0 | 12 | #include "mozilla/Monitor.h" |
michael@0 | 13 | #include "mozilla/storage/StatementCache.h" |
michael@0 | 14 | #include "nsString.h" |
michael@0 | 15 | #include "nsCOMPtr.h" |
michael@0 | 16 | #include "nsClassHashtable.h" |
michael@0 | 17 | #include "nsIFile.h" |
michael@0 | 18 | |
michael@0 | 19 | class mozIStorageConnection; |
michael@0 | 20 | |
michael@0 | 21 | namespace mozilla { |
michael@0 | 22 | namespace dom { |
michael@0 | 23 | |
michael@0 | 24 | class DOMStorageCacheBridge; |
michael@0 | 25 | class DOMStorageUsageBridge; |
michael@0 | 26 | class DOMStorageUsage; |
michael@0 | 27 | |
michael@0 | 28 | typedef mozilla::storage::StatementCache<mozIStorageStatement> StatementCache; |
michael@0 | 29 | |
michael@0 | 30 | // Interface used by the cache to post operations to the asynchronous |
michael@0 | 31 | // database thread or process. |
michael@0 | 32 | class DOMStorageDBBridge |
michael@0 | 33 | { |
michael@0 | 34 | public: |
michael@0 | 35 | DOMStorageDBBridge(); |
michael@0 | 36 | virtual ~DOMStorageDBBridge() {} |
michael@0 | 37 | |
michael@0 | 38 | // Ensures the database engine is started |
michael@0 | 39 | virtual nsresult Init() = 0; |
michael@0 | 40 | |
michael@0 | 41 | // Releases the database and disallows its usage |
michael@0 | 42 | virtual nsresult Shutdown() = 0; |
michael@0 | 43 | |
michael@0 | 44 | // Asynchronously fills the cache with data from the database for first use. |
michael@0 | 45 | // When |aPriority| is true, the preload operation is scheduled as the first one. |
michael@0 | 46 | // This method is responsible to keep hard reference to the cache for the time of |
michael@0 | 47 | // the preload or, when preload cannot be performed, call LoadDone() immediately. |
michael@0 | 48 | virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false) = 0; |
michael@0 | 49 | |
michael@0 | 50 | // Asynchronously fill the |usage| object with actual usage of data by its scope. |
michael@0 | 51 | // The scope is eTLD+1 tops, never deeper subdomains. |
michael@0 | 52 | virtual void AsyncGetUsage(DOMStorageUsageBridge* aUsage) = 0; |
michael@0 | 53 | |
michael@0 | 54 | // Synchronously fills the cache, when |aForceSync| is false and cache already got some |
michael@0 | 55 | // data before, the method waits for the running preload to finish |
michael@0 | 56 | virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForceSync = false) = 0; |
michael@0 | 57 | |
michael@0 | 58 | // Called when an existing key is modified in the storage, schedules update to the database |
michael@0 | 59 | virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0; |
michael@0 | 60 | |
michael@0 | 61 | // Called when an existing key is modified in the storage, schedules update to the database |
michael@0 | 62 | virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) = 0; |
michael@0 | 63 | |
michael@0 | 64 | // Called when an item is removed from the storage, schedules delete of the key |
michael@0 | 65 | virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey) = 0; |
michael@0 | 66 | |
michael@0 | 67 | // Called when the whole storage is cleared by the DOM API, schedules delete of the scope |
michael@0 | 68 | virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache) = 0; |
michael@0 | 69 | |
michael@0 | 70 | // Called when chrome deletes e.g. cookies, schedules delete of the whole database |
michael@0 | 71 | virtual void AsyncClearAll() = 0; |
michael@0 | 72 | |
michael@0 | 73 | // Called when only a domain and its subdomains or an app data is about to clear |
michael@0 | 74 | virtual void AsyncClearMatchingScope(const nsACString& aScope) = 0; |
michael@0 | 75 | |
michael@0 | 76 | // Forces scheduled DB operations to be early flushed to the disk |
michael@0 | 77 | virtual void AsyncFlush() = 0; |
michael@0 | 78 | |
michael@0 | 79 | // Check whether the scope has any data stored on disk and is thus allowed to preload |
michael@0 | 80 | virtual bool ShouldPreloadScope(const nsACString& aScope) = 0; |
michael@0 | 81 | |
michael@0 | 82 | // Get the complete list of scopes having data |
michael@0 | 83 | virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes) = 0; |
michael@0 | 84 | }; |
michael@0 | 85 | |
michael@0 | 86 | // The implementation of the the database engine, this directly works |
michael@0 | 87 | // with the sqlite or any other db API we are based on |
michael@0 | 88 | // This class is resposible for collecting and processing asynchronous |
michael@0 | 89 | // DB operations over caches (DOMStorageCache) communicating though |
michael@0 | 90 | // DOMStorageCacheBridge interface class |
michael@0 | 91 | class DOMStorageDBThread MOZ_FINAL : public DOMStorageDBBridge |
michael@0 | 92 | { |
michael@0 | 93 | public: |
michael@0 | 94 | class PendingOperations; |
michael@0 | 95 | |
michael@0 | 96 | // Representation of a singe database task, like adding and removing keys, |
michael@0 | 97 | // (pre)loading the whole origin data, cleaning. |
michael@0 | 98 | class DBOperation |
michael@0 | 99 | { |
michael@0 | 100 | public: |
michael@0 | 101 | typedef enum { |
michael@0 | 102 | // Only operation that reads data from the database |
michael@0 | 103 | opPreload, |
michael@0 | 104 | // The same as opPreload, just executed with highest priority |
michael@0 | 105 | opPreloadUrgent, |
michael@0 | 106 | |
michael@0 | 107 | // Load usage of a scope |
michael@0 | 108 | opGetUsage, |
michael@0 | 109 | |
michael@0 | 110 | // Operations invoked by the DOM content API |
michael@0 | 111 | opAddItem, |
michael@0 | 112 | opUpdateItem, |
michael@0 | 113 | opRemoveItem, |
michael@0 | 114 | opClear, |
michael@0 | 115 | |
michael@0 | 116 | // Operations invoked by chrome |
michael@0 | 117 | opClearAll, |
michael@0 | 118 | opClearMatchingScope, |
michael@0 | 119 | } OperationType; |
michael@0 | 120 | |
michael@0 | 121 | DBOperation(const OperationType aType, |
michael@0 | 122 | DOMStorageCacheBridge* aCache = nullptr, |
michael@0 | 123 | const nsAString& aKey = EmptyString(), |
michael@0 | 124 | const nsAString& aValue = EmptyString()); |
michael@0 | 125 | DBOperation(const OperationType aType, |
michael@0 | 126 | DOMStorageUsageBridge* aUsage); |
michael@0 | 127 | DBOperation(const OperationType aType, |
michael@0 | 128 | const nsACString& aScope); |
michael@0 | 129 | ~DBOperation(); |
michael@0 | 130 | |
michael@0 | 131 | // Executes the operation, doesn't necessarity have to be called on the I/O thread |
michael@0 | 132 | void PerformAndFinalize(DOMStorageDBThread* aThread); |
michael@0 | 133 | |
michael@0 | 134 | // Finalize the operation, i.e. do any internal cleanup and finish calls |
michael@0 | 135 | void Finalize(nsresult aRv); |
michael@0 | 136 | |
michael@0 | 137 | // The operation type |
michael@0 | 138 | OperationType Type() { return mType; } |
michael@0 | 139 | |
michael@0 | 140 | // The operation scope (=origin) |
michael@0 | 141 | const nsCString Scope(); |
michael@0 | 142 | |
michael@0 | 143 | // |Scope + key| the operation is working with |
michael@0 | 144 | const nsCString Target(); |
michael@0 | 145 | |
michael@0 | 146 | private: |
michael@0 | 147 | // The operation implementation body |
michael@0 | 148 | nsresult Perform(DOMStorageDBThread* aThread); |
michael@0 | 149 | |
michael@0 | 150 | friend class PendingOperations; |
michael@0 | 151 | OperationType mType; |
michael@0 | 152 | nsRefPtr<DOMStorageCacheBridge> mCache; |
michael@0 | 153 | nsRefPtr<DOMStorageUsageBridge> mUsage; |
michael@0 | 154 | nsString mKey; |
michael@0 | 155 | nsString mValue; |
michael@0 | 156 | nsCString mScope; |
michael@0 | 157 | }; |
michael@0 | 158 | |
michael@0 | 159 | // Encapsulation of collective and coalescing logic for all pending operations |
michael@0 | 160 | // except preloads that are handled separately as priority operations |
michael@0 | 161 | class PendingOperations { |
michael@0 | 162 | public: |
michael@0 | 163 | PendingOperations(); |
michael@0 | 164 | |
michael@0 | 165 | // Method responsible for coalescing redundant update operations with the same |
michael@0 | 166 | // |Target()| or clear operations with the same or matching |Scope()| |
michael@0 | 167 | void Add(DBOperation* aOperation); |
michael@0 | 168 | |
michael@0 | 169 | // True when there are some scheduled operations to flush on disk |
michael@0 | 170 | bool HasTasks(); |
michael@0 | 171 | |
michael@0 | 172 | // Moves collected operations to a local flat list to allow execution of the operation |
michael@0 | 173 | // list out of the thread lock |
michael@0 | 174 | bool Prepare(); |
michael@0 | 175 | |
michael@0 | 176 | // Executes the previously |Prepared()'ed| list of operations, retuns result, but doesn't |
michael@0 | 177 | // handle it in any way in case of a failure |
michael@0 | 178 | nsresult Execute(DOMStorageDBThread* aThread); |
michael@0 | 179 | |
michael@0 | 180 | // Finalizes the pending operation list, returns false when too many operations failed |
michael@0 | 181 | // to flush what indicates a long standing issue with the database access. |
michael@0 | 182 | bool Finalize(nsresult aRv); |
michael@0 | 183 | |
michael@0 | 184 | // true when a clear that deletes the given |scope| is among the pending operations; |
michael@0 | 185 | // when a preload for that scope is being scheduled, it must be finished right away |
michael@0 | 186 | bool IsScopeClearPending(const nsACString& aScope); |
michael@0 | 187 | |
michael@0 | 188 | // Checks whether there is a pending update (or clear, actually) operation for this scope. |
michael@0 | 189 | bool IsScopeUpdatePending(const nsACString& aScope); |
michael@0 | 190 | |
michael@0 | 191 | private: |
michael@0 | 192 | // Returns true iff new operation is of type newType and there is a pending |
michael@0 | 193 | // operation of type pendingType for the same key (target). |
michael@0 | 194 | bool CheckForCoalesceOpportunity(DBOperation* aNewOp, |
michael@0 | 195 | DBOperation::OperationType aPendingType, |
michael@0 | 196 | DBOperation::OperationType aNewType); |
michael@0 | 197 | |
michael@0 | 198 | // List of all clearing operations, executed first |
michael@0 | 199 | nsClassHashtable<nsCStringHashKey, DBOperation> mClears; |
michael@0 | 200 | |
michael@0 | 201 | // List of all update/insert operations, executed as second |
michael@0 | 202 | nsClassHashtable<nsCStringHashKey, DBOperation> mUpdates; |
michael@0 | 203 | |
michael@0 | 204 | // Collection of all tasks, valid only between Prepare() and Execute() |
michael@0 | 205 | nsTArray<nsAutoPtr<DBOperation> > mExecList; |
michael@0 | 206 | |
michael@0 | 207 | // Number of failing flush attempts |
michael@0 | 208 | uint32_t mFlushFailureCount; |
michael@0 | 209 | }; |
michael@0 | 210 | |
michael@0 | 211 | public: |
michael@0 | 212 | DOMStorageDBThread(); |
michael@0 | 213 | virtual ~DOMStorageDBThread() {} |
michael@0 | 214 | |
michael@0 | 215 | virtual nsresult Init(); |
michael@0 | 216 | virtual nsresult Shutdown(); |
michael@0 | 217 | |
michael@0 | 218 | virtual void AsyncPreload(DOMStorageCacheBridge* aCache, bool aPriority = false) |
michael@0 | 219 | { InsertDBOp(new DBOperation(aPriority ? DBOperation::opPreloadUrgent : DBOperation::opPreload, aCache)); } |
michael@0 | 220 | |
michael@0 | 221 | virtual void SyncPreload(DOMStorageCacheBridge* aCache, bool aForce = false); |
michael@0 | 222 | |
michael@0 | 223 | virtual void AsyncGetUsage(DOMStorageUsageBridge * aUsage) |
michael@0 | 224 | { InsertDBOp(new DBOperation(DBOperation::opGetUsage, aUsage)); } |
michael@0 | 225 | |
michael@0 | 226 | virtual nsresult AsyncAddItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) |
michael@0 | 227 | { return InsertDBOp(new DBOperation(DBOperation::opAddItem, aCache, aKey, aValue)); } |
michael@0 | 228 | |
michael@0 | 229 | virtual nsresult AsyncUpdateItem(DOMStorageCacheBridge* aCache, const nsAString& aKey, const nsAString& aValue) |
michael@0 | 230 | { return InsertDBOp(new DBOperation(DBOperation::opUpdateItem, aCache, aKey, aValue)); } |
michael@0 | 231 | |
michael@0 | 232 | virtual nsresult AsyncRemoveItem(DOMStorageCacheBridge* aCache, const nsAString& aKey) |
michael@0 | 233 | { return InsertDBOp(new DBOperation(DBOperation::opRemoveItem, aCache, aKey)); } |
michael@0 | 234 | |
michael@0 | 235 | virtual nsresult AsyncClear(DOMStorageCacheBridge* aCache) |
michael@0 | 236 | { return InsertDBOp(new DBOperation(DBOperation::opClear, aCache)); } |
michael@0 | 237 | |
michael@0 | 238 | virtual void AsyncClearAll() |
michael@0 | 239 | { InsertDBOp(new DBOperation(DBOperation::opClearAll)); } |
michael@0 | 240 | |
michael@0 | 241 | virtual void AsyncClearMatchingScope(const nsACString& aScope) |
michael@0 | 242 | { InsertDBOp(new DBOperation(DBOperation::opClearMatchingScope, aScope)); } |
michael@0 | 243 | |
michael@0 | 244 | virtual void AsyncFlush(); |
michael@0 | 245 | |
michael@0 | 246 | virtual bool ShouldPreloadScope(const nsACString& aScope); |
michael@0 | 247 | virtual void GetScopesHavingData(InfallibleTArray<nsCString>* aScopes); |
michael@0 | 248 | |
michael@0 | 249 | private: |
michael@0 | 250 | nsCOMPtr<nsIFile> mDatabaseFile; |
michael@0 | 251 | PRThread* mThread; |
michael@0 | 252 | |
michael@0 | 253 | // The monitor we drive the thread with |
michael@0 | 254 | Monitor mMonitor; |
michael@0 | 255 | |
michael@0 | 256 | // Flag to stop, protected by the monitor |
michael@0 | 257 | bool mStopIOThread; |
michael@0 | 258 | |
michael@0 | 259 | // Whether WAL is enabled |
michael@0 | 260 | bool mWALModeEnabled; |
michael@0 | 261 | |
michael@0 | 262 | // Whether DB has already been open, avoid races between main thread reads |
michael@0 | 263 | // and pending DB init in the background I/O thread |
michael@0 | 264 | bool mDBReady; |
michael@0 | 265 | |
michael@0 | 266 | // State of the database initiation |
michael@0 | 267 | nsresult mStatus; |
michael@0 | 268 | |
michael@0 | 269 | // List of scopes having data, for optimization purposes only |
michael@0 | 270 | nsTHashtable<nsCStringHashKey> mScopesHavingData; |
michael@0 | 271 | |
michael@0 | 272 | StatementCache mWorkerStatements; |
michael@0 | 273 | StatementCache mReaderStatements; |
michael@0 | 274 | |
michael@0 | 275 | // Connection used by the worker thread for all read and write ops |
michael@0 | 276 | nsCOMPtr<mozIStorageConnection> mWorkerConnection; |
michael@0 | 277 | |
michael@0 | 278 | // Connection used only on the main thread for sync read operations |
michael@0 | 279 | nsCOMPtr<mozIStorageConnection> mReaderConnection; |
michael@0 | 280 | |
michael@0 | 281 | // Time the first pending operation has been added to the pending operations |
michael@0 | 282 | // list |
michael@0 | 283 | PRIntervalTime mDirtyEpoch; |
michael@0 | 284 | |
michael@0 | 285 | // Flag to force immediate flush of all pending operations |
michael@0 | 286 | bool mFlushImmediately; |
michael@0 | 287 | |
michael@0 | 288 | // List of preloading operations, in chronological or priority order. |
michael@0 | 289 | // Executed prioritly over pending update operations. |
michael@0 | 290 | nsTArray<DBOperation*> mPreloads; |
michael@0 | 291 | |
michael@0 | 292 | // Collector of pending update operations |
michael@0 | 293 | PendingOperations mPendingTasks; |
michael@0 | 294 | |
michael@0 | 295 | // Counter of calls for thread priority rising. |
michael@0 | 296 | int32_t mPriorityCounter; |
michael@0 | 297 | |
michael@0 | 298 | // Helper to direct an operation to one of the arrays above; |
michael@0 | 299 | // also checks IsScopeClearPending for preloads |
michael@0 | 300 | nsresult InsertDBOp(DBOperation* aOperation); |
michael@0 | 301 | |
michael@0 | 302 | // Opens the database, first thing we do after start of the thread. |
michael@0 | 303 | nsresult OpenDatabaseConnection(); |
michael@0 | 304 | nsresult InitDatabase(); |
michael@0 | 305 | nsresult ShutdownDatabase(); |
michael@0 | 306 | |
michael@0 | 307 | // Tries to establish WAL mode |
michael@0 | 308 | nsresult SetJournalMode(bool aIsWal); |
michael@0 | 309 | nsresult TryJournalMode(); |
michael@0 | 310 | |
michael@0 | 311 | // Sets the threshold for auto-checkpointing the WAL. |
michael@0 | 312 | nsresult ConfigureWALBehavior(); |
michael@0 | 313 | |
michael@0 | 314 | void SetHigherPriority(); |
michael@0 | 315 | void SetDefaultPriority(); |
michael@0 | 316 | |
michael@0 | 317 | // Ensures we flush pending tasks in some reasonble time |
michael@0 | 318 | void ScheduleFlush(); |
michael@0 | 319 | |
michael@0 | 320 | // Called when flush of pending tasks is being executed |
michael@0 | 321 | void UnscheduleFlush(); |
michael@0 | 322 | |
michael@0 | 323 | // This method is used for two purposes: |
michael@0 | 324 | // 1. as a value passed to monitor.Wait() method |
michael@0 | 325 | // 2. as in indicator that flush has to be performed |
michael@0 | 326 | // |
michael@0 | 327 | // Return: |
michael@0 | 328 | // - PR_INTERVAL_NO_TIMEOUT when no pending tasks are scheduled |
michael@0 | 329 | // - larger then zero when tasks have been scheduled, but it is |
michael@0 | 330 | // still not time to perform the flush ; it is actual interval |
michael@0 | 331 | // time to wait until the flush has to happen |
michael@0 | 332 | // - 0 when it is time to do the flush |
michael@0 | 333 | PRIntervalTime TimeUntilFlush(); |
michael@0 | 334 | |
michael@0 | 335 | // Notifies to the main thread that flush has completed |
michael@0 | 336 | void NotifyFlushCompletion(); |
michael@0 | 337 | |
michael@0 | 338 | // Thread loop |
michael@0 | 339 | static void ThreadFunc(void* aArg); |
michael@0 | 340 | void ThreadFunc(); |
michael@0 | 341 | }; |
michael@0 | 342 | |
michael@0 | 343 | } // ::dom |
michael@0 | 344 | } // ::mozilla |
michael@0 | 345 | |
michael@0 | 346 | #endif /* DOMStorageDBThread_h___ */ |