Bug 1405286: Part 1 - Allow retrieving the delivery target from retargetable requests. r?dragana
After data delivery for a request has been retargeted, there's no reliable way
to get the appropriate event target to re-dispatch data events after
asynchronous processing.
While it's technically possible to retrieve the current thread from
OnDataAvailable callbacks and re-use that for later dispatch, that approach
has some issues:
1) It's not currently possible to reliably map the current thread to the
thread pool that owns it. That means that if data delivery is being targetted
to a thread pool, attempts to redispatch events to the previous delivery
thread might lead to long delays when one thread in a pool is blocked.
2) If a filter wishes to dispatch data events to the wrapped listeners before
it's recieved any data (as extensions StreamFilters sometimes do), there's no
way to determine the proper event target without waiting for initial data to
be received.
Simply returning the correct event target from the request solves both of
these problems.
MozReview-Commit-ID: CJxq7O4399R
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -1170,16 +1170,29 @@ nsJARChannel::RetargetDeliveryTo(nsIEven
if (!request) {
return NS_ERROR_NO_INTERFACE;
}
return request->RetargetDeliveryTo(aEventTarget);
}
NS_IMETHODIMP
+nsJARChannel::GetDeliveryTarget(nsIEventTarget** aEventTarget)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest);
+ if (!request) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ return request->GetDeliveryTarget(aEventTarget);
+}
+
+NS_IMETHODIMP
nsJARChannel::CheckListenerChain()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
do_QueryInterface(mListener);
if (!listener) {
return NS_ERROR_NO_INTERFACE;
--- a/netwerk/base/nsBaseChannel.cpp
+++ b/netwerk/base/nsBaseChannel.cpp
@@ -970,16 +970,30 @@ nsBaseChannel::RetargetDeliveryTo(nsIEve
}
NS_ENSURE_TRUE(req, NS_ERROR_NOT_IMPLEMENTED);
return req->RetargetDeliveryTo(aEventTarget);
}
NS_IMETHODIMP
+nsBaseChannel::GetDeliveryTarget(nsIEventTarget** aEventTarget)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ NS_ENSURE_TRUE(mRequest, NS_ERROR_NOT_INITIALIZED);
+
+ nsCOMPtr<nsIThreadRetargetableRequest> req;
+ req = do_QueryInterface(mRequest);
+
+ NS_ENSURE_TRUE(req, NS_ERROR_NOT_IMPLEMENTED);
+ return req->GetDeliveryTarget(aEventTarget);
+}
+
+NS_IMETHODIMP
nsBaseChannel::CheckListenerChain()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mAllowThreadRetargeting) {
return NS_ERROR_NOT_IMPLEMENTED;
}
--- a/netwerk/base/nsIThreadRetargetableRequest.idl
+++ b/netwerk/base/nsIThreadRetargetableRequest.idl
@@ -27,9 +27,11 @@ interface nsIThreadRetargetableRequest :
* @param aNewTarget New event target, e.g. thread or threadpool.
*
* Note: no return value is given. If the retargeting cannot be handled,
* normal delivery to the main thread will continue. As such, listeners
* should be ready to deal with OnDataAvailable on either the main thread or
* the new target thread.
*/
void retargetDeliveryTo(in nsIEventTarget aNewTarget);
+
+ readonly attribute nsIEventTarget deliveryTarget;
};
--- a/netwerk/base/nsInputStreamPump.cpp
+++ b/netwerk/base/nsInputStreamPump.cpp
@@ -770,8 +770,18 @@ nsInputStreamPump::RetargetDeliveryTo(ns
}
}
LOG(("nsInputStreamPump::RetargetDeliveryTo [this=%p aNewTarget=%p] "
"%s listener [%p] rv[%" PRIx32 "]",
this, aNewTarget, (mTargetThread == aNewTarget ? "success" : "failure"),
(nsIStreamListener*)mListener, static_cast<uint32_t>(rv)));
return rv;
}
+
+NS_IMETHODIMP
+nsInputStreamPump::GetDeliveryTarget(nsIEventTarget** aNewTarget)
+{
+ RecursiveMutexAutoLock lock(mMutex);
+
+ nsCOMPtr<nsIEventTarget> target = mTargetThread;
+ target.forget(aNewTarget);
+ return NS_OK;
+}
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -3381,16 +3381,29 @@ HttpChannelChild::RetargetDeliveryTo(nsI
MutexAutoLock lock(mEventTargetMutex);
mODATarget = aNewTarget;
}
mOMTResult = LABELS_HTTP_CHILD_OMT_STATS::success;
return NS_OK;
}
+NS_IMETHODIMP
+HttpChannelChild::GetDeliveryTarget(nsIEventTarget** aEventTarget)
+{
+ MutexAutoLock lock(mEventTargetMutex);
+
+ nsCOMPtr<nsIEventTarget> target = mODATarget;
+ if (!mODATarget) {
+ target = GetCurrentThreadEventTarget();
+ }
+ target.forget(aEventTarget);
+ return NS_OK;
+}
+
void
HttpChannelChild::ResetInterception()
{
NS_ENSURE_TRUE_VOID(gNeckoChild != nullptr);
if (mInterceptListener) {
mInterceptListener->Cleanup();
}
--- a/netwerk/protocol/http/InterceptedHttpChannel.cpp
+++ b/netwerk/protocol/http/InterceptedHttpChannel.cpp
@@ -1074,16 +1074,25 @@ InterceptedHttpChannel::RetargetDelivery
if (!mPump) {
return NS_ERROR_NOT_AVAILABLE;
}
return mPump->RetargetDeliveryTo(aNewTarget);
}
NS_IMETHODIMP
+InterceptedHttpChannel::GetDeliveryTarget(nsIEventTarget** aEventTarget)
+{
+ if (!mPump) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ return mPump->GetDeliveryTarget(aEventTarget);
+}
+
+NS_IMETHODIMP
InterceptedHttpChannel::CheckListenerChain()
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = NS_OK;
nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
do_QueryInterface(mListener, &rv);
if (retargetableListener) {
rv = retargetableListener->CheckListenerChain();
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -7745,16 +7745,29 @@ nsHttpChannel::RetargetDeliveryTo(nsIEve
nsCOMPtr<nsIEventTarget> main = GetMainThreadEventTarget();
NS_ENSURE_TRUE(main, NS_ERROR_UNEXPECTED);
rv = retargetableCachePump->RetargetDeliveryTo(main);
}
}
return rv;
}
+
+NS_IMETHODIMP
+nsHttpChannel::GetDeliveryTarget(nsIEventTarget** aEventTarget)
+{
+ if (mCachePump) {
+ return mCachePump->GetDeliveryTarget(aEventTarget);
+ }
+ if (mTransactionPump) {
+ return mTransactionPump->GetDeliveryTarget(aEventTarget);
+ }
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
//-----------------------------------------------------------------------------
// nsHttpChannel::nsThreadRetargetableStreamListener
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::CheckListenerChain()
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!");
--- a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
@@ -354,16 +354,29 @@ BaseWebSocketChannel::RetargetDeliveryTo
MOZ_ASSERT(!mTargetThread, "Delivery target should be set once, before AsyncOpen");
MOZ_ASSERT(!mWasOpened, "Should not be called after AsyncOpen!");
mTargetThread = do_QueryInterface(aTargetThread);
MOZ_ASSERT(mTargetThread);
return NS_OK;
}
+NS_IMETHODIMP
+BaseWebSocketChannel::GetDeliveryTarget(nsIEventTarget** aTargetThread)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIEventTarget> target = mTargetThread;
+ if (!target) {
+ target = GetCurrentThreadEventTarget();
+ }
+ target.forget(aTargetThread);
+ return NS_OK;
+}
+
BaseWebSocketChannel::ListenerAndContextContainer::ListenerAndContextContainer(
nsIWebSocketListener* aListener,
nsISupports* aContext)
: mListener(aListener)
, mContext(aContext)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mListener);