mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1367850 - Move Android UI thread runnable queue from AndroidBridge to AndroidUiThread r=jchen
MozReview-Commit-ID: 4okw7R2P2LC
This commit is contained in:
parent
7949bdab33
commit
49e524747b
@ -153,7 +153,6 @@ AndroidBridge::~AndroidBridge()
|
||||
}
|
||||
|
||||
AndroidBridge::AndroidBridge()
|
||||
: mUiTaskQueueLock("UiTaskQueue")
|
||||
{
|
||||
ALOG_BRIDGE("AndroidBridge::Init");
|
||||
|
||||
@ -947,109 +946,6 @@ AndroidBridge::IsContentDocumentDisplayed(mozIDOMWindowProxy* aWindow)
|
||||
return layerClient->IsContentDocumentDisplayed();
|
||||
}
|
||||
|
||||
class AndroidBridge::DelayedTask
|
||||
{
|
||||
using TimeStamp = mozilla::TimeStamp;
|
||||
using TimeDuration = mozilla::TimeDuration;
|
||||
|
||||
public:
|
||||
DelayedTask(already_AddRefed<nsIRunnable> aTask)
|
||||
: mTask(aTask)
|
||||
, mRunTime() // Null timestamp representing no delay.
|
||||
{}
|
||||
|
||||
DelayedTask(already_AddRefed<nsIRunnable> aTask, int aDelayMs)
|
||||
: mTask(aTask)
|
||||
, mRunTime(TimeStamp::Now() + TimeDuration::FromMilliseconds(aDelayMs))
|
||||
{}
|
||||
|
||||
bool IsEarlierThan(const DelayedTask& aOther) const
|
||||
{
|
||||
if (mRunTime) {
|
||||
return aOther.mRunTime ? mRunTime < aOther.mRunTime : false;
|
||||
}
|
||||
// In the case of no delay, we're earlier if aOther has a delay.
|
||||
// Otherwise, we're not earlier, to maintain task order.
|
||||
return !!aOther.mRunTime;
|
||||
}
|
||||
|
||||
int64_t MillisecondsToRunTime() const
|
||||
{
|
||||
if (mRunTime) {
|
||||
return int64_t((mRunTime - TimeStamp::Now()).ToMilliseconds());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIRunnable> TakeTask()
|
||||
{
|
||||
return mTask.forget();
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIRunnable> mTask;
|
||||
const TimeStamp mRunTime;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
AndroidBridge::PostTaskToUiThread(already_AddRefed<nsIRunnable> aTask, int aDelayMs)
|
||||
{
|
||||
// add the new task into the mUiTaskQueue, sorted with
|
||||
// the earliest task first in the queue
|
||||
size_t i;
|
||||
DelayedTask newTask(aDelayMs ? DelayedTask(mozilla::Move(aTask), aDelayMs)
|
||||
: DelayedTask(mozilla::Move(aTask)));
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mUiTaskQueueLock);
|
||||
|
||||
for (i = 0; i < mUiTaskQueue.Length(); i++) {
|
||||
if (newTask.IsEarlierThan(mUiTaskQueue[i])) {
|
||||
mUiTaskQueue.InsertElementAt(i, mozilla::Move(newTask));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == mUiTaskQueue.Length()) {
|
||||
// We didn't insert the task, which means we should append it.
|
||||
mUiTaskQueue.AppendElement(mozilla::Move(newTask));
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
// if we're inserting it at the head of the queue, notify Java because
|
||||
// we need to get a callback at an earlier time than the last scheduled
|
||||
// callback
|
||||
GeckoThread::RequestUiThreadCallback(int64_t(aDelayMs));
|
||||
}
|
||||
}
|
||||
|
||||
int64_t
|
||||
AndroidBridge::RunDelayedUiThreadTasks()
|
||||
{
|
||||
MutexAutoLock lock(mUiTaskQueueLock);
|
||||
|
||||
while (!mUiTaskQueue.IsEmpty()) {
|
||||
const int64_t timeLeft = mUiTaskQueue[0].MillisecondsToRunTime();
|
||||
if (timeLeft > 0) {
|
||||
// this task (and therefore all remaining tasks)
|
||||
// have not yet reached their runtime. return the
|
||||
// time left until we should be called again
|
||||
return timeLeft;
|
||||
}
|
||||
|
||||
// Retrieve task before unlocking/running.
|
||||
nsCOMPtr<nsIRunnable> nextTask(mUiTaskQueue[0].TakeTask());
|
||||
mUiTaskQueue.RemoveElementAt(0);
|
||||
|
||||
// Unlock to allow posting new tasks reentrantly.
|
||||
MutexAutoUnlock unlock(mUiTaskQueueLock);
|
||||
nextTask->Run();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Object::LocalRef AndroidBridge::ChannelCreate(Object::Param stream) {
|
||||
JNIEnv* const env = GetEnvForThread();
|
||||
auto rv = Object::LocalRef::Adopt(env, env->CallStaticObjectMethod(
|
||||
|
@ -224,15 +224,6 @@ protected:
|
||||
jni::Object::GlobalRef mMessageQueue;
|
||||
jfieldID mMessageQueueMessages;
|
||||
jmethodID mMessageQueueNext;
|
||||
|
||||
private:
|
||||
class DelayedTask;
|
||||
nsTArray<DelayedTask> mUiTaskQueue;
|
||||
mozilla::Mutex mUiTaskQueueLock;
|
||||
|
||||
public:
|
||||
void PostTaskToUiThread(already_AddRefed<nsIRunnable> aTask, int aDelayMs);
|
||||
int64_t RunDelayedUiThreadTasks();
|
||||
};
|
||||
|
||||
class AutoJNIClass {
|
||||
|
@ -3,12 +3,15 @@
|
||||
* 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 "GeneratedJNIWrappers.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsThread.h"
|
||||
#include "nsThreadManager.h"
|
||||
#include "nsThreadUtils.h"
|
||||
@ -18,17 +21,22 @@ using namespace mozilla;
|
||||
namespace {
|
||||
|
||||
class AndroidUiThread;
|
||||
class AndroidUiTask;
|
||||
|
||||
StaticAutoPtr<LinkedList<AndroidUiTask> > sTaskQueue;
|
||||
StaticAutoPtr<mozilla::Mutex> sTaskQueueLock;
|
||||
StaticRefPtr<AndroidUiThread> sThread;
|
||||
static bool sThreadDestroyed;
|
||||
static MessageLoop* sMessageLoop;
|
||||
static Atomic<Monitor*> sMessageLoopAccessMonitor;
|
||||
|
||||
void EnqueueTask(already_AddRefed<nsIRunnable> aTask, int aDelayMs);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* are immediately dispatched to the Android UI thread.
|
||||
* 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
|
||||
@ -48,14 +56,6 @@ public:
|
||||
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()
|
||||
{}
|
||||
@ -69,7 +69,7 @@ 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);
|
||||
EnqueueTask(Move(aEvent), 0);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
@ -77,7 +77,7 @@ AndroidUiThread::Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags)
|
||||
NS_IMETHODIMP
|
||||
AndroidUiThread::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aDelayMs)
|
||||
{
|
||||
AndroidBridge::Bridge()->PostTaskToUiThread(Move(aEvent), aDelayMs);
|
||||
EnqueueTask(Move(aEvent), aDelayMs);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ NS_IMPL_ISUPPORTS(ThreadObserver, nsIThreadObserver)
|
||||
NS_IMETHODIMP
|
||||
ThreadObserver::OnDispatchedEvent(nsIThreadInternal *thread)
|
||||
{
|
||||
AndroidBridge::Bridge()->PostTaskToUiThread(NS_NewRunnableFunction("PumpEvents", &PumpEvents), 0);
|
||||
EnqueueTask(NS_NewRunnableFunction("PumpEvents", &PumpEvents), 0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -121,6 +121,49 @@ ThreadObserver::AfterProcessNextEvent(nsIThreadInternal *thread, bool eventWasPr
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class AndroidUiTask : public LinkedListElement<AndroidUiTask> {
|
||||
using TimeStamp = mozilla::TimeStamp;
|
||||
using TimeDuration = mozilla::TimeDuration;
|
||||
|
||||
public:
|
||||
AndroidUiTask(already_AddRefed<nsIRunnable> aTask)
|
||||
: mTask(aTask)
|
||||
, mRunTime() // Null timestamp representing no delay.
|
||||
{}
|
||||
|
||||
AndroidUiTask(already_AddRefed<nsIRunnable> aTask, int aDelayMs)
|
||||
: mTask(aTask)
|
||||
, mRunTime(TimeStamp::Now() + TimeDuration::FromMilliseconds(aDelayMs))
|
||||
{}
|
||||
|
||||
bool IsEarlierThan(const AndroidUiTask& aOther) const
|
||||
{
|
||||
if (mRunTime) {
|
||||
return aOther.mRunTime ? mRunTime < aOther.mRunTime : false;
|
||||
}
|
||||
// In the case of no delay, we're earlier if aOther has a delay.
|
||||
// Otherwise, we're not earlier, to maintain task order.
|
||||
return !!aOther.mRunTime;
|
||||
}
|
||||
|
||||
int64_t MillisecondsToRunTime() const
|
||||
{
|
||||
if (mRunTime) {
|
||||
return int64_t((mRunTime - TimeStamp::Now()).ToMilliseconds());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIRunnable> TakeTask()
|
||||
{
|
||||
return mTask.forget();
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIRunnable> mTask;
|
||||
const TimeStamp mRunTime;
|
||||
};
|
||||
|
||||
class CreateOnUiThread : public Runnable {
|
||||
public:
|
||||
CreateOnUiThread() : Runnable("CreateOnUiThread")
|
||||
@ -147,7 +190,17 @@ public:
|
||||
NS_IMETHOD Run() override {
|
||||
MOZ_ASSERT(!sThreadDestroyed);
|
||||
MOZ_ASSERT(sMessageLoopAccessMonitor);
|
||||
MOZ_ASSERT(sTaskQueue);
|
||||
MonitorAutoLock lock(*sMessageLoopAccessMonitor);
|
||||
sThreadDestroyed = true;
|
||||
|
||||
{
|
||||
// Flush the queue
|
||||
MutexAutoLock lock (*sTaskQueueLock);
|
||||
while (AndroidUiTask* task = sTaskQueue->getFirst()) {
|
||||
delete task;
|
||||
}
|
||||
}
|
||||
|
||||
delete sMessageLoop;
|
||||
sMessageLoop = nullptr;
|
||||
@ -155,7 +208,6 @@ public:
|
||||
nsThreadManager::get().UnregisterCurrentThread(*sThread);
|
||||
sThread = nullptr;
|
||||
mDestroyed = true;
|
||||
sThreadDestroyed = true;
|
||||
lock.NotifyAll();
|
||||
return NS_OK;
|
||||
}
|
||||
@ -173,6 +225,47 @@ private:
|
||||
bool mDestroyed;
|
||||
};
|
||||
|
||||
void
|
||||
EnqueueTask(already_AddRefed<nsIRunnable> aTask, int aDelayMs)
|
||||
{
|
||||
|
||||
if (sThreadDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add the new task into the sTaskQueue, sorted with
|
||||
// the earliest task first in the queue
|
||||
AndroidUiTask* newTask = (aDelayMs ? new AndroidUiTask(mozilla::Move(aTask), aDelayMs)
|
||||
: new AndroidUiTask(mozilla::Move(aTask)));
|
||||
|
||||
{
|
||||
MOZ_ASSERT(sTaskQueue);
|
||||
MOZ_ASSERT(sTaskQueueLock);
|
||||
MutexAutoLock lock(*sTaskQueueLock);
|
||||
|
||||
AndroidUiTask* task = sTaskQueue->getFirst();
|
||||
|
||||
while (task) {
|
||||
if (newTask->IsEarlierThan(*task)) {
|
||||
task->setPrevious(newTask);
|
||||
break;
|
||||
}
|
||||
task = task->getNext();
|
||||
}
|
||||
|
||||
if (!newTask->isInList()) {
|
||||
sTaskQueue->insertBack(newTask);
|
||||
}
|
||||
}
|
||||
|
||||
if (!newTask->getPrevious()) {
|
||||
// if we're inserting it at the head of the queue, notify Java because
|
||||
// we need to get a callback at an earlier time than the last scheduled
|
||||
// callback
|
||||
GeckoThread::RequestUiThreadCallback(int64_t(aDelayMs));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mozilla {
|
||||
@ -182,20 +275,20 @@ CreateAndroidUiThread()
|
||||
{
|
||||
MOZ_ASSERT(!sThread);
|
||||
MOZ_ASSERT(!sMessageLoopAccessMonitor);
|
||||
sTaskQueue = new LinkedList<AndroidUiTask>();
|
||||
sTaskQueueLock = new Mutex("AndroidUiThreadTaskQueueLock");
|
||||
sMessageLoopAccessMonitor = new Monitor("AndroidUiThreadMessageLoopAccessMonitor");
|
||||
sThreadDestroyed = false;
|
||||
RefPtr<CreateOnUiThread> runnable = new CreateOnUiThread;
|
||||
AndroidBridge::Bridge()->PostTaskToUiThread(do_AddRef(runnable), 0);
|
||||
EnqueueTask(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);
|
||||
EnqueueTask(do_AddRef(runnable), 0);
|
||||
runnable->WaitForDestruction();
|
||||
delete sMessageLoopAccessMonitor;
|
||||
sMessageLoopAccessMonitor = nullptr;
|
||||
@ -231,4 +324,38 @@ GetAndroidUiThread()
|
||||
return sThread;
|
||||
}
|
||||
|
||||
int64_t
|
||||
RunAndroidUiTasks()
|
||||
{
|
||||
MutexAutoLock lock(*sTaskQueueLock);
|
||||
|
||||
if (sThreadDestroyed) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!sTaskQueue->isEmpty()) {
|
||||
AndroidUiTask* task = sTaskQueue->getFirst();
|
||||
const int64_t timeLeft = task->MillisecondsToRunTime();
|
||||
if (timeLeft > 0) {
|
||||
// this task (and therefore all remaining tasks)
|
||||
// have not yet reached their runtime. return the
|
||||
// time left until we should be called again
|
||||
return timeLeft;
|
||||
}
|
||||
|
||||
// Retrieve task before unlocking/running.
|
||||
nsCOMPtr<nsIRunnable> runnable(task->TakeTask());
|
||||
// LinkedListElements auto remove from list upon destruction
|
||||
delete task;
|
||||
|
||||
// Unlock to allow posting new tasks reentrantly.
|
||||
MutexAutoUnlock unlock(*sTaskQueueLock);
|
||||
runnable->Run();
|
||||
if (sThreadDestroyed) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -15,6 +15,7 @@ namespace mozilla {
|
||||
|
||||
void CreateAndroidUiThread();
|
||||
void DestroyAndroidUiThread();
|
||||
int64_t RunAndroidUiTasks();
|
||||
|
||||
MessageLoop* GetAndroidUiThreadMessageLoop();
|
||||
RefPtr<nsThread> GetAndroidUiThread();
|
||||
|
@ -242,7 +242,7 @@ public:
|
||||
|
||||
static int64_t RunUiThreadCallback()
|
||||
{
|
||||
return AndroidUiThread::RunDelayedTasksIfValid();
|
||||
return RunAndroidUiTasks();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -422,9 +422,13 @@ public:
|
||||
};
|
||||
|
||||
NativePanZoomController::GlobalRef npzc = mNPZC;
|
||||
AndroidBridge::Bridge()->PostTaskToUiThread(NewRunnableFunction(
|
||||
RefPtr<nsThread> uiThread = GetAndroidUiThread();
|
||||
if (!uiThread) {
|
||||
return;
|
||||
}
|
||||
uiThread->Dispatch(NewRunnableFunction(
|
||||
static_cast<void(*)(const NPZCRef&)>(callDestroy),
|
||||
mozilla::Move(npzc)), 0);
|
||||
mozilla::Move(npzc)), nsIThread::DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user