Add a static boolean sThreadDestroyed which can be accessed only on JAVA UI thread. Set sThreadDestroyed to true at DestroyOnUiThread that will stop remain tasks to access the Bridge() instance at JAVA thread. MozReview-Commit-ID: 5JtUFgc6Vl3
235 lines
5.8 KiB
C++
235 lines
5.8 KiB
C++
/* -*- 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/Atomics.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 bool sThreadDestroyed;
|
|
static MessageLoop* sMessageLoop;
|
|
static Atomic<Monitor*> sMessageLoopAccessMonitor;
|
|
|
|
/*
|
|
* 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;
|
|
|
|
static int64_t RunDelayedTasksIfValid() {
|
|
if (!AndroidBridge::Bridge() ||
|
|
sThreadDestroyed) {
|
|
return -1;
|
|
}
|
|
return AndroidBridge::Bridge()->RunDelayedUiThreadTasks();
|
|
}
|
|
|
|
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()
|
|
{}
|
|
|
|
NS_IMETHOD Run() override {
|
|
MOZ_ASSERT(!sThreadDestroyed);
|
|
MOZ_ASSERT(sMessageLoopAccessMonitor);
|
|
MonitorAutoLock lock(*sMessageLoopAccessMonitor);
|
|
sThread = new AndroidUiThread();
|
|
sThread->InitCurrentThread();
|
|
sThread->SetObserver(new ThreadObserver());
|
|
sMessageLoop = new MessageLoop(MessageLoop::TYPE_MOZILLA_ANDROID_UI, sThread.get());
|
|
lock.NotifyAll();
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
class DestroyOnUiThread : public Runnable {
|
|
public:
|
|
DestroyOnUiThread() : mDestroyed(false)
|
|
{}
|
|
|
|
NS_IMETHOD Run() override {
|
|
MOZ_ASSERT(!sThreadDestroyed);
|
|
MOZ_ASSERT(sMessageLoopAccessMonitor);
|
|
MonitorAutoLock lock(*sMessageLoopAccessMonitor);
|
|
|
|
delete sMessageLoop;
|
|
sMessageLoop = nullptr;
|
|
MOZ_ASSERT(sThread);
|
|
nsThreadManager::get().UnregisterCurrentThread(*sThread);
|
|
sThread = nullptr;
|
|
mDestroyed = true;
|
|
sThreadDestroyed = true;
|
|
lock.NotifyAll();
|
|
return NS_OK;
|
|
}
|
|
|
|
void WaitForDestruction()
|
|
{
|
|
MOZ_ASSERT(sMessageLoopAccessMonitor);
|
|
MonitorAutoLock lock(*sMessageLoopAccessMonitor);
|
|
while (!mDestroyed) {
|
|
lock.Wait();
|
|
}
|
|
}
|
|
|
|
private:
|
|
bool mDestroyed;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
namespace mozilla {
|
|
|
|
void
|
|
CreateAndroidUiThread()
|
|
{
|
|
MOZ_ASSERT(!sThread);
|
|
MOZ_ASSERT(!sMessageLoopAccessMonitor);
|
|
sMessageLoopAccessMonitor = new Monitor("AndroidUiThreadMessageLoopAccessMonitor");
|
|
sThreadDestroyed = false;
|
|
RefPtr<CreateOnUiThread> runnable = new CreateOnUiThread;
|
|
AndroidBridge::Bridge()->PostTaskToUiThread(do_AddRef(runnable), 0);
|
|
}
|
|
|
|
void
|
|
DestroyAndroidUiThread()
|
|
{
|
|
MOZ_ASSERT(sThread);
|
|
// Insure the Android bridge has not already been deconstructed.
|
|
MOZ_ASSERT(AndroidBridge::Bridge() != nullptr);
|
|
RefPtr<DestroyOnUiThread> runnable = new DestroyOnUiThread;
|
|
AndroidBridge::Bridge()->PostTaskToUiThread(do_AddRef(runnable), 0);
|
|
runnable->WaitForDestruction();
|
|
delete sMessageLoopAccessMonitor;
|
|
sMessageLoopAccessMonitor = nullptr;
|
|
}
|
|
|
|
MessageLoop*
|
|
GetAndroidUiThreadMessageLoop()
|
|
{
|
|
if (!sMessageLoopAccessMonitor) {
|
|
return nullptr;
|
|
}
|
|
|
|
MonitorAutoLock lock(*sMessageLoopAccessMonitor);
|
|
while (!sMessageLoop) {
|
|
lock.Wait();
|
|
}
|
|
|
|
return sMessageLoop;
|
|
}
|
|
|
|
RefPtr<nsThread>
|
|
GetAndroidUiThread()
|
|
{
|
|
if (!sMessageLoopAccessMonitor) {
|
|
return nullptr;
|
|
}
|
|
|
|
MonitorAutoLock lock(*sMessageLoopAccessMonitor);
|
|
while (!sThread) {
|
|
lock.Wait();
|
|
}
|
|
|
|
return sThread;
|
|
}
|
|
|
|
} // namespace mozilla
|