Bug 1352534 - [1.3] Allow for mupltiple native queues and assign a unique queue per GeckoView. r=jchen

This commit is contained in:
Eugen Sawin 2017-03-31 21:30:16 +02:00
Родитель 75452c87a4
Коммит 2b30df194c
4 изменённых файлов: 114 добавлений и 113 удалений

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

@ -9,7 +9,6 @@ import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.mozglue.JNIObject;
import org.mozilla.gecko.NativeQueue.StateHolder;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
@ -55,7 +54,7 @@ public final class EventDispatcher extends JNIObject {
new HashMap<String, List<BundleEventListener>>(DEFAULT_BACKGROUND_EVENTS_COUNT);
private boolean mAttachedToGecko;
private volatile StateHolder mStateHolder;
private final NativeQueue mNativeQueue;
@ReflectionTarget
@WrapForJNI(calledFrom = "gecko")
@ -64,22 +63,15 @@ public final class EventDispatcher extends JNIObject {
}
/* package */ EventDispatcher() {
mStateHolder = GeckoThread.getStateHolder();
mNativeQueue = GeckoThread.getNativeQueue();
}
/* package */ EventDispatcher(final NativeQueue.StateHolder stateHolder) {
mStateHolder = stateHolder;
}
/* package */ void setStateHolder(final NativeQueue.StateHolder stateHolder) {
mStateHolder = stateHolder;
// Force queue flushing.
final NativeQueue.State state = mStateHolder.getState();
mStateHolder.checkAndSetState(state, state);
/* package */ EventDispatcher(final NativeQueue queue) {
mNativeQueue = queue;
}
private boolean isReadyForDispatchingToGecko() {
return mStateHolder.isReady();
return mNativeQueue.isReady();
}
@WrapForJNI(dispatchTo = "gecko") @Override // JNIObject
@ -301,8 +293,7 @@ public final class EventDispatcher extends JNIObject {
// Gecko, we make a special exception to queue this event until
// Gecko(View) is ready. This way, Gecko can first register its
// listeners, and accept the event when it is ready.
NativeQueue.queueUntil(mStateHolder,
mStateHolder.getReadyState(), this, "dispatchToGecko",
mNativeQueue.queueUntilReady(this, "dispatchToGecko",
String.class, type,
GeckoBundle.class, message,
EventCallback.class, JavaCallbackDelegate.wrap(callback));

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

@ -8,7 +8,6 @@ package org.mozilla.gecko;
import org.mozilla.gecko.annotation.RobocopTarget;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.NativeQueue.StateHolder;
import org.mozilla.gecko.util.FileUtils;
import org.mozilla.gecko.util.ThreadUtils;
@ -80,11 +79,11 @@ public class GeckoThread extends Thread {
}
}
private static final StateHolder sStateHolder =
new StateHolder(State.INITIAL, State.RUNNING);
private static final NativeQueue sNativeQueue =
new NativeQueue(State.INITIAL, State.RUNNING);
/* package */ static StateHolder getStateHolder() {
return sStateHolder;
/* package */ static NativeQueue getNativeQueue() {
return sNativeQueue;
}
public static final State MIN_STATE = State.INITIAL;
@ -446,7 +445,7 @@ public class GeckoThread extends Thread {
* @return True if the current Gecko thread state matches
*/
public static boolean isState(final State state) {
return sStateHolder.getState().is(state);
return sNativeQueue.getState().is(state);
}
/**
@ -457,7 +456,7 @@ public class GeckoThread extends Thread {
* @return True if the current Gecko thread state matches
*/
public static boolean isStateAtLeast(final State state) {
return sStateHolder.getState().isAtLeast(state);
return sNativeQueue.getState().isAtLeast(state);
}
/**
@ -468,7 +467,7 @@ public class GeckoThread extends Thread {
* @return True if the current Gecko thread state matches
*/
public static boolean isStateAtMost(final State state) {
return state.isAtLeast(sStateHolder.getState());
return state.isAtLeast(sNativeQueue.getState());
}
/**
@ -485,13 +484,13 @@ public class GeckoThread extends Thread {
@WrapForJNI(calledFrom = "gecko")
private static void setState(final State newState) {
sStateHolder.setState(newState);
sNativeQueue.setState(newState);
}
@WrapForJNI(calledFrom = "gecko")
private static boolean checkAndSetState(final State expectedState,
final State newState) {
return sStateHolder.checkAndSetState(expectedState, newState);
return sNativeQueue.checkAndSetState(expectedState, newState);
}
@WrapForJNI(stubName = "SpeculativeConnect")
@ -561,7 +560,7 @@ public class GeckoThread extends Thread {
*/
public static void queueNativeCall(final Class<?> cls, final String methodName,
final Object... args) {
NativeQueue.queueUntil(getStateHolder(), State.RUNNING, cls, methodName, args);
sNativeQueue.queueUntilReady(cls, methodName, args);
}
/**
@ -569,7 +568,7 @@ public class GeckoThread extends Thread {
*/
public static void queueNativeCall(final Object obj, final String methodName,
final Object... args) {
NativeQueue.queueUntil(getStateHolder(), State.RUNNING, obj, methodName, args);
sNativeQueue.queueUntilReady(obj, methodName, args);
}
/**
@ -577,7 +576,7 @@ public class GeckoThread extends Thread {
*/
public static void queueNativeCallUntil(final State state, final Object obj, final String methodName,
final Object... args) {
NativeQueue.queueUntil(getStateHolder(), state, obj, methodName, args);
sNativeQueue.queueUntil(state, obj, methodName, args);
}
/**
@ -585,6 +584,6 @@ public class GeckoThread extends Thread {
*/
public static void queueNativeCallUntil(final State state, final Class<?> cls, final String methodName,
final Object... args) {
NativeQueue.queueUntil(getStateHolder(), state, cls, methodName, args);
sNativeQueue.queueUntil(state, cls, methodName, args);
}
}

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

@ -12,7 +12,6 @@ import org.mozilla.gecko.annotation.ReflectionTarget;
import org.mozilla.gecko.annotation.WrapForJNI;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.mozglue.JNIObject;
import org.mozilla.gecko.NativeQueue.StateHolder;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
@ -47,7 +46,7 @@ public class GeckoView extends LayerView
@WrapForJNI INITIAL(0),
@WrapForJNI READY(1);
private int mRank;
private final int mRank;
private State(int rank) {
mRank = rank;
@ -60,18 +59,16 @@ public class GeckoView extends LayerView
@Override
public boolean isAtLeast(final NativeQueue.State other) {
if (other instanceof State) {
return mRank >= ((State) other).mRank;
}
return false;
return (other instanceof State) &&
mRank >= ((State) other).mRank;
}
}
private static final StateHolder sDummyStateHolder =
new StateHolder(State.INITIAL, State.READY);
private final NativeQueue mNativeQueue =
new NativeQueue(State.INITIAL, State.READY);
private final EventDispatcher mEventDispatcher =
new EventDispatcher(sDummyStateHolder);
new EventDispatcher(mNativeQueue);
private ChromeDelegate mChromeDelegate;
/* package */ ContentListener mContentListener;
@ -88,11 +85,12 @@ public class GeckoView extends LayerView
@WrapForJNI(dispatchTo = "proxy")
protected static final class Window extends JNIObject {
@WrapForJNI(skip = true)
/* package */ final StateHolder mStateHolder =
new StateHolder(State.INITIAL, State.READY);
/* package */ NativeQueue mNativeQueue;
@WrapForJNI(skip = true)
/* package */ Window() {}
/* package */ Window(final NativeQueue queue) {
mNativeQueue = queue;
}
static native void open(Window instance, GeckoView view,
Object compositor, EventDispatcher dispatcher,
@ -109,8 +107,8 @@ public class GeckoView extends LayerView
native void loadUri(String uri, int flags);
@WrapForJNI(calledFrom = "gecko")
private void setState(final State newState) {
mStateHolder.setState(newState);
private synchronized void setState(final State newState) {
mNativeQueue.setState(newState);
}
}
@ -287,7 +285,12 @@ public class GeckoView extends LayerView
}
protected void reattachWindow() {
mEventDispatcher.setStateHolder(mWindow.mStateHolder);
synchronized (mWindow) {
if (mNativeQueue != mWindow.mNativeQueue) {
mNativeQueue.setState(mWindow.mNativeQueue.getState());
mWindow.mNativeQueue = mNativeQueue;
}
}
if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
mWindow.reattach(this, getCompositor(), mEventDispatcher);
@ -304,8 +307,7 @@ public class GeckoView extends LayerView
if (mWindow == null) {
// Open a new nsWindow if we didn't have one from before.
mWindow = new Window();
mEventDispatcher.setStateHolder(mWindow.mStateHolder);
mWindow = new Window(mNativeQueue);
openWindow();
} else {
reattachWindow();
@ -336,7 +338,6 @@ public class GeckoView extends LayerView
mWindow, "disposeNative");
}
mEventDispatcher.setStateHolder(sDummyStateHolder);
mOnAttachedToWindowCalled = false;
}

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

@ -20,42 +20,34 @@ public class NativeQueue {
boolean isAtLeast(final State other);
}
public static class StateHolder {
private volatile State mState;
private final State mReadyState;
private volatile State mState;
private final State mReadyState;
public StateHolder(final State initial, final State ready) {
this.mState = initial;
this.mReadyState = ready;
}
public NativeQueue(final State initial, final State ready) {
mState = initial;
mReadyState = ready;
}
public boolean isReady() {
return getState().isAtLeast(mReadyState);
}
public boolean isReady() {
return getState().isAtLeast(mReadyState);
}
public State getReadyState() {
return mReadyState;
}
public State getState() {
return mState;
}
public State getState() {
return mState;
}
public boolean setState(final State newState) {
return checkAndSetState(null, newState);
}
public boolean setState(final State newState) {
return checkAndSetState(null, newState);
}
public boolean checkAndSetState(final State expectedState,
final State newState) {
synchronized (NativeQueue.sQueue) {
if (expectedState != null && !mState.is(expectedState)) {
return false;
}
NativeQueue.flushQueuedLocked(newState);
mState = newState;
return true;
}
public synchronized boolean checkAndSetState(final State expectedState,
final State newState) {
if (expectedState != null && !mState.is(expectedState)) {
return false;
}
flushQueuedLocked(newState);
mState = newState;
return true;
}
private static class QueuedCall {
@ -74,7 +66,7 @@ public class NativeQueue {
}
private static final int QUEUED_CALLS_COUNT = 16;
/* package */ static final ArrayList<QueuedCall> sQueue =
/* package */ final ArrayList<QueuedCall> mQueue =
new ArrayList<>(QUEUED_CALLS_COUNT);
// Invoke the given Method and handle checked Exceptions.
@ -91,12 +83,11 @@ public class NativeQueue {
}
// Queue a call to the given method.
private static void queueNativeCallLocked(final StateHolder stateHolder,
final Class<?> cls,
final String methodName,
final Object obj,
final Object[] args,
final State state) {
private void queueNativeCallLocked(final Class<?> cls,
final String methodName,
final Object obj,
final Object[] args,
final State state) {
final ArrayList<Class<?>> argTypes = new ArrayList<>(args.length);
final ArrayList<Object> argValues = new ArrayList<>(args.length);
@ -135,63 +126,82 @@ public class NativeQueue {
throw new UnsupportedOperationException("Not allowed to queue non-native methods");
}
if (stateHolder.getState().isAtLeast(state)) {
if (getState().isAtLeast(state)) {
invokeMethod(method, obj, argValues.toArray());
return;
}
sQueue.add(new QueuedCall(
mQueue.add(new QueuedCall(
method, obj, argValues.toArray(), state));
}
/**
* Queue a call to the given instance method if the given current state does
* not satisfy the isReady condition.
*
* @param obj Object that declares the instance method.
* @param methodName Name of the instance method.
* @param args Args to call the instance method with; to specify a parameter
* type, pass in a Class instance first, followed by the value.
*/
public synchronized void queueUntilReady(final Object obj,
final String methodName,
final Object... args) {
queueNativeCallLocked(obj.getClass(), methodName, obj, args, mReadyState);
}
/**
* Queue a call to the given static method if the given current state does
* not satisfy the isReady condition.
*
* @param cls Class that declares the static method.
* @param methodName Name of the instance method.
* @param args Args to call the instance method with; to specify a parameter
* type, pass in a Class instance first, followed by the value.
*/
public synchronized void queueUntilReady(final Class<?> cls,
final String methodName,
final Object... args) {
queueNativeCallLocked(cls, methodName, null, args, mReadyState);
}
/**
* Queue a call to the given instance method if the given current state does
* not satisfy the given state.
*
* @param stateHolder The state holder used to query the current state.
* @param state The 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; to specify a parameter
* type, pass in a Class instance first, followed by the value.
*/
public static void queueUntil(final StateHolder stateHolder,
final State state,
final Object obj,
final String methodName,
final Object... args) {
synchronized (sQueue) {
queueNativeCallLocked(stateHolder, obj.getClass(), methodName, obj,
args, state);
}
public synchronized void queueUntil(final State state, final Object obj,
final String methodName,
final Object... args) {
queueNativeCallLocked(obj.getClass(), methodName, obj, args, state);
}
/**
* Queue a call to the given static method if the given current state does
* not satisfy the given state.
*
* @param stateHolder The state holder used to query the current state.
* @param state The state in which the native call could be executed.
* @param cls Class that declares the static method.
* @param methodName Name of the instance method.
* @param args Args to call the instance method with; to specify a parameter
* type, pass in a Class instance first, followed by the value.
*/
public static void queueUntil(final StateHolder stateHolder,
final State state,
final Class<?> cls,
final String methodName,
final Object... args) {
synchronized (sQueue) {
queueNativeCallLocked(stateHolder, cls, methodName, null, args, state);
}
public synchronized void queueUntil(final State state, final Class<?> cls,
final String methodName,
final Object... args) {
queueNativeCallLocked(cls, methodName, null, args, state);
}
// Run all queued methods
private static void flushQueuedLocked(final State state) {
private void flushQueuedLocked(final State state) {
int lastSkipped = -1;
for (int i = 0; i < sQueue.size(); i++) {
final QueuedCall call = sQueue.get(i);
for (int i = 0; i < mQueue.size(); i++) {
final QueuedCall call = mQueue.get(i);
if (call == null) {
// We already handled the call.
continue;
@ -202,18 +212,18 @@ public class NativeQueue {
continue;
}
// Mark as handled.
sQueue.set(i, null);
mQueue.set(i, null);
invokeMethod(call.method, call.target, call.args);
}
if (lastSkipped < 0) {
// We're done here; release the memory
sQueue.clear();
sQueue.trimToSize();
} else if (lastSkipped < sQueue.size() - 1) {
mQueue.clear();
mQueue.trimToSize();
} else if (lastSkipped < mQueue.size() - 1) {
// We skipped some; free up null entries at the end,
// but keep all the previous entries for later.
sQueue.subList(lastSkipped + 1, sQueue.size()).clear();
mQueue.subList(lastSkipped + 1, mQueue.size()).clear();
}
}
}