Bug 1319850 - part 4, Code to initialize and support AndroidUI MessageLoop and nsIThread r=nfroyd
☠☠ backed out by b1213723e156 ☠ ☠
authorRandall Barker <rbarker@mozilla.com>
Wed, 30 Nov 2016 14:59:57 -0800
changeset 355118 15b92bb6d8107bbc60c1932e340b4a954ed8a8d2
parent 355117 e31107c3f677659805753097d4273e472975dfbb
child 355119 c25bce77b775a551e55f80faaa9b721f6bdbbac9
push id10621
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 16:02:43 +0000
treeherdermozilla-aurora@dca7b42e6c67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs1319850
milestone53.0a1
Bug 1319850 - part 4, Code to initialize and support AndroidUI MessageLoop and nsIThread r=nfroyd
widget/android/AndroidUiThread.cpp
widget/android/AndroidUiThread.h
widget/android/moz.build
widget/android/nsAppShell.cpp
widget/android/nsWindow.cpp
new file mode 100644
--- /dev/null
+++ b/widget/android/AndroidUiThread.cpp
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AndroidBridge.h"
+#include "base/message_loop.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/StaticPtr.h"
+#include "nsThread.h"
+#include "nsThreadManager.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla;
+
+namespace {
+
+class AndroidUiThread;
+
+StaticRefPtr<AndroidUiThread> sThread;
+static MessageLoop* sMessageLoop;
+
+/*
+ * The AndroidUiThread is derived from nsThread so that nsIRunnable objects that get
+ * dispatched may be intercepted. Only nsIRunnable objects that need to be synchronously
+ * executed are passed into the nsThread to be queued. All other nsIRunnable object
+ * are immediately dispatched to the Android UI thread via the AndroidBridge.
+ * AndroidUiThread is derived from nsThread instead of being an nsIEventTarget
+ * wrapper that contains an nsThread object because if nsIRunnable objects with a
+ * delay were dispatch directly to an nsThread object, such as obtained from
+ * nsThreadManager::GetCurrentThread(), the nsIRunnable could get stuck in the
+ * nsThread nsIRunnable queue. This is due to the fact that Android controls the
+ * event loop in the Android UI thread and has no knowledge of when the nsThread
+ * needs to be drained.
+*/
+
+class AndroidUiThread : public nsThread
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  AndroidUiThread() : nsThread(nsThread::NOT_MAIN_THREAD, 0)
+  {}
+
+  nsresult Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags) override;
+  nsresult DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelayMs) override;
+
+private:
+  ~AndroidUiThread()
+  {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(AndroidUiThread, nsThread)
+
+NS_IMETHODIMP
+AndroidUiThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
+{
+  if (aFlags & NS_DISPATCH_SYNC) {
+    return nsThread::Dispatch(Move(aEvent), aFlags);
+  } else {
+    AndroidBridge::Bridge()->PostTaskToUiThread(Move(aEvent), 0);
+    return NS_OK;
+  }
+}
+
+NS_IMETHODIMP
+AndroidUiThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelayMs)
+{
+  AndroidBridge::Bridge()->PostTaskToUiThread(Move(aEvent), aDelayMs);
+  return NS_OK;
+}
+
+static void
+PumpEvents() {
+  NS_ProcessPendingEvents(sThread.get());
+}
+
+class ThreadObserver : public nsIThreadObserver
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSITHREADOBSERVER
+
+  ThreadObserver()
+  {}
+
+private:
+  virtual ~ThreadObserver()
+  {}
+};
+
+NS_IMPL_ISUPPORTS(ThreadObserver, nsIThreadObserver)
+
+NS_IMETHODIMP
+ThreadObserver::OnDispatchedEvent(nsIThreadInternal *thread)
+{
+  AndroidBridge::Bridge()->PostTaskToUiThread(NS_NewRunnableFunction(&PumpEvents), 0);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ThreadObserver::OnProcessNextEvent(nsIThreadInternal *thread, bool mayWait)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ThreadObserver::AfterProcessNextEvent(nsIThreadInternal *thread, bool eventWasProcessed)
+{
+  return NS_OK;
+}
+
+class CreateOnUiThread : public Runnable {
+public:
+  CreateOnUiThread() : mCreated(false), mThreadCreationMonitor("AndroidUiThreadCreationLock")
+  {}
+
+  NS_IMETHOD Run() override {
+    MonitorAutoLock lock(mThreadCreationMonitor);
+
+    sThread = new AndroidUiThread();
+    sThread->InitCurrentThread();
+    sThread->SetObserver(new ThreadObserver());
+    sMessageLoop = new MessageLoop(MessageLoop::TYPE_MOZILLA_ANDROID_UI, sThread.get());
+    mCreated = true;
+    lock.NotifyAll();
+    return NS_OK;
+  }
+
+  void WaitForCreation()
+  {
+    MonitorAutoLock lock(mThreadCreationMonitor);
+    while (!mCreated) {
+      lock.Wait();
+    }
+  }
+
+private:
+  bool mCreated;
+  Monitor mThreadCreationMonitor;
+};
+
+class DestroyOnUiThread : public Runnable {
+public:
+  DestroyOnUiThread() : mDestroyed(false), mThreadDestructionMonitor("AndroidUiThreadCreationLock")
+  {}
+
+  NS_IMETHOD Run() override {
+    MonitorAutoLock lock(mThreadDestructionMonitor);
+
+    delete sMessageLoop;
+    sMessageLoop = nullptr;
+    MOZ_ASSERT(sThread);
+    nsThreadManager::get().UnregisterCurrentThread(*sThread);
+    sThread = nullptr;
+    mDestroyed = true;
+    lock.NotifyAll();
+    return NS_OK;
+  }
+
+  void WaitForDestruction()
+  {
+    MonitorAutoLock lock(mThreadDestructionMonitor);
+    while (!mDestroyed) {
+      lock.Wait();
+    }
+  }
+
+private:
+  bool mDestroyed;
+  Monitor mThreadDestructionMonitor;
+};
+
+} // namespace
+
+namespace mozilla {
+
+void
+CreateAndroidUiThread()
+{
+  MOZ_ASSERT(!sThread);
+  RefPtr<CreateOnUiThread> runnable = new CreateOnUiThread;
+  AndroidBridge::Bridge()->PostTaskToUiThread(do_AddRef(runnable), 0);
+  runnable->WaitForCreation();
+}
+
+void
+DestroyAndroidUiThread()
+{
+  MOZ_ASSERT(sThread);
+  RefPtr<DestroyOnUiThread> runnable = new DestroyOnUiThread;
+  // Insure the Android bridge has not already been deconstructed.
+  MOZ_ASSERT(AndroidBridge::Bridge() != nullptr);
+  AndroidBridge::Bridge()->PostTaskToUiThread(do_AddRef(runnable), 0);
+  runnable->WaitForDestruction();
+}
+
+MessageLoop*
+GetAndroidUiThreadMessageLoop()
+{
+  return sMessageLoop;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/android/AndroidUiThread.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef AndroidUiThread_h__
+#define AndroidUiThread_h__
+
+class MessageLoop;
+
+namespace mozilla {
+
+void CreateAndroidUiThread();
+void DestroyAndroidUiThread();
+
+MessageLoop* GetAndroidUiThreadMessageLoop();
+
+} // namespace mozilla
+
+#endif // AndroidUiThread_h__
--- a/widget/android/moz.build
+++ b/widget/android/moz.build
@@ -31,16 +31,17 @@ EXPORTS.mozilla.widget += [
 UNIFIED_SOURCES += [
     'AndroidAlerts.cpp',
     'AndroidBridge.cpp',
     'AndroidCompositorWidget.cpp',
     'AndroidContentController.cpp',
     'AndroidJavaWrappers.cpp',
     'AndroidJNI.cpp',
     'AndroidJNIWrapper.cpp',
+    'AndroidUiThread.cpp',
     'ANRReporter.cpp',
     'EventDispatcher.cpp',
     'GeneratedJNIWrappers.cpp',
     'GfxInfo.cpp',
     'NativeJSContainer.cpp',
     'nsAndroidProtocolHandler.cpp',
     'nsAppShell.cpp',
     'nsClipboard.cpp',
@@ -59,16 +60,17 @@ FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/docshell/base',
     '/dom/base',
     '/dom/system/android',
     '/netwerk/base',
     '/netwerk/cache',
     '/widget',
+    '/xpcom/threads',
 ]
 
 CXXFLAGS += ['-Wno-error=shadow']
 
 OS_LIBS += ['android']
 
 #DEFINES['DEBUG_WIDGETS'] = True
 
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -56,16 +56,17 @@
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsICrashReporter.h"
 #include "nsExceptionHandler.h"
 #endif
 
 #include "AndroidAlerts.h"
+#include "AndroidUiThread.h"
 #include "ANRReporter.h"
 #include "GeckoBatteryManager.h"
 #include "GeckoNetworkManager.h"
 #include "GeckoScreenOrientation.h"
 #include "PrefsHelper.h"
 #include "fennec/MemoryMonitor.h"
 #include "fennec/Telemetry.h"
 #include "fennec/ThumbnailHelper.h"
@@ -390,29 +391,33 @@ nsAppShell::nsAppShell()
         if (jni::IsFennec()) {
             mozilla::ANRReporter::Init();
             mozilla::MemoryMonitor::Init();
             mozilla::widget::Telemetry::Init();
             mozilla::ThumbnailHelper::Init();
         }
 
         java::GeckoThread::SetState(java::GeckoThread::State::JNI_READY());
+
+        CreateAndroidUiThread();
     }
 
     sPowerManagerService = do_GetService(POWERMANAGERSERVICE_CONTRACTID);
 
     if (sPowerManagerService) {
         sWakeLockListener = new WakeLockListener();
     } else {
         NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
     }
 }
 
 nsAppShell::~nsAppShell()
 {
+    DestroyAndroidUiThread();
+
     {
         MutexAutoLock lock(*sAppShellLock);
         sAppShell = nullptr;
     }
 
     while (mEventQueue.Pop(/* mayWait */ false)) {
         NS_WARNING("Discarded event on shutdown");
     }
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -68,16 +68,17 @@ using mozilla::Unused;
 #include "ScopedGLHelpers.h"
 #include "mozilla/layers/CompositorOGL.h"
 #include "AndroidContentController.h"
 
 #include "nsTArray.h"
 
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
+#include "AndroidUiThread.h"
 #include "android_npapi.h"
 #include "FennecJNINatives.h"
 #include "GeneratedJNINatives.h"
 #include "KeyEvent.h"
 #include "MotionEvent.h"
 
 #include "imgIEncoder.h"
 
@@ -3555,17 +3556,17 @@ nsWindow::NeedsPaint()
         return false;
     }
     return nsIWidget::NeedsPaint();
 }
 
 void
 nsWindow::ConfigureAPZControllerThread()
 {
-    APZThreadUtils::SetControllerThread(nullptr);
+    APZThreadUtils::SetControllerThread(mozilla::GetAndroidUiThreadMessageLoop());
 }
 
 already_AddRefed<GeckoContentController>
 nsWindow::CreateRootContentController()
 {
     RefPtr<GeckoContentController> controller = new AndroidContentController(this, mAPZEventState, mAPZC);
     return controller.forget();
 }