Bug 458320 - 'Workers: Support synchronous events from within XHR methods'. r+sr=jst.
authorBen Turner <bent.mozilla@gmail.com>
Tue, 14 Oct 2008 11:16:37 -0700
changeset 20473 e3930c7ad1410f803cbdc923ae8999dfc0520889
parent 20472 4dba9cbb788df05448868578c3361dd343fef91b
child 20474 f0804610995bc53ce6f7dcee8657cd7b097e07fb
push id2894
push userbturner@mozilla.com
push dateTue, 14 Oct 2008 18:19:37 +0000
treeherdermozilla-central@a043e83d291f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs458320
milestone1.9.1b2pre
Bug 458320 - 'Workers: Support synchronous events from within XHR methods'. r+sr=jst.
dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h
dom/src/threads/nsDOMWorkerXHRProxy.cpp
dom/src/threads/nsDOMWorkerXHRProxy.h
--- a/dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h
+++ b/dom/src/threads/nsDOMWorkerXHRProxiedFunctions.h
@@ -35,200 +35,212 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __NSDOMWORKERXHRPROXIEDFUNCTIONS_H__
 #define __NSDOMWORKERXHRPROXIEDFUNCTIONS_H__
 
 #define MAKE_PROXIED_FUNCTION0(_name) \
-  class _name : public nsRunnable \
+  class _name : public SyncEventCapturingRunnable \
   { \
   public: \
-    _name (nsDOMWorkerXHRProxy* aXHR) \
-    : mXHR(aXHR) \
-    { \
-      NS_ASSERTION(aXHR, "Null pointer!"); \
-    } \
+    _name (nsDOMWorkerXHRProxy* aXHR, SyncEventQueue* aQueue) \
+    : SyncEventCapturingRunnable(aXHR, aQueue) { } \
   \
-    NS_IMETHOD Run() \
+    virtual nsresult RunInternal() \
     { \
       nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
       if (xhr) { \
         return xhr-> _name (); \
       } \
       return NS_OK; \
     } \
-  private: \
-    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
   }
 
 #define MAKE_PROXIED_FUNCTION1(_name, _arg1) \
-  class _name : public nsRunnable \
+  class _name : public SyncEventCapturingRunnable \
   { \
   public: \
-     _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1) \
-    : mXHR(aXHR), mArg1(aArg1) \
-    { \
-      NS_ASSERTION(aXHR, "Null pointer!"); \
-    } \
+     _name (nsDOMWorkerXHRProxy* aXHR, SyncEventQueue* aQueue, _arg1 aArg1) \
+    : SyncEventCapturingRunnable(aXHR, aQueue), mArg1(aArg1) { } \
   \
-    NS_IMETHOD Run() \
+    virtual nsresult RunInternal() \
     { \
       nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
       if (xhr) { \
         return xhr-> _name (mArg1); \
       } \
       return NS_OK; \
     } \
   private: \
-    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
      _arg1 mArg1; \
   }
 
 #define MAKE_PROXIED_FUNCTION2(_name, _arg1, _arg2) \
-  class _name : public nsRunnable \
+  class _name : public SyncEventCapturingRunnable \
   { \
   public: \
-    _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2) \
-    : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2) \
-    { \
-      NS_ASSERTION(aXHR, "Null pointer!"); \
-    } \
+    _name (nsDOMWorkerXHRProxy* aXHR, SyncEventQueue* aQueue, _arg1 aArg1, \
+           _arg2 aArg2) \
+    : SyncEventCapturingRunnable(aXHR, aQueue), mArg1(aArg1), mArg2(aArg2) { } \
   \
-    NS_IMETHOD Run() \
+    virtual nsresult RunInternal() \
     { \
       nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
       if (xhr) { \
         return xhr-> _name (mArg1, mArg2); \
       } \
       return NS_OK; \
     } \
   private: \
-    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
     _arg1 mArg1; \
     _arg2 mArg2; \
   }
 
 #define MAKE_PROXIED_FUNCTION3(_name, _arg1, _arg2, _arg3) \
-  class _name : public nsRunnable \
+  class _name : public SyncEventCapturingRunnable \
   { \
   public: \
-    _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2, _arg3 aArg3) \
-    : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2), mArg3(aArg3) \
-    { \
-      NS_ASSERTION(aXHR, "Null pointer!"); \
-    } \
+    _name (nsDOMWorkerXHRProxy* aXHR, SyncEventQueue* aQueue, _arg1 aArg1, \
+           _arg2 aArg2, _arg3 aArg3) \
+    : SyncEventCapturingRunnable(aXHR, aQueue), mArg1(aArg1), mArg2(aArg2), \
+      mArg3(aArg3) { } \
   \
-    NS_IMETHOD Run() \
+    virtual nsresult RunInternal() \
     { \
       nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
       if (xhr) { \
         return xhr-> _name (mArg1, mArg2, mArg3); \
       } \
       return NS_OK; \
     } \
   private: \
-    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
     _arg1 mArg1; \
     _arg2 mArg2; \
     _arg3 mArg3; \
   }
 
 #define MAKE_PROXIED_FUNCTION4(_name, _arg1, _arg2, _arg3, _arg4) \
-  class _name : public nsRunnable \
+  class _name : public SyncEventCapturingRunnable \
   { \
   public: \
-    _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2, _arg3 aArg3, \
-           _arg4 aArg4) \
-    : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2), mArg3(aArg3), mArg4(aArg4) \
-    { \
-      NS_ASSERTION(aXHR, "Null pointer!"); \
-    } \
+    _name (nsDOMWorkerXHRProxy* aXHR, SyncEventQueue* aQueue, _arg1 aArg1, \
+           _arg2 aArg2, _arg3 aArg3, _arg4 aArg4) \
+    : SyncEventCapturingRunnable(aXHR, aQueue), mArg1(aArg1), mArg2(aArg2), \
+      mArg3(aArg3), mArg4(aArg4) { } \
   \
-    NS_IMETHOD Run() \
+    virtual nsresult RunInternal() \
     { \
       nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
       if (xhr) { \
         return xhr-> _name (mArg1, mArg2, mArg3, mArg4); \
       } \
       return NS_OK; \
     } \
   private: \
-    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
     _arg1 mArg1; \
     _arg2 mArg2; \
     _arg3 mArg3; \
     _arg4 mArg4; \
   }
 
 #define MAKE_PROXIED_FUNCTION5(_name, _arg1, _arg2, _arg3, _arg4, _arg5) \
-  class _name : public nsRunnable \
+  class _name : public SyncEventCapturingRunnable \
   { \
   public: \
-    _name (nsDOMWorkerXHRProxy* aXHR, _arg1 aArg1, _arg2 aArg2, _arg3 aArg3, \
-           _arg4 aArg4, _arg5 aArg5) \
-    : mXHR(aXHR), mArg1(aArg1), mArg2(aArg2), mArg3(aArg3), mArg4(aArg4), \
-      mArg5(aArg5) \
-    { \
-      NS_ASSERTION(aXHR, "Null pointer!"); \
-    } \
+    _name (nsDOMWorkerXHRProxy* aXHR, SyncEventQueue* aQueue, _arg1 aArg1, \
+           _arg2 aArg2, _arg3 aArg3, _arg4 aArg4, _arg5 aArg5) \
+    : SyncEventCapturingRunnable(aXHR, aQueue), mArg1(aArg1), mArg2(aArg2), \
+      mArg3(aArg3), mArg4(aArg4), mArg5(aArg5) { } \
   \
-    NS_IMETHOD Run() \
+    virtual nsresult RunInternal() \
     { \
       nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR->GetXMLHttpRequest(); \
       if (xhr) { \
         return xhr-> _name (mArg1, mArg2, mArg3, mArg4, mArg5); \
       } \
       return NS_OK; \
     } \
   private: \
-    nsRefPtr<nsDOMWorkerXHRProxy> mXHR; \
     _arg1 mArg1; \
     _arg2 mArg2; \
     _arg3 mArg3; \
     _arg4 mArg4; \
     _arg5 mArg5; \
   }
 
 #define RUN_PROXIED_FUNCTION(_name, _args) \
   PR_BEGIN_MACRO \
+    NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); \
+    \
     if (mCanceled) { \
       return NS_ERROR_ABORT; \
     } \
+    SyncEventQueue queue; \
     \
     nsCOMPtr<nsIRunnable> method = new :: _name _args; \
     NS_ENSURE_TRUE(method, NS_ERROR_OUT_OF_MEMORY); \
     \
     nsRefPtr<nsResultReturningRunnable> runnable = \
       new nsResultReturningRunnable(mMainThread, method, mWorkerXHR->mWorker); \
     NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); \
     \
     nsresult _rv = runnable->Dispatch(); \
+    \
+    PRUint32 queueLength = queue.Length(); \
+    for (PRUint32 index = 0; index < queueLength; index++) { \
+      queue[index]->Run(); \
+    } \
+    \
     if (NS_FAILED(_rv)) { \
       return _rv; \
     } \
   PR_END_MACRO
 
 namespace nsDOMWorkerProxiedXHRFunctions
 {
-  class Abort : public nsRunnable
+  typedef nsDOMWorkerXHRProxy::SyncEventQueue SyncEventQueue;
+
+  class SyncEventCapturingRunnable : public nsRunnable
   {
   public:
-    Abort (nsDOMWorkerXHRProxy* aXHR)
-    : mXHR(aXHR)
-    {
+    SyncEventCapturingRunnable(nsDOMWorkerXHRProxy* aXHR,
+                               SyncEventQueue* aQueue)
+    : mXHR(aXHR), mQueue(aQueue) {
       NS_ASSERTION(aXHR, "Null pointer!");
+      NS_ASSERTION(aQueue, "Null pointer!");
     }
 
+    virtual nsresult RunInternal() = 0;
+
     NS_IMETHOD Run() {
+      SyncEventQueue* oldQueue = mXHR->SetSyncEventQueue(mQueue);
+
+      nsresult rv = RunInternal();
+
+      mXHR->SetSyncEventQueue(oldQueue);
+
+      return rv;
+    }
+
+  protected:
+    nsRefPtr<nsDOMWorkerXHRProxy> mXHR;
+    SyncEventQueue* mQueue;
+  };
+
+  class Abort : public SyncEventCapturingRunnable
+  {
+  public:
+    Abort (nsDOMWorkerXHRProxy* aXHR, SyncEventQueue* aQueue)
+    : SyncEventCapturingRunnable(aXHR, aQueue) { }
+
+    virtual nsresult RunInternal() {
       return mXHR->Abort();
     }
-  private:
-    nsRefPtr<nsDOMWorkerXHRProxy> mXHR;
   };
 
   MAKE_PROXIED_FUNCTION1(GetAllResponseHeaders, char**);
 
   MAKE_PROXIED_FUNCTION2(GetResponseHeader, const nsACString&, nsACString&);
 
   MAKE_PROXIED_FUNCTION5(OpenRequest, const nsACString&, const nsACString&,
                          PRBool, const nsAString&, const nsAString&);
--- a/dom/src/threads/nsDOMWorkerXHRProxy.cpp
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.cpp
@@ -437,16 +437,17 @@ private:
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerXHRWrappedListener,
                               nsIDOMEventListener)
 
 nsDOMWorkerXHRProxy::nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR)
 : mWorkerXHR(aWorkerXHR),
   mXHR(nsnull),
   mConcreteXHR(nsnull),
   mUpload(nsnull),
+  mSyncEventQueue(nsnull),
   mOwnedByXHR(PR_FALSE),
   mMultipart(PR_FALSE),
   mCanceled(PR_FALSE)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(MAX_XHR_LISTENER_TYPE >= MAX_UPLOAD_LISTENER_TYPE,
                "Upload should support a subset of XHR's event types!");
 }
@@ -905,18 +906,28 @@ nsDOMWorkerXHRProxy::HandleEvent(nsIDOME
   NS_ENSURE_ARG_POINTER(aEvent);
 
   nsRefPtr<nsDOMWorkerXHREvent> newEvent = new nsDOMWorkerXHREvent(this);
   NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
 
   nsresult rv = newEvent->Init(aEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = nsDOMThreadService::get()->Dispatch(mWorkerXHR->mWorker, newEvent);
-  NS_ENSURE_SUCCESS(rv, rv);
+  // If we're supposed to be capturing events for synchronous execution then
+  // place this event in the queue. Otherwise schedule it for the worker via
+  // the thread service.
+  if (mSyncEventQueue) {
+    nsCOMPtr<nsIRunnable>* newElement =
+      mSyncEventQueue->AppendElement(newEvent);
+    NS_ENSURE_TRUE(newElement, NS_ERROR_OUT_OF_MEMORY);
+  }
+  else {
+    rv = nsDOMThreadService::get()->Dispatch(mWorkerXHR->mWorker, newEvent);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::Abort()
 {
   if (NS_IsMainThread()) {
@@ -927,73 +938,82 @@ nsDOMWorkerXHRProxy::Abort()
     nsCOMPtr<nsIXMLHttpRequest> xhr = mXHR;
     if (mOwnedByXHR) {
       FlipOwnership();
     }
 
     return xhr->Abort();
   }
 
-  RUN_PROXIED_FUNCTION(Abort, (this));
+  RUN_PROXIED_FUNCTION(Abort, (this, &queue));
   return NS_OK;
 }
 
+nsDOMWorkerXHRProxy::SyncEventQueue*
+nsDOMWorkerXHRProxy::SetSyncEventQueue(SyncEventQueue* aQueue)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  SyncEventQueue* oldQueue = mSyncEventQueue;
+  mSyncEventQueue = aQueue;
+  return oldQueue;
+}
+
 nsresult
 nsDOMWorkerXHRProxy::GetAllResponseHeaders(char** _retval)
 {
-  RUN_PROXIED_FUNCTION(GetAllResponseHeaders, (this, _retval));
+  RUN_PROXIED_FUNCTION(GetAllResponseHeaders, (this, &queue, _retval));
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::GetResponseHeader(const nsACString& aHeader,
                                        nsACString& _retval)
 {
-  RUN_PROXIED_FUNCTION(GetResponseHeader, (this, aHeader, _retval));
+  RUN_PROXIED_FUNCTION(GetResponseHeader, (this, &queue, aHeader, _retval));
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::OpenRequest(const nsACString& aMethod,
                                  const nsACString& aUrl,
                                  PRBool aAsync,
                                  const nsAString& aUser,
                                  const nsAString& aPassword)
 {
-  RUN_PROXIED_FUNCTION(OpenRequest, (this, aMethod, aUrl, aAsync, aUser,
+  RUN_PROXIED_FUNCTION(OpenRequest, (this, &queue, aMethod, aUrl, aAsync, aUser,
                                      aPassword));
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::Send(nsIVariant* aBody)
 {
-  RUN_PROXIED_FUNCTION(Send, (this, aBody));
+  RUN_PROXIED_FUNCTION(Send, (this, &queue, aBody));
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::SendAsBinary(const nsAString& aBody)
 {
-  RUN_PROXIED_FUNCTION(SendAsBinary, (this, aBody));
+  RUN_PROXIED_FUNCTION(SendAsBinary, (this, &queue, aBody));
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::SetRequestHeader(const nsACString& aHeader,
                                       const nsACString& aValue)
 {
-  RUN_PROXIED_FUNCTION(SetRequestHeader, (this, aHeader, aValue));
+  RUN_PROXIED_FUNCTION(SetRequestHeader, (this, &queue, aHeader, aValue));
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::OverrideMimeType(const nsACString& aMimetype)
 {
-  RUN_PROXIED_FUNCTION(OverrideMimeType, (this, aMimetype));
+  RUN_PROXIED_FUNCTION(OverrideMimeType, (this, &queue, aMimetype));
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::GetMultipart(PRBool* aMultipart)
 {
   if (mCanceled) {
     return NS_ERROR_ABORT;
@@ -1001,17 +1021,17 @@ nsDOMWorkerXHRProxy::GetMultipart(PRBool
 
   *aMultipart = mMultipart;
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::SetMultipart(PRBool aMultipart)
 {
-  RUN_PROXIED_FUNCTION(SetMultipart, (this, aMultipart));
+  RUN_PROXIED_FUNCTION(SetMultipart, (this, &queue, aMultipart));
   mMultipart = aMultipart;
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::GetResponseText(nsAString& _retval)
 {
   if (mCanceled) {
--- a/dom/src/threads/nsDOMWorkerXHRProxy.h
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.h
@@ -73,30 +73,34 @@ class nsDOMWorkerXHRProxy : public nsRun
   typedef nsTArray<Listener> ListenerArray;
 
   typedef nsRefPtr<nsDOMWorkerXHRWrappedListener> WrappedListener;
 
   typedef nsresult (NS_STDCALL nsIDOMEventTarget::*EventListenerFunction)
     (const nsAString&, nsIDOMEventListener*, PRBool);
 
 public:
+  typedef nsAutoTArray<nsCOMPtr<nsIRunnable>, 5> SyncEventQueue;
+
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMEVENTLISTENER
   NS_DECL_NSIRUNNABLE
   NS_DECL_NSIREQUESTOBSERVER
 
   nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR);
   virtual ~nsDOMWorkerXHRProxy();
 
   nsresult Init();
 
   nsIXMLHttpRequest* GetXMLHttpRequest();
 
   nsresult Abort();
 
+  SyncEventQueue* SetSyncEventQueue(SyncEventQueue* aQueue);
+
 protected:
   nsresult InitInternal();
   void DestroyInternal();
 
   nsresult Destroy();
 
   void FlipOwnership();
 
@@ -158,16 +162,18 @@ protected:
   nsRefPtr<nsDOMWorkerXHREvent> mLastXHREvent;
 
   nsTArray<ListenerArray> mXHRListeners;
   nsTArray<WrappedListener> mXHROnXListeners;
 
   nsTArray<ListenerArray> mUploadListeners;
   nsTArray<WrappedListener> mUploadOnXListeners;
 
+  SyncEventQueue* mSyncEventQueue;
+
   // Whether or not this object is owned by the real XHR object.
   PRPackedBool mOwnedByXHR;
 
   PRPackedBool mMultipart;
   PRPackedBool mCanceled;
 };
 
 #endif /* __NSDOMWORKERXHRPROXY_H__ */