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