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