Bug 1195496 - Associate queued calls with available states; r=snorp

Many calls are associated with a Gecko state when they become available.
For example, calls that only depend on XPCOM become available very early
in startup, at the JNI_READY state. However, calls that depend on JS
components may only be available at the end of startup, at the RUNNING
state.

This patch adds an available state to every queued call, so that calls
can be made as soon as they become available, which is important for
calls like making speculative connections.
This commit is contained in:
Jim Chen 2015-08-19 18:14:47 -04:00
Родитель cb9e675820
Коммит 1cba8bd3ef
1 изменённых файлов: 63 добавлений и 17 удалений

Просмотреть файл

@ -84,11 +84,14 @@ public class GeckoThread extends Thread implements GeckoEventListener {
public Method method;
public Object target;
public Object[] args;
public State state;
public QueuedCall(final Method method, final Object target, final Object[] args) {
public QueuedCall(final Method method, final Object target,
final Object[] args, final State state) {
this.method = method;
this.target = target;
this.args = args;
this.state = state;
}
}
@ -161,7 +164,8 @@ public class GeckoThread extends Thread implements GeckoEventListener {
// Queue a call to the given method.
private static void queueNativeCallLocked(final Class<?> cls, final String methodName,
final Object obj, final Object[] args) {
final Object obj, final Object[] args,
final State state) {
final Class<?>[] argTypes = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
Class<?> argType = args[i].getClass();
@ -182,48 +186,83 @@ public class GeckoThread extends Thread implements GeckoEventListener {
throw new UnsupportedOperationException("Cannot find method", e);
}
if (QUEUED_CALLS.size() == 0 && isRunning()) {
if (QUEUED_CALLS.size() == 0 && isStateAtLeast(state)) {
invokeMethod(method, obj, args);
return;
}
QUEUED_CALLS.add(new QueuedCall(method, obj, args));
QUEUED_CALLS.add(new QueuedCall(method, obj, args, state));
}
/**
* Queue a call to the given static method until Gecko is in RUNNING state.
* Queue a call to the given static method until Gecko is in the given state.
*
* @param state The Gecko state in which the native call could be executed.
* Default is State.RUNNING, which means this queued call will
* run when Gecko is at or after RUNNING state.
* @param cls Class that declares the static method.
* @param methodName Name of the static method.
* @param args Args to call the static method with.
*/
public static void queueNativeCall(final Class<?> cls, final String methodName,
final Object... args) {
public static void queueNativeCallUntil(final State state, final Class<?> cls,
final String methodName, final Object... args) {
synchronized (QUEUED_CALLS) {
queueNativeCallLocked(cls, methodName, null, args);
queueNativeCallLocked(cls, methodName, null, args, state);
}
}
/**
* Queue a call to the given instance method until Gecko is in RUNNING state.
* Queue a call to the given static method until Gecko is in the RUNNING state.
*/
public static void queueNativeCall(final Class<?> cls, final String methodName,
final Object... args) {
synchronized (QUEUED_CALLS) {
queueNativeCallLocked(cls, methodName, null, args, State.RUNNING);
}
}
/**
* Queue a call to the given instance method until Gecko is in the given state.
*
* @param state The Gecko state in which the native call could be executed.
* @param obj Object that declares the instance method.
* @param methodName Name of the instance method.
* @param args Args to call the instance method with.
*/
public static void queueNativeCallUntil(final State state, final Object obj,
final String methodName, final Object... args) {
synchronized (QUEUED_CALLS) {
queueNativeCallLocked(obj.getClass(), methodName, obj, args, state);
}
}
/**
* Queue a call to the given instance method until Gecko is in the RUNNING state.
*/
public static void queueNativeCall(final Object obj, final String methodName,
final Object... args) {
synchronized (QUEUED_CALLS) {
queueNativeCallLocked(obj.getClass(), methodName, obj, args);
queueNativeCallLocked(obj.getClass(), methodName, obj, args, State.RUNNING);
}
}
// Run all queued methods
private static void flushQueuedNativeCalls(final State state) {
if (!state.is(State.RUNNING)) {
return;
}
synchronized (QUEUED_CALLS) {
for (QueuedCall call : QUEUED_CALLS) {
int lastSkipped = -1;
for (int i = 0; i < QUEUED_CALLS.size(); i++) {
final QueuedCall call = QUEUED_CALLS.get(i);
if (call == null) {
// We already handled the call.
continue;
}
if (!state.isAtLeast(call.state)) {
// The call is not ready yet; skip it.
lastSkipped = i;
continue;
}
// Mark as handled.
QUEUED_CALLS.set(i, null);
if (call.method == null) {
final GeckoEvent e = (GeckoEvent) call.target;
GeckoAppShell.notifyGeckoOfEvent(e);
@ -232,8 +271,15 @@ public class GeckoThread extends Thread implements GeckoEventListener {
}
invokeMethod(call.method, call.target, call.args);
}
QUEUED_CALLS.clear();
QUEUED_CALLS.trimToSize();
if (lastSkipped < 0) {
// We're done here; release the memory
QUEUED_CALLS.clear();
QUEUED_CALLS.trimToSize();
} else if (lastSkipped < QUEUED_CALLS.size() - 1) {
// We skipped some; free up null entries at the end,
// but keep all the previous entries for later.
QUEUED_CALLS.subList(lastSkipped + 1, QUEUED_CALLS.size()).clear();
}
}
}
@ -409,7 +455,7 @@ public class GeckoThread extends Thread implements GeckoEventListener {
GeckoAppShell.notifyGeckoOfEvent(e);
e.recycle();
} else {
QUEUED_CALLS.add(new QueuedCall(null, e, null));
QUEUED_CALLS.add(new QueuedCall(null, e, null, State.RUNNING));
}
}
}