CacheStreamControlChild.cpp (5161B)
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 #include "CacheStreamControlChild.h" 8 9 #include "mozilla/dom/cache/ActorUtils.h" 10 #include "mozilla/dom/cache/CacheTypes.h" 11 #include "mozilla/dom/cache/CacheWorkerRef.h" 12 #include "mozilla/ipc/IPCStreamUtils.h" 13 #include "mozilla/ipc/PBackgroundChild.h" 14 #include "nsISupportsImpl.h" 15 16 namespace mozilla::dom::cache { 17 18 using mozilla::ipc::FileDescriptor; 19 20 // declared in ActorUtils.h 21 already_AddRefed<PCacheStreamControlChild> AllocPCacheStreamControlChild( 22 ActorChild* aParentActor) { 23 return MakeAndAddRef<CacheStreamControlChild>(aParentActor); 24 } 25 26 CacheStreamControlChild::CacheStreamControlChild(ActorChild* aParentActor) 27 : mParentActor(aParentActor), 28 mDestroyStarted(false), 29 mDestroyDelayed(false) { 30 MOZ_COUNT_CTOR(cache::CacheStreamControlChild); 31 } 32 33 CacheStreamControlChild::~CacheStreamControlChild() { 34 NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild); 35 MOZ_COUNT_DTOR(cache::CacheStreamControlChild); 36 } 37 38 void CacheStreamControlChild::StartDestroy() { 39 NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild); 40 // This can get called twice under some circumstances. For example, if the 41 // actor is added to a CacheWorkerRef that has already been notified and 42 // the Cache actor has no mListener. 43 if (mDestroyStarted) { 44 return; 45 } 46 mDestroyStarted = true; 47 48 // If any of the streams have started to be read, then wait for them to close 49 // naturally. 50 if (HasEverBeenRead()) { 51 // Note that we are delaying so that we can re-check for active streams 52 // in NoteClosedAfterForget(). 53 mDestroyDelayed = true; 54 return; 55 } 56 57 // Otherwise, if the streams have not been touched then just pre-emptively 58 // close them now. This handles the case where someone retrieves a Response 59 // from the Cache, but never accesses the body. We should not keep the 60 // Worker alive until that Response is GC'd just because of its ignored 61 // body stream. 62 63 // Begin shutting down all streams. This is the same as if the parent had 64 // asked us to shutdown. So simulate the CloseAll IPC message. 65 RecvCloseAll(); 66 } 67 68 void CacheStreamControlChild::SerializeControl( 69 CacheReadStream* aReadStreamOut) { 70 NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild); 71 MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut); 72 aReadStreamOut->control() = this; 73 } 74 75 void CacheStreamControlChild::SerializeStream(CacheReadStream* aReadStreamOut, 76 nsIInputStream* aStream) { 77 NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild); 78 MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut); 79 MOZ_ALWAYS_TRUE(mozilla::ipc::SerializeIPCStream( 80 do_AddRef(aStream), aReadStreamOut->stream(), /* aAllowLazy */ false)); 81 } 82 83 void CacheStreamControlChild::OpenStream(const nsID& aId, 84 InputStreamResolver&& aResolver) { 85 NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild); 86 87 if (mDestroyStarted) { 88 aResolver(nullptr); 89 return; 90 } 91 92 // If we are on a worker, then we need to hold it alive until the async 93 // IPC operation below completes. While the IPC layer will trigger a 94 // rejection here in many cases, we must handle the case where the 95 // MozPromise resolve runnable is already in the event queue when the 96 // worker wants to shut down. 97 const SafeRefPtr<CacheWorkerRef> holder = GetWorkerRefPtr().clonePtr(); 98 99 SendOpenStream(aId)->Then( 100 GetCurrentSerialEventTarget(), __func__, 101 [aResolver, 102 holder = holder.clonePtr()](const Maybe<IPCStream>& aOptionalStream) { 103 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aOptionalStream); 104 aResolver(std::move(stream)); 105 }, 106 [aResolver, holder = holder.clonePtr()](ResponseRejectReason&& aReason) { 107 aResolver(nullptr); 108 }); 109 } 110 111 void CacheStreamControlChild::NoteClosedAfterForget(const nsID& aId) { 112 NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild); 113 114 QM_WARNONLY_TRY(OkIf(SendNoteClosed(aId))); 115 116 // A stream has closed. If we delayed StartDestry() due to this stream 117 // being read, then we should check to see if any of the remaining streams 118 // are active. If none of our other streams have been read, then we can 119 // proceed with the shutdown now. 120 if (mDestroyDelayed && !HasEverBeenRead()) { 121 mDestroyDelayed = false; 122 RecvCloseAll(); 123 } 124 } 125 126 #ifdef DEBUG 127 void CacheStreamControlChild::AssertOwningThread() { 128 NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild); 129 } 130 #endif 131 132 void CacheStreamControlChild::ActorDestroy(ActorDestroyReason aReason) { 133 NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild); 134 CloseAllReadStreamsWithoutReporting(); 135 136 if (mParentActor) { 137 mParentActor->NoteDeletedActor(); 138 } 139 140 RemoveWorkerRef(); 141 } 142 143 mozilla::ipc::IPCResult CacheStreamControlChild::RecvCloseAll() { 144 NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild); 145 CloseAllReadStreams(); 146 return IPC_OK(); 147 } 148 149 } // namespace mozilla::dom::cache