Coverage Report

Created: 2018-09-25 14:53

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