DataStorage.h 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #ifndef mozilla_DataStorage_h
  6. #define mozilla_DataStorage_h
  7. #include "mozilla/Monitor.h"
  8. #include "mozilla/Mutex.h"
  9. #include "mozilla/StaticPtr.h"
  10. #include "nsCOMPtr.h"
  11. #include "nsDataHashtable.h"
  12. #include "nsIObserver.h"
  13. #include "nsIThread.h"
  14. #include "nsITimer.h"
  15. #include "nsRefPtrHashtable.h"
  16. #include "nsString.h"
  17. namespace mozilla {
  18. namespace dom {
  19. class DataStorageItem;
  20. }
  21. /**
  22. * DataStorage is a threadsafe, generic, narrow string-based hash map that
  23. * persists data on disk and additionally handles temporary and private data.
  24. * However, if used in a context where there is no profile directory, data
  25. * will not be persisted.
  26. *
  27. * Its lifecycle is as follows:
  28. * - Allocate with a filename (this is or will eventually be a file in the
  29. * profile directory, if the profile exists).
  30. * - Call Init() from the main thread. This spins off an asynchronous read
  31. * of the backing file.
  32. * - Eventually observers of the topic "data-storage-ready" will be notified
  33. * with the backing filename as the data in the notification when this
  34. * has completed.
  35. * - Should the profile directory not be available, (e.g. in xpcshell),
  36. * DataStorage will not initially read any persistent data. The
  37. * "data-storage-ready" event will still be emitted. This follows semantics
  38. * similar to the permission manager and allows tests that test
  39. * unrelated components to proceed without a profile.
  40. * - When any persistent data changes, a timer is initialized that will
  41. * eventually asynchronously write all persistent data to the backing file.
  42. * When this happens, observers will be notified with the topic
  43. * "data-storage-written" and the backing filename as the data.
  44. * It is possible to receive a "data-storage-written" event while there exist
  45. * pending persistent data changes. However, those changes will cause the
  46. * timer to be reinitialized and another "data-storage-written" event will
  47. * be sent.
  48. * - When DataStorage observes the topic "profile-before-change" in
  49. * anticipation of shutdown, all persistent data is synchronously written to
  50. * the backing file. The worker thread responsible for these writes is then
  51. * disabled to prevent further writes to that file (the delayed-write timer
  52. * is cancelled when this happens).
  53. * - For testing purposes, the preference "test.datastorage.write_timer_ms" can
  54. * be set to cause the asynchronous writing of data to happen more quickly.
  55. * - To prevent unbounded memory and disk use, the number of entries in each
  56. * table is limited to 1024. Evictions are handled in by a modified LRU scheme
  57. * (see implementation comments).
  58. * - NB: Instances of DataStorage have long lifetimes because they are strong
  59. * observers of events and won't go away until the observer service does.
  60. *
  61. * For each key/value:
  62. * - The key must be a non-empty string containing no instances of '\t' or '\n'
  63. * (this is a limitation of how the data is stored and will be addressed in
  64. * the future).
  65. * - The key must have a length no more than 256.
  66. * - The value must not contain '\n' and must have a length no more than 1024.
  67. * (the length limits are to prevent unbounded disk and memory usage)
  68. */
  69. /**
  70. * Data that is DataStorage_Persistent is saved on disk. DataStorage_Temporary
  71. * and DataStorage_Private are not saved. DataStorage_Private is meant to
  72. * only be set and accessed from private contexts. It will be cleared upon
  73. * observing the event "last-pb-context-exited".
  74. */
  75. enum DataStorageType {
  76. DataStorage_Persistent,
  77. DataStorage_Temporary,
  78. DataStorage_Private
  79. };
  80. class DataStorage : public nsIObserver
  81. {
  82. typedef dom::DataStorageItem DataStorageItem;
  83. public:
  84. NS_DECL_THREADSAFE_ISUPPORTS
  85. NS_DECL_NSIOBSERVER
  86. // If there is a profile directory, there is or will eventually be a file
  87. // by the name specified by aFilename there.
  88. static already_AddRefed<DataStorage> Get(const nsString& aFilename);
  89. static already_AddRefed<DataStorage> GetIfExists(const nsString& aFilename);
  90. // Initializes the DataStorage. Must be called before using.
  91. // aDataWillPersist returns whether or not data can be persistently saved.
  92. nsresult Init(/*out*/bool& aDataWillPersist);
  93. // Given a key and a type of data, returns a value. Returns an empty string if
  94. // the key is not present for that type of data. If Get is called before the
  95. // "data-storage-ready" event is observed, it will block. NB: It is not
  96. // currently possible to differentiate between missing data and data that is
  97. // the empty string.
  98. nsCString Get(const nsCString& aKey, DataStorageType aType);
  99. // Give a key, value, and type of data, adds an entry as appropriate.
  100. // Updates existing entries.
  101. nsresult Put(const nsCString& aKey, const nsCString& aValue,
  102. DataStorageType aType);
  103. // Given a key and type of data, removes an entry if present.
  104. void Remove(const nsCString& aKey, DataStorageType aType);
  105. // Removes all entries of all types of data.
  106. nsresult Clear();
  107. // Read all of the data items.
  108. void GetAll(InfallibleTArray<DataStorageItem>* aItems);
  109. private:
  110. explicit DataStorage(const nsString& aFilename);
  111. virtual ~DataStorage();
  112. class Writer;
  113. class Reader;
  114. class Entry
  115. {
  116. public:
  117. Entry();
  118. bool UpdateScore();
  119. uint32_t mScore;
  120. int32_t mLastAccessed; // the last accessed time in days since the epoch
  121. nsCString mValue;
  122. };
  123. // Utility class for scanning tables for an entry to evict.
  124. class KeyAndEntry
  125. {
  126. public:
  127. nsCString mKey;
  128. Entry mEntry;
  129. };
  130. typedef nsDataHashtable<nsCStringHashKey, Entry> DataStorageTable;
  131. typedef nsRefPtrHashtable<nsStringHashKey, DataStorage> DataStorages;
  132. void WaitForReady();
  133. nsresult AsyncWriteData(const MutexAutoLock& aProofOfLock);
  134. nsresult AsyncReadData(bool& aHaveProfileDir,
  135. const MutexAutoLock& aProofOfLock);
  136. nsresult AsyncSetTimer(const MutexAutoLock& aProofOfLock);
  137. nsresult DispatchShutdownTimer(const MutexAutoLock& aProofOfLock);
  138. static nsresult ValidateKeyAndValue(const nsCString& aKey,
  139. const nsCString& aValue);
  140. static void TimerCallback(nsITimer* aTimer, void* aClosure);
  141. void SetTimer();
  142. void ShutdownTimer();
  143. void NotifyObservers(const char* aTopic);
  144. bool GetInternal(const nsCString& aKey, Entry* aEntry, DataStorageType aType,
  145. const MutexAutoLock& aProofOfLock);
  146. nsresult PutInternal(const nsCString& aKey, Entry& aEntry,
  147. DataStorageType aType,
  148. const MutexAutoLock& aProofOfLock);
  149. void MaybeEvictOneEntry(DataStorageType aType,
  150. const MutexAutoLock& aProofOfLock);
  151. DataStorageTable& GetTableForType(DataStorageType aType,
  152. const MutexAutoLock& aProofOfLock);
  153. void ReadAllFromTable(DataStorageType aType,
  154. InfallibleTArray<DataStorageItem>* aItems,
  155. const MutexAutoLock& aProofOfLock);
  156. Mutex mMutex; // This mutex protects access to the following members:
  157. DataStorageTable mPersistentDataTable;
  158. DataStorageTable mTemporaryDataTable;
  159. DataStorageTable mPrivateDataTable;
  160. nsCOMPtr<nsIThread> mWorkerThread;
  161. nsCOMPtr<nsIFile> mBackingFile;
  162. nsCOMPtr<nsITimer> mTimer; // All uses after init must be on the worker thread
  163. uint32_t mTimerDelay; // in milliseconds
  164. bool mPendingWrite; // true if a write is needed but hasn't been dispatched
  165. bool mShuttingDown;
  166. bool mInitCalled; // Indicates that Init() has been called.
  167. // (End list of members protected by mMutex)
  168. Monitor mReadyMonitor; // Do not acquire this at the same time as mMutex.
  169. bool mReady; // Indicates that saved data has been read and Get can proceed.
  170. const nsString mFilename;
  171. static StaticAutoPtr<DataStorages> sDataStorages;
  172. };
  173. } // namespace mozilla
  174. #endif // mozilla_DataStorage_h