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