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