Context.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149
  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. #include "mozilla/dom/cache/Context.h"
  6. #include "mozilla/AutoRestore.h"
  7. #include "mozilla/dom/cache/Action.h"
  8. #include "mozilla/dom/cache/FileUtils.h"
  9. #include "mozilla/dom/cache/Manager.h"
  10. #include "mozilla/dom/cache/ManagerId.h"
  11. #include "mozilla/dom/quota/QuotaManager.h"
  12. #include "mozIStorageConnection.h"
  13. #include "nsIFile.h"
  14. #include "nsIPrincipal.h"
  15. #include "nsIRunnable.h"
  16. #include "nsThreadUtils.h"
  17. namespace {
  18. using mozilla::dom::cache::Action;
  19. using mozilla::dom::cache::QuotaInfo;
  20. class NullAction final : public Action
  21. {
  22. public:
  23. NullAction()
  24. {
  25. }
  26. virtual void
  27. RunOnTarget(Resolver* aResolver, const QuotaInfo&, Data*) override
  28. {
  29. // Resolve success immediately. This Action does no actual work.
  30. MOZ_DIAGNOSTIC_ASSERT(aResolver);
  31. aResolver->Resolve(NS_OK);
  32. }
  33. };
  34. } // namespace
  35. namespace mozilla {
  36. namespace dom {
  37. namespace cache {
  38. using mozilla::dom::quota::AssertIsOnIOThread;
  39. using mozilla::dom::quota::OpenDirectoryListener;
  40. using mozilla::dom::quota::QuotaManager;
  41. using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
  42. using mozilla::dom::quota::PersistenceType;
  43. class Context::Data final : public Action::Data
  44. {
  45. public:
  46. explicit Data(nsIThread* aTarget)
  47. : mTarget(aTarget)
  48. {
  49. MOZ_DIAGNOSTIC_ASSERT(mTarget);
  50. }
  51. virtual mozIStorageConnection*
  52. GetConnection() const override
  53. {
  54. MOZ_ASSERT(mTarget == NS_GetCurrentThread());
  55. return mConnection;
  56. }
  57. virtual void
  58. SetConnection(mozIStorageConnection* aConn) override
  59. {
  60. MOZ_ASSERT(mTarget == NS_GetCurrentThread());
  61. MOZ_DIAGNOSTIC_ASSERT(!mConnection);
  62. mConnection = aConn;
  63. MOZ_DIAGNOSTIC_ASSERT(mConnection);
  64. }
  65. private:
  66. ~Data()
  67. {
  68. // We could proxy release our data here, but instead just assert. The
  69. // Context code should guarantee that we are destroyed on the target
  70. // thread once the connection is initialized. If we're not, then
  71. // QuotaManager might race and try to clear the origin out from under us.
  72. MOZ_ASSERT_IF(mConnection, mTarget == NS_GetCurrentThread());
  73. }
  74. nsCOMPtr<nsIThread> mTarget;
  75. nsCOMPtr<mozIStorageConnection> mConnection;
  76. // Threadsafe counting because we're created on the PBackground thread
  77. // and destroyed on the target IO thread.
  78. NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Context::Data)
  79. };
  80. // Executed to perform the complicated dance of steps necessary to initialize
  81. // the QuotaManager. This must be performed for each origin before any disk
  82. // IO occurrs.
  83. class Context::QuotaInitRunnable final : public nsIRunnable
  84. , public OpenDirectoryListener
  85. {
  86. public:
  87. QuotaInitRunnable(Context* aContext,
  88. Manager* aManager,
  89. Data* aData,
  90. nsIThread* aTarget,
  91. Action* aInitAction)
  92. : mContext(aContext)
  93. , mThreadsafeHandle(aContext->CreateThreadsafeHandle())
  94. , mManager(aManager)
  95. , mData(aData)
  96. , mTarget(aTarget)
  97. , mInitAction(aInitAction)
  98. , mInitiatingThread(NS_GetCurrentThread())
  99. , mResult(NS_OK)
  100. , mState(STATE_INIT)
  101. , mCanceled(false)
  102. {
  103. MOZ_DIAGNOSTIC_ASSERT(mContext);
  104. MOZ_DIAGNOSTIC_ASSERT(mManager);
  105. MOZ_DIAGNOSTIC_ASSERT(mData);
  106. MOZ_DIAGNOSTIC_ASSERT(mTarget);
  107. MOZ_DIAGNOSTIC_ASSERT(mInitiatingThread);
  108. MOZ_DIAGNOSTIC_ASSERT(mInitAction);
  109. }
  110. nsresult Dispatch()
  111. {
  112. NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
  113. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_INIT);
  114. mState = STATE_GET_INFO;
  115. nsresult rv = NS_DispatchToMainThread(this, nsIThread::DISPATCH_NORMAL);
  116. if (NS_WARN_IF(NS_FAILED(rv))) {
  117. mState = STATE_COMPLETE;
  118. Clear();
  119. }
  120. return rv;
  121. }
  122. void Cancel()
  123. {
  124. NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
  125. MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
  126. mCanceled = true;
  127. mInitAction->CancelOnInitiatingThread();
  128. }
  129. void OpenDirectory();
  130. // OpenDirectoryListener methods
  131. virtual void
  132. DirectoryLockAcquired(DirectoryLock* aLock) override;
  133. virtual void
  134. DirectoryLockFailed() override;
  135. private:
  136. class SyncResolver final : public Action::Resolver
  137. {
  138. public:
  139. SyncResolver()
  140. : mResolved(false)
  141. , mResult(NS_OK)
  142. { }
  143. virtual void
  144. Resolve(nsresult aRv) override
  145. {
  146. MOZ_DIAGNOSTIC_ASSERT(!mResolved);
  147. mResolved = true;
  148. mResult = aRv;
  149. };
  150. bool Resolved() const { return mResolved; }
  151. nsresult Result() const { return mResult; }
  152. private:
  153. ~SyncResolver() { }
  154. bool mResolved;
  155. nsresult mResult;
  156. NS_INLINE_DECL_REFCOUNTING(Context::QuotaInitRunnable::SyncResolver, override)
  157. };
  158. ~QuotaInitRunnable()
  159. {
  160. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_COMPLETE);
  161. MOZ_DIAGNOSTIC_ASSERT(!mContext);
  162. MOZ_DIAGNOSTIC_ASSERT(!mInitAction);
  163. }
  164. enum State
  165. {
  166. STATE_INIT,
  167. STATE_GET_INFO,
  168. STATE_CREATE_QUOTA_MANAGER,
  169. STATE_OPEN_DIRECTORY,
  170. STATE_WAIT_FOR_DIRECTORY_LOCK,
  171. STATE_ENSURE_ORIGIN_INITIALIZED,
  172. STATE_RUN_ON_TARGET,
  173. STATE_RUNNING,
  174. STATE_COMPLETING,
  175. STATE_COMPLETE
  176. };
  177. void Complete(nsresult aResult)
  178. {
  179. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_RUNNING || NS_FAILED(aResult));
  180. MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(mResult));
  181. mResult = aResult;
  182. mState = STATE_COMPLETING;
  183. MOZ_ALWAYS_SUCCEEDS(
  184. mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL));
  185. }
  186. void Clear()
  187. {
  188. NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
  189. MOZ_DIAGNOSTIC_ASSERT(mContext);
  190. mContext = nullptr;
  191. mManager = nullptr;
  192. mInitAction = nullptr;
  193. }
  194. RefPtr<Context> mContext;
  195. RefPtr<ThreadsafeHandle> mThreadsafeHandle;
  196. RefPtr<Manager> mManager;
  197. RefPtr<Data> mData;
  198. nsCOMPtr<nsIThread> mTarget;
  199. RefPtr<Action> mInitAction;
  200. nsCOMPtr<nsIThread> mInitiatingThread;
  201. nsresult mResult;
  202. QuotaInfo mQuotaInfo;
  203. RefPtr<DirectoryLock> mDirectoryLock;
  204. State mState;
  205. Atomic<bool> mCanceled;
  206. public:
  207. NS_DECL_THREADSAFE_ISUPPORTS
  208. NS_DECL_NSIRUNNABLE
  209. };
  210. void
  211. Context::QuotaInitRunnable::OpenDirectory()
  212. {
  213. NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
  214. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CREATE_QUOTA_MANAGER ||
  215. mState == STATE_OPEN_DIRECTORY);
  216. MOZ_DIAGNOSTIC_ASSERT(QuotaManager::Get());
  217. // QuotaManager::OpenDirectory() will hold a reference to us as
  218. // a listener. We will then get DirectoryLockAcquired() on the owning
  219. // thread when it is safe to access our storage directory.
  220. mState = STATE_WAIT_FOR_DIRECTORY_LOCK;
  221. QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
  222. mQuotaInfo.mGroup,
  223. mQuotaInfo.mOrigin,
  224. mQuotaInfo.mIsApp,
  225. quota::Client::DOMCACHE,
  226. /* aExclusive */ false,
  227. this);
  228. }
  229. void
  230. Context::QuotaInitRunnable::DirectoryLockAcquired(DirectoryLock* aLock)
  231. {
  232. NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
  233. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
  234. MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock);
  235. mDirectoryLock = aLock;
  236. if (mCanceled) {
  237. Complete(NS_ERROR_ABORT);
  238. return;
  239. }
  240. QuotaManager* qm = QuotaManager::Get();
  241. MOZ_DIAGNOSTIC_ASSERT(qm);
  242. mState = STATE_ENSURE_ORIGIN_INITIALIZED;
  243. nsresult rv = qm->IOThread()->Dispatch(this, nsIThread::DISPATCH_NORMAL);
  244. if (NS_WARN_IF(NS_FAILED(rv))) {
  245. Complete(rv);
  246. return;
  247. }
  248. }
  249. void
  250. Context::QuotaInitRunnable::DirectoryLockFailed()
  251. {
  252. NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
  253. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
  254. MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock);
  255. NS_WARNING("Failed to acquire a directory lock!");
  256. Complete(NS_ERROR_FAILURE);
  257. }
  258. NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::QuotaInitRunnable, nsIRunnable);
  259. // The QuotaManager init state machine is represented in the following diagram:
  260. //
  261. // +---------------+
  262. // | Start | Resolve(error)
  263. // | (Orig Thread) +---------------------+
  264. // +-------+-------+ |
  265. // | |
  266. // +----------v-----------+ |
  267. // | GetInfo | Resolve(error) |
  268. // | (Main Thread) +-----------------+
  269. // +----------+-----------+ |
  270. // | |
  271. // +----------v-----------+ |
  272. // | CreateQuotaManager | Resolve(error) |
  273. // | (Orig Thread) +-----------------+
  274. // +----------+-----------+ |
  275. // | |
  276. // +----------v-----------+ |
  277. // | OpenDirectory | Resolve(error) |
  278. // | (Orig Thread) +-----------------+
  279. // +----------+-----------+ |
  280. // | |
  281. // +----------v-----------+ |
  282. // | WaitForDirectoryLock | Resolve(error) |
  283. // | (Orig Thread) +-----------------+
  284. // +----------+-----------+ |
  285. // | |
  286. // +----------v------------+ |
  287. // |EnsureOriginInitialized| Resolve(error) |
  288. // | (Quota IO Thread) +----------------+
  289. // +----------+------------+ |
  290. // | |
  291. // +----------v------------+ |
  292. // | RunOnTarget | Resolve(error) |
  293. // | (Target Thread) +----------------+
  294. // +----------+------------+ |
  295. // | |
  296. // +---------v---------+ +------v------+
  297. // | Running | | Completing |
  298. // | (Target Thread) +------------>(Orig Thread)|
  299. // +-------------------+ +------+------+
  300. // |
  301. // +-----v----+
  302. // | Complete |
  303. // +----------+
  304. //
  305. // The initialization process proceeds through the main states. If an error
  306. // occurs, then we transition to Completing state back on the original thread.
  307. NS_IMETHODIMP
  308. Context::QuotaInitRunnable::Run()
  309. {
  310. // May run on different threads depending on the state. See individual
  311. // state cases for thread assertions.
  312. RefPtr<SyncResolver> resolver = new SyncResolver();
  313. switch(mState) {
  314. // -----------------------------------
  315. case STATE_GET_INFO:
  316. {
  317. MOZ_ASSERT(NS_IsMainThread());
  318. if (mCanceled) {
  319. resolver->Resolve(NS_ERROR_ABORT);
  320. break;
  321. }
  322. RefPtr<ManagerId> managerId = mManager->GetManagerId();
  323. nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
  324. nsresult rv = QuotaManager::GetInfoFromPrincipal(principal,
  325. &mQuotaInfo.mSuffix,
  326. &mQuotaInfo.mGroup,
  327. &mQuotaInfo.mOrigin,
  328. &mQuotaInfo.mIsApp);
  329. if (NS_WARN_IF(NS_FAILED(rv))) {
  330. resolver->Resolve(rv);
  331. break;
  332. }
  333. mState = STATE_CREATE_QUOTA_MANAGER;
  334. MOZ_ALWAYS_SUCCEEDS(
  335. mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL));
  336. break;
  337. }
  338. // ----------------------------------
  339. case STATE_CREATE_QUOTA_MANAGER:
  340. {
  341. NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
  342. if (mCanceled || QuotaManager::IsShuttingDown()) {
  343. resolver->Resolve(NS_ERROR_ABORT);
  344. break;
  345. }
  346. if (QuotaManager::Get()) {
  347. OpenDirectory();
  348. return NS_OK;
  349. }
  350. mState = STATE_OPEN_DIRECTORY;
  351. QuotaManager::GetOrCreate(this);
  352. break;
  353. }
  354. // ----------------------------------
  355. case STATE_OPEN_DIRECTORY:
  356. {
  357. NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
  358. if (NS_WARN_IF(!QuotaManager::Get())) {
  359. resolver->Resolve(NS_ERROR_FAILURE);
  360. break;
  361. }
  362. OpenDirectory();
  363. break;
  364. }
  365. // ----------------------------------
  366. case STATE_ENSURE_ORIGIN_INITIALIZED:
  367. {
  368. AssertIsOnIOThread();
  369. if (mCanceled) {
  370. resolver->Resolve(NS_ERROR_ABORT);
  371. break;
  372. }
  373. QuotaManager* qm = QuotaManager::Get();
  374. MOZ_DIAGNOSTIC_ASSERT(qm);
  375. nsresult rv = qm->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
  376. mQuotaInfo.mSuffix,
  377. mQuotaInfo.mGroup,
  378. mQuotaInfo.mOrigin,
  379. mQuotaInfo.mIsApp,
  380. getter_AddRefs(mQuotaInfo.mDir));
  381. if (NS_FAILED(rv)) {
  382. resolver->Resolve(rv);
  383. break;
  384. }
  385. mState = STATE_RUN_ON_TARGET;
  386. MOZ_ALWAYS_SUCCEEDS(
  387. mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
  388. break;
  389. }
  390. // -------------------
  391. case STATE_RUN_ON_TARGET:
  392. {
  393. MOZ_ASSERT(NS_GetCurrentThread() == mTarget);
  394. mState = STATE_RUNNING;
  395. // Execute the provided initialization Action. The Action must Resolve()
  396. // before returning.
  397. mInitAction->RunOnTarget(resolver, mQuotaInfo, mData);
  398. MOZ_DIAGNOSTIC_ASSERT(resolver->Resolved());
  399. mData = nullptr;
  400. // If the database was opened, then we should always succeed when creating
  401. // the marker file. If it wasn't opened successfully, then no need to
  402. // create a marker file anyway.
  403. if (NS_SUCCEEDED(resolver->Result())) {
  404. MOZ_ALWAYS_SUCCEEDS(CreateMarkerFile(mQuotaInfo));
  405. }
  406. break;
  407. }
  408. // -------------------
  409. case STATE_COMPLETING:
  410. {
  411. NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
  412. mInitAction->CompleteOnInitiatingThread(mResult);
  413. mContext->OnQuotaInit(mResult, mQuotaInfo, mDirectoryLock.forget());
  414. mState = STATE_COMPLETE;
  415. // Explicitly cleanup here as the destructor could fire on any of
  416. // the threads we have bounced through.
  417. Clear();
  418. break;
  419. }
  420. // -----
  421. case STATE_WAIT_FOR_DIRECTORY_LOCK:
  422. default:
  423. {
  424. MOZ_CRASH("unexpected state in QuotaInitRunnable");
  425. }
  426. }
  427. if (resolver->Resolved()) {
  428. Complete(resolver->Result());
  429. }
  430. return NS_OK;
  431. }
  432. // Runnable wrapper around Action objects dispatched on the Context. This
  433. // runnable executes the Action on the appropriate threads while the Context
  434. // is initialized.
  435. class Context::ActionRunnable final : public nsIRunnable
  436. , public Action::Resolver
  437. , public Context::Activity
  438. {
  439. public:
  440. ActionRunnable(Context* aContext, Data* aData, nsIEventTarget* aTarget,
  441. Action* aAction, const QuotaInfo& aQuotaInfo)
  442. : mContext(aContext)
  443. , mData(aData)
  444. , mTarget(aTarget)
  445. , mAction(aAction)
  446. , mQuotaInfo(aQuotaInfo)
  447. , mInitiatingThread(NS_GetCurrentThread())
  448. , mState(STATE_INIT)
  449. , mResult(NS_OK)
  450. , mExecutingRunOnTarget(false)
  451. {
  452. MOZ_DIAGNOSTIC_ASSERT(mContext);
  453. // mData may be nullptr
  454. MOZ_DIAGNOSTIC_ASSERT(mTarget);
  455. MOZ_DIAGNOSTIC_ASSERT(mAction);
  456. // mQuotaInfo.mDir may be nullptr if QuotaInitRunnable failed
  457. MOZ_DIAGNOSTIC_ASSERT(mInitiatingThread);
  458. }
  459. nsresult Dispatch()
  460. {
  461. NS_ASSERT_OWNINGTHREAD(ActionRunnable);
  462. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_INIT);
  463. mState = STATE_RUN_ON_TARGET;
  464. nsresult rv = mTarget->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
  465. if (NS_WARN_IF(NS_FAILED(rv))) {
  466. mState = STATE_COMPLETE;
  467. Clear();
  468. }
  469. return rv;
  470. }
  471. virtual bool
  472. MatchesCacheId(CacheId aCacheId) const override
  473. {
  474. NS_ASSERT_OWNINGTHREAD(ActionRunnable);
  475. return mAction->MatchesCacheId(aCacheId);
  476. }
  477. virtual void
  478. Cancel() override
  479. {
  480. NS_ASSERT_OWNINGTHREAD(ActionRunnable);
  481. mAction->CancelOnInitiatingThread();
  482. }
  483. virtual void Resolve(nsresult aRv) override
  484. {
  485. MOZ_ASSERT(mTarget == NS_GetCurrentThread());
  486. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_RUNNING);
  487. mResult = aRv;
  488. // We ultimately must complete on the initiating thread, but bounce through
  489. // the current thread again to ensure that we don't destroy objects and
  490. // state out from under the currently running action's stack.
  491. mState = STATE_RESOLVING;
  492. // If we were resolved synchronously within Action::RunOnTarget() then we
  493. // can avoid a thread bounce and just resolve once RunOnTarget() returns.
  494. // The Run() method will handle this by looking at mState after
  495. // RunOnTarget() returns.
  496. if (mExecutingRunOnTarget) {
  497. return;
  498. }
  499. // Otherwise we are in an asynchronous resolve. And must perform a thread
  500. // bounce to run on the target thread again.
  501. MOZ_ALWAYS_SUCCEEDS(
  502. mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
  503. }
  504. private:
  505. ~ActionRunnable()
  506. {
  507. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_COMPLETE);
  508. MOZ_DIAGNOSTIC_ASSERT(!mContext);
  509. MOZ_DIAGNOSTIC_ASSERT(!mAction);
  510. }
  511. void Clear()
  512. {
  513. NS_ASSERT_OWNINGTHREAD(ActionRunnable);
  514. MOZ_DIAGNOSTIC_ASSERT(mContext);
  515. MOZ_DIAGNOSTIC_ASSERT(mAction);
  516. mContext->RemoveActivity(this);
  517. mContext = nullptr;
  518. mAction = nullptr;
  519. }
  520. enum State
  521. {
  522. STATE_INIT,
  523. STATE_RUN_ON_TARGET,
  524. STATE_RUNNING,
  525. STATE_RESOLVING,
  526. STATE_COMPLETING,
  527. STATE_COMPLETE
  528. };
  529. RefPtr<Context> mContext;
  530. RefPtr<Data> mData;
  531. nsCOMPtr<nsIEventTarget> mTarget;
  532. RefPtr<Action> mAction;
  533. const QuotaInfo mQuotaInfo;
  534. nsCOMPtr<nsIThread> mInitiatingThread;
  535. State mState;
  536. nsresult mResult;
  537. // Only accessible on target thread;
  538. bool mExecutingRunOnTarget;
  539. public:
  540. NS_DECL_THREADSAFE_ISUPPORTS
  541. NS_DECL_NSIRUNNABLE
  542. };
  543. NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::ActionRunnable, nsIRunnable);
  544. // The ActionRunnable has a simpler state machine. It basically needs to run
  545. // the action on the target thread and then complete on the original thread.
  546. //
  547. // +-------------+
  548. // | Start |
  549. // |(Orig Thread)|
  550. // +-----+-------+
  551. // |
  552. // +-------v---------+
  553. // | RunOnTarget |
  554. // |Target IO Thread)+---+ Resolve()
  555. // +-------+---------+ |
  556. // | |
  557. // +-------v----------+ |
  558. // | Running | |
  559. // |(Target IO Thread)| |
  560. // +------------------+ |
  561. // | Resolve() |
  562. // +-------v----------+ |
  563. // | Resolving <--+ +-------------+
  564. // | | | Completing |
  565. // |(Target IO Thread)+---------------------->(Orig Thread)|
  566. // +------------------+ +-------+-----+
  567. // |
  568. // |
  569. // +----v---+
  570. // |Complete|
  571. // +--------+
  572. //
  573. // Its important to note that synchronous actions will effectively Resolve()
  574. // out of the Running state immediately. Asynchronous Actions may remain
  575. // in the Running state for some time, but normally the ActionRunnable itself
  576. // does not see any execution there. Its all handled internal to the Action.
  577. NS_IMETHODIMP
  578. Context::ActionRunnable::Run()
  579. {
  580. switch(mState) {
  581. // ----------------------
  582. case STATE_RUN_ON_TARGET:
  583. {
  584. MOZ_ASSERT(NS_GetCurrentThread() == mTarget);
  585. MOZ_DIAGNOSTIC_ASSERT(!mExecutingRunOnTarget);
  586. // Note that we are calling RunOnTarget(). This lets us detect
  587. // if Resolve() is called synchronously.
  588. AutoRestore<bool> executingRunOnTarget(mExecutingRunOnTarget);
  589. mExecutingRunOnTarget = true;
  590. mState = STATE_RUNNING;
  591. mAction->RunOnTarget(this, mQuotaInfo, mData);
  592. mData = nullptr;
  593. // Resolve was called synchronously from RunOnTarget(). We can
  594. // immediately move to completing now since we are sure RunOnTarget()
  595. // completed.
  596. if (mState == STATE_RESOLVING) {
  597. // Use recursion instead of switch case fall-through... Seems slightly
  598. // easier to understand.
  599. Run();
  600. }
  601. break;
  602. }
  603. // -----------------
  604. case STATE_RESOLVING:
  605. {
  606. MOZ_ASSERT(NS_GetCurrentThread() == mTarget);
  607. // The call to Action::RunOnTarget() must have returned now if we
  608. // are running on the target thread again. We may now proceed
  609. // with completion.
  610. mState = STATE_COMPLETING;
  611. // Shutdown must be delayed until all Contexts are destroyed. Crash
  612. // for this invariant violation.
  613. MOZ_ALWAYS_SUCCEEDS(
  614. mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL));
  615. break;
  616. }
  617. // -------------------
  618. case STATE_COMPLETING:
  619. {
  620. NS_ASSERT_OWNINGTHREAD(ActionRunnable);
  621. mAction->CompleteOnInitiatingThread(mResult);
  622. mState = STATE_COMPLETE;
  623. // Explicitly cleanup here as the destructor could fire on any of
  624. // the threads we have bounced through.
  625. Clear();
  626. break;
  627. }
  628. // -----------------
  629. default:
  630. {
  631. MOZ_CRASH("unexpected state in ActionRunnable");
  632. break;
  633. }
  634. }
  635. return NS_OK;
  636. }
  637. void
  638. Context::ThreadsafeHandle::AllowToClose()
  639. {
  640. if (mOwningThread == NS_GetCurrentThread()) {
  641. AllowToCloseOnOwningThread();
  642. return;
  643. }
  644. // Dispatch is guaranteed to succeed here because we block shutdown until
  645. // all Contexts have been destroyed.
  646. nsCOMPtr<nsIRunnable> runnable =
  647. NewRunnableMethod(this, &ThreadsafeHandle::AllowToCloseOnOwningThread);
  648. MOZ_ALWAYS_SUCCEEDS(
  649. mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL));
  650. }
  651. void
  652. Context::ThreadsafeHandle::InvalidateAndAllowToClose()
  653. {
  654. if (mOwningThread == NS_GetCurrentThread()) {
  655. InvalidateAndAllowToCloseOnOwningThread();
  656. return;
  657. }
  658. // Dispatch is guaranteed to succeed here because we block shutdown until
  659. // all Contexts have been destroyed.
  660. nsCOMPtr<nsIRunnable> runnable =
  661. NewRunnableMethod(this, &ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread);
  662. MOZ_ALWAYS_SUCCEEDS(
  663. mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL));
  664. }
  665. Context::ThreadsafeHandle::ThreadsafeHandle(Context* aContext)
  666. : mStrongRef(aContext)
  667. , mWeakRef(aContext)
  668. , mOwningThread(NS_GetCurrentThread())
  669. {
  670. }
  671. Context::ThreadsafeHandle::~ThreadsafeHandle()
  672. {
  673. // Normally we only touch mStrongRef on the owning thread. This is safe,
  674. // however, because when we do use mStrongRef on the owning thread we are
  675. // always holding a strong ref to the ThreadsafeHandle via the owning
  676. // runnable. So we cannot run the ThreadsafeHandle destructor simultaneously.
  677. if (!mStrongRef || mOwningThread == NS_GetCurrentThread()) {
  678. return;
  679. }
  680. // Dispatch is guaranteed to succeed here because we block shutdown until
  681. // all Contexts have been destroyed.
  682. NS_ProxyRelease(mOwningThread, mStrongRef.forget());
  683. }
  684. void
  685. Context::ThreadsafeHandle::AllowToCloseOnOwningThread()
  686. {
  687. MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
  688. // A Context "closes" when its ref count drops to zero. Dropping this
  689. // strong ref is necessary, but not sufficient for the close to occur.
  690. // Any outstanding IO will continue and keep the Context alive. Once
  691. // the Context is idle, it will be destroyed.
  692. // First, tell the context to flush any target thread shared data. This
  693. // data must be released on the target thread prior to running the Context
  694. // destructor. This will schedule an Action which ensures that the
  695. // ~Context() is not immediately executed when we drop the strong ref.
  696. if (mStrongRef) {
  697. mStrongRef->DoomTargetData();
  698. }
  699. // Now drop our strong ref and let Context finish running any outstanding
  700. // Actions.
  701. mStrongRef = nullptr;
  702. }
  703. void
  704. Context::ThreadsafeHandle::InvalidateAndAllowToCloseOnOwningThread()
  705. {
  706. MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
  707. // Cancel the Context through the weak reference. This means we can
  708. // allow the Context to close by dropping the strong ref, but then
  709. // still cancel ongoing IO if necessary.
  710. if (mWeakRef) {
  711. mWeakRef->Invalidate();
  712. }
  713. // We should synchronously have AllowToCloseOnOwningThread called when
  714. // the Context is canceled.
  715. MOZ_DIAGNOSTIC_ASSERT(!mStrongRef);
  716. }
  717. void
  718. Context::ThreadsafeHandle::ContextDestroyed(Context* aContext)
  719. {
  720. MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
  721. MOZ_DIAGNOSTIC_ASSERT(!mStrongRef);
  722. MOZ_DIAGNOSTIC_ASSERT(mWeakRef);
  723. MOZ_DIAGNOSTIC_ASSERT(mWeakRef == aContext);
  724. mWeakRef = nullptr;
  725. }
  726. // static
  727. already_AddRefed<Context>
  728. Context::Create(Manager* aManager, nsIThread* aTarget,
  729. Action* aInitAction, Context* aOldContext)
  730. {
  731. RefPtr<Context> context = new Context(aManager, aTarget, aInitAction);
  732. context->Init(aOldContext);
  733. return context.forget();
  734. }
  735. Context::Context(Manager* aManager, nsIThread* aTarget, Action* aInitAction)
  736. : mManager(aManager)
  737. , mTarget(aTarget)
  738. , mData(new Data(aTarget))
  739. , mState(STATE_CONTEXT_PREINIT)
  740. , mOrphanedData(false)
  741. , mInitAction(aInitAction)
  742. {
  743. MOZ_DIAGNOSTIC_ASSERT(mManager);
  744. MOZ_DIAGNOSTIC_ASSERT(mTarget);
  745. }
  746. void
  747. Context::Dispatch(Action* aAction)
  748. {
  749. NS_ASSERT_OWNINGTHREAD(Context);
  750. MOZ_DIAGNOSTIC_ASSERT(aAction);
  751. MOZ_DIAGNOSTIC_ASSERT(mState != STATE_CONTEXT_CANCELED);
  752. if (mState == STATE_CONTEXT_CANCELED) {
  753. return;
  754. } else if (mState == STATE_CONTEXT_INIT ||
  755. mState == STATE_CONTEXT_PREINIT) {
  756. PendingAction* pending = mPendingActions.AppendElement();
  757. pending->mAction = aAction;
  758. return;
  759. }
  760. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_READY);
  761. DispatchAction(aAction);
  762. }
  763. void
  764. Context::CancelAll()
  765. {
  766. NS_ASSERT_OWNINGTHREAD(Context);
  767. // In PREINIT state we have not dispatch the init action yet. Just
  768. // forget it.
  769. if (mState == STATE_CONTEXT_PREINIT) {
  770. MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
  771. mInitAction = nullptr;
  772. // In INIT state we have dispatched the runnable, but not received the
  773. // async completion yet. Cancel the runnable, but don't forget about it
  774. // until we get OnQuotaInit() callback.
  775. } else if (mState == STATE_CONTEXT_INIT) {
  776. mInitRunnable->Cancel();
  777. }
  778. mState = STATE_CONTEXT_CANCELED;
  779. mPendingActions.Clear();
  780. {
  781. ActivityList::ForwardIterator iter(mActivityList);
  782. while (iter.HasMore()) {
  783. iter.GetNext()->Cancel();
  784. }
  785. }
  786. AllowToClose();
  787. }
  788. bool
  789. Context::IsCanceled() const
  790. {
  791. NS_ASSERT_OWNINGTHREAD(Context);
  792. return mState == STATE_CONTEXT_CANCELED;
  793. }
  794. void
  795. Context::Invalidate()
  796. {
  797. NS_ASSERT_OWNINGTHREAD(Context);
  798. mManager->NoteClosing();
  799. CancelAll();
  800. }
  801. void
  802. Context::AllowToClose()
  803. {
  804. NS_ASSERT_OWNINGTHREAD(Context);
  805. if (mThreadsafeHandle) {
  806. mThreadsafeHandle->AllowToClose();
  807. }
  808. }
  809. void
  810. Context::CancelForCacheId(CacheId aCacheId)
  811. {
  812. NS_ASSERT_OWNINGTHREAD(Context);
  813. // Remove matching pending actions
  814. for (int32_t i = mPendingActions.Length() - 1; i >= 0; --i) {
  815. if (mPendingActions[i].mAction->MatchesCacheId(aCacheId)) {
  816. mPendingActions.RemoveElementAt(i);
  817. }
  818. }
  819. // Cancel activities and let them remove themselves
  820. ActivityList::ForwardIterator iter(mActivityList);
  821. while (iter.HasMore()) {
  822. Activity* activity = iter.GetNext();
  823. if (activity->MatchesCacheId(aCacheId)) {
  824. activity->Cancel();
  825. }
  826. }
  827. }
  828. Context::~Context()
  829. {
  830. NS_ASSERT_OWNINGTHREAD(Context);
  831. MOZ_DIAGNOSTIC_ASSERT(mManager);
  832. MOZ_DIAGNOSTIC_ASSERT(!mData);
  833. if (mThreadsafeHandle) {
  834. mThreadsafeHandle->ContextDestroyed(this);
  835. }
  836. // Note, this may set the mOrphanedData flag.
  837. mManager->RemoveContext(this);
  838. if (mQuotaInfo.mDir && !mOrphanedData) {
  839. MOZ_ALWAYS_SUCCEEDS(DeleteMarkerFile(mQuotaInfo));
  840. }
  841. if (mNextContext) {
  842. mNextContext->Start();
  843. }
  844. }
  845. void
  846. Context::Init(Context* aOldContext)
  847. {
  848. NS_ASSERT_OWNINGTHREAD(Context);
  849. if (aOldContext) {
  850. aOldContext->SetNextContext(this);
  851. return;
  852. }
  853. Start();
  854. }
  855. void
  856. Context::Start()
  857. {
  858. NS_ASSERT_OWNINGTHREAD(Context);
  859. // Previous context closing delayed our start, but then we were canceled.
  860. // In this case, just do nothing here.
  861. if (mState == STATE_CONTEXT_CANCELED) {
  862. MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
  863. MOZ_DIAGNOSTIC_ASSERT(!mInitAction);
  864. return;
  865. }
  866. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_PREINIT);
  867. MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
  868. mInitRunnable = new QuotaInitRunnable(this, mManager, mData, mTarget,
  869. mInitAction);
  870. mInitAction = nullptr;
  871. mState = STATE_CONTEXT_INIT;
  872. nsresult rv = mInitRunnable->Dispatch();
  873. if (NS_FAILED(rv)) {
  874. // Shutdown must be delayed until all Contexts are destroyed. Shutdown
  875. // must also prevent any new Contexts from being constructed. Crash
  876. // for this invariant violation.
  877. MOZ_CRASH("Failed to dispatch QuotaInitRunnable.");
  878. }
  879. }
  880. void
  881. Context::DispatchAction(Action* aAction, bool aDoomData)
  882. {
  883. NS_ASSERT_OWNINGTHREAD(Context);
  884. RefPtr<ActionRunnable> runnable =
  885. new ActionRunnable(this, mData, mTarget, aAction, mQuotaInfo);
  886. if (aDoomData) {
  887. mData = nullptr;
  888. }
  889. nsresult rv = runnable->Dispatch();
  890. if (NS_FAILED(rv)) {
  891. // Shutdown must be delayed until all Contexts are destroyed. Crash
  892. // for this invariant violation.
  893. MOZ_CRASH("Failed to dispatch ActionRunnable to target thread.");
  894. }
  895. AddActivity(runnable);
  896. }
  897. void
  898. Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
  899. already_AddRefed<DirectoryLock> aDirectoryLock)
  900. {
  901. NS_ASSERT_OWNINGTHREAD(Context);
  902. MOZ_DIAGNOSTIC_ASSERT(mInitRunnable);
  903. mInitRunnable = nullptr;
  904. mQuotaInfo = aQuotaInfo;
  905. // Always save the directory lock to ensure QuotaManager does not shutdown
  906. // before the Context has gone away.
  907. MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock);
  908. mDirectoryLock = aDirectoryLock;
  909. // If we opening the context failed, but we were not explicitly canceled,
  910. // still treat the entire context as canceled. We don't want to allow
  911. // new actions to be dispatched. We also cannot leave the context in
  912. // the INIT state after failing to open.
  913. if (NS_FAILED(aRv)) {
  914. mState = STATE_CONTEXT_CANCELED;
  915. }
  916. if (mState == STATE_CONTEXT_CANCELED) {
  917. for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
  918. mPendingActions[i].mAction->CompleteOnInitiatingThread(aRv);
  919. }
  920. mPendingActions.Clear();
  921. mThreadsafeHandle->AllowToClose();
  922. // Context will destruct after return here and last ref is released.
  923. return;
  924. }
  925. MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_INIT);
  926. mState = STATE_CONTEXT_READY;
  927. for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
  928. DispatchAction(mPendingActions[i].mAction);
  929. }
  930. mPendingActions.Clear();
  931. }
  932. void
  933. Context::AddActivity(Activity* aActivity)
  934. {
  935. NS_ASSERT_OWNINGTHREAD(Context);
  936. MOZ_DIAGNOSTIC_ASSERT(aActivity);
  937. MOZ_ASSERT(!mActivityList.Contains(aActivity));
  938. mActivityList.AppendElement(aActivity);
  939. }
  940. void
  941. Context::RemoveActivity(Activity* aActivity)
  942. {
  943. NS_ASSERT_OWNINGTHREAD(Context);
  944. MOZ_DIAGNOSTIC_ASSERT(aActivity);
  945. MOZ_ALWAYS_TRUE(mActivityList.RemoveElement(aActivity));
  946. MOZ_ASSERT(!mActivityList.Contains(aActivity));
  947. }
  948. void
  949. Context::NoteOrphanedData()
  950. {
  951. NS_ASSERT_OWNINGTHREAD(Context);
  952. // This may be called more than once
  953. mOrphanedData = true;
  954. }
  955. already_AddRefed<Context::ThreadsafeHandle>
  956. Context::CreateThreadsafeHandle()
  957. {
  958. NS_ASSERT_OWNINGTHREAD(Context);
  959. if (!mThreadsafeHandle) {
  960. mThreadsafeHandle = new ThreadsafeHandle(this);
  961. }
  962. RefPtr<ThreadsafeHandle> ref = mThreadsafeHandle;
  963. return ref.forget();
  964. }
  965. void
  966. Context::SetNextContext(Context* aNextContext)
  967. {
  968. NS_ASSERT_OWNINGTHREAD(Context);
  969. MOZ_DIAGNOSTIC_ASSERT(aNextContext);
  970. MOZ_DIAGNOSTIC_ASSERT(!mNextContext);
  971. mNextContext = aNextContext;
  972. }
  973. void
  974. Context::DoomTargetData()
  975. {
  976. NS_ASSERT_OWNINGTHREAD(Context);
  977. MOZ_DIAGNOSTIC_ASSERT(mData);
  978. // We are about to drop our reference to the Data. We need to ensure that
  979. // the ~Context() destructor does not run until contents of Data have been
  980. // released on the Target thread.
  981. // Dispatch a no-op Action. This will hold the Context alive through a
  982. // roundtrip to the target thread and back to the owning thread. The
  983. // ref to the Data object is cleared on the owning thread after creating
  984. // the ActionRunnable, but before dispatching it.
  985. RefPtr<Action> action = new NullAction();
  986. DispatchAction(action, true /* doomed data */);
  987. MOZ_DIAGNOSTIC_ASSERT(!mData);
  988. }
  989. } // namespace cache
  990. } // namespace dom
  991. } // namespace mozilla