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