| Index: base/android/java/src/org/chromium/base/SystemMessageHandler.java
|
| diff --git a/base/android/java/src/org/chromium/base/SystemMessageHandler.java b/base/android/java/src/org/chromium/base/SystemMessageHandler.java
|
| index 10f8d55448e014b8d9717d34197428fbf4bd9200..fd3dc5a08b640fe0d7002bd657d5e9973d127812 100644
|
| --- a/base/android/java/src/org/chromium/base/SystemMessageHandler.java
|
| +++ b/base/android/java/src/org/chromium/base/SystemMessageHandler.java
|
| @@ -5,12 +5,9 @@
|
| package org.chromium.base;
|
|
|
| import android.os.Handler;
|
| -import android.os.Looper;
|
| import android.os.Message;
|
| -import android.os.MessageQueue;
|
| import android.util.Log;
|
|
|
| -import java.lang.reflect.Field;
|
| import java.lang.reflect.InvocationTargetException;
|
| import java.lang.reflect.Method;
|
|
|
| @@ -25,23 +22,31 @@ class SystemMessageHandler extends Handler {
|
| private long mMessagePumpDelegateNative = 0;
|
| private long mDelayedScheduledTimeTicks = 0;
|
|
|
| - // The following members are used to detect and trace the presence of sync
|
| - // barriers in Android's MessageQueue. Note that this detection is
|
| - // experimental, temporary and intended only for diagnostic purposes.
|
| - private MessageQueue mMessageQueue;
|
| - private Field mMessageQueueMessageField;
|
| - private Field mMessageTargetField;
|
| - private boolean mQueueHasSyncBarrier;
|
| - private long mSyncBarrierTraceId;
|
| + // Reflected API for marking a message as asynchronous. This is a workaround
|
| + // to provide fair Chromium task dispatch when served by the Android UI
|
| + // thread's Looper, avoiding stalls when the Looper has a sync barrier.
|
| + // Note: Use of this API is experimental and likely to evolve in the future.
|
| + private Method mMessageMethodSetAsynchronous;
|
|
|
| private SystemMessageHandler(long messagePumpDelegateNative) {
|
| mMessagePumpDelegateNative = messagePumpDelegateNative;
|
| - tryEnableSyncBarrierDetection();
|
| +
|
| + try {
|
| + Class<?> messageClass = Class.forName("android.os.Message");
|
| + mMessageMethodSetAsynchronous = messageClass.getMethod(
|
| + "setAsynchronous", new Class[]{boolean.class});
|
| + } catch (ClassNotFoundException e) {
|
| + Log.e(TAG, "Failed to find android.os.Message class:" + e);
|
| + } catch (NoSuchMethodException e) {
|
| + Log.e(TAG, "Failed to load Message.setAsynchronous method:" + e);
|
| + } catch (RuntimeException e) {
|
| + Log.e(TAG, "Exception while loading Message.setAsynchronous method: " + e);
|
| + }
|
| +
|
| }
|
|
|
| @Override
|
| public void handleMessage(Message msg) {
|
| - updateWhetherQueueHasBlockingSyncBarrier();
|
| if (msg.what == DELAYED_SCHEDULED_WORK) {
|
| mDelayedScheduledTimeTicks = 0;
|
| }
|
| @@ -51,9 +56,7 @@ class SystemMessageHandler extends Handler {
|
| @SuppressWarnings("unused")
|
| @CalledByNative
|
| private void scheduleWork() {
|
| - updateWhetherQueueHasBlockingSyncBarrier();
|
| - if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:immediateWorkBlocked");
|
| - sendEmptyMessage(SCHEDULED_WORK);
|
| + sendMessage(obtainAsyncMessage(SCHEDULED_WORK));
|
| }
|
|
|
| @SuppressWarnings("unused")
|
| @@ -63,97 +66,39 @@ class SystemMessageHandler extends Handler {
|
| removeMessages(DELAYED_SCHEDULED_WORK);
|
| }
|
| mDelayedScheduledTimeTicks = delayedTimeTicks;
|
| - updateWhetherQueueHasBlockingSyncBarrier();
|
| - if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:delayedWorkBlocked");
|
| - sendEmptyMessageDelayed(DELAYED_SCHEDULED_WORK, millis);
|
| + sendMessageDelayed(obtainAsyncMessage(DELAYED_SCHEDULED_WORK), millis);
|
| }
|
|
|
| @SuppressWarnings("unused")
|
| @CalledByNative
|
| private void removeAllPendingMessages() {
|
| - updateWhetherQueueHasBlockingSyncBarrier();
|
| removeMessages(SCHEDULED_WORK);
|
| removeMessages(DELAYED_SCHEDULED_WORK);
|
| }
|
|
|
| - private void updateWhetherQueueHasBlockingSyncBarrier() {
|
| - if (mMessageQueue == null) return;
|
| - // As barrier detection is only used for tracing, early out when tracing
|
| - // is disabled to avoid any potential performance penalties.
|
| - if (!TraceEvent.enabled()) {
|
| - mQueueHasSyncBarrier = false;
|
| - return;
|
| - }
|
| - Message queueHead = (Message) getField(mMessageQueue, mMessageQueueMessageField);
|
| - setqueueHasSyncBarrier(isSyncBarrierMessage(queueHead));
|
| - }
|
| -
|
| - private boolean isSyncBarrierMessage(Message message) {
|
| - if (message == null) return false;
|
| - // Sync barrier messages have null targets.
|
| - return getField(message, mMessageTargetField) == null;
|
| - }
|
| -
|
| - private void tryEnableSyncBarrierDetection() {
|
| - assert mMessageQueue == null;
|
| -
|
| - boolean success = false;
|
| - try {
|
| - Method getQueueMethod = Looper.class.getMethod("getQueue", new Class[]{});
|
| - mMessageQueue = (MessageQueue) getQueueMethod.invoke(getLooper());
|
| -
|
| - mMessageQueueMessageField = mMessageQueue.getClass().getDeclaredField("mMessages");
|
| - mMessageQueueMessageField.setAccessible(true);
|
| -
|
| - mMessageTargetField = Message.class.getDeclaredField("target");
|
| - mMessageTargetField.setAccessible(true);
|
| -
|
| - mSyncBarrierTraceId = hashCode();
|
| -
|
| - success = true;
|
| - } catch (NoSuchMethodException e) {
|
| - Log.e(TAG, "Failed to load method: " + e);
|
| - } catch (NoSuchFieldException e) {
|
| - Log.e(TAG, "Failed to load field: " + e);
|
| - } catch (InvocationTargetException e) {
|
| - Log.e(TAG, "Failed invocation: " + e);
|
| - } catch (IllegalAccessException e) {
|
| - Log.e(TAG, "Illegal access to reflected invocation: " + e);
|
| - } catch (IllegalArgumentException e) {
|
| - Log.e(TAG, "Illegal argument to reflected invocation: " + e);
|
| - } catch (RuntimeException e) {
|
| - Log.e(TAG, e.toString());
|
| - } finally {
|
| - if (!success) disableSyncBarrierDetection();
|
| - }
|
| - }
|
| -
|
| - private void disableSyncBarrierDetection() {
|
| - Log.e(TAG, "Unexpected error with sync barrier detection, disabling.");
|
| - mMessageQueue = null;
|
| - mMessageQueueMessageField = null;
|
| - mMessageTargetField = null;
|
| - setqueueHasSyncBarrier(false);
|
| - }
|
| -
|
| - private void setqueueHasSyncBarrier(boolean queueHasSyncBarrier) {
|
| - if (queueHasSyncBarrier == mQueueHasSyncBarrier) return;
|
| - mQueueHasSyncBarrier = queueHasSyncBarrier;
|
| - if (mQueueHasSyncBarrier) {
|
| - TraceEvent.startAsync("SyncBarrier", mSyncBarrierTraceId);
|
| - } else {
|
| - TraceEvent.finishAsync("SyncBarrier", mSyncBarrierTraceId);
|
| - }
|
| - }
|
| -
|
| - private Object getField(Object object, Field field) {
|
| - try {
|
| - return field.get(object);
|
| - } catch (IllegalAccessException e) {
|
| - Log.e(TAG, "Failed field access: " + e);
|
| - disableSyncBarrierDetection();
|
| + private Message obtainAsyncMessage(int what) {
|
| + Message msg = Message.obtain();
|
| + msg.what = what;
|
| + if (mMessageMethodSetAsynchronous != null) {
|
| + // If invocation fails, assume this is indicative of future
|
| + // failures, and avoid log spam by nulling the reflected method.
|
| + try {
|
| + mMessageMethodSetAsynchronous.invoke(msg, true);
|
| + } catch (IllegalAccessException e) {
|
| + Log.e(TAG, "Illegal access to asynchronous message creation, disabling.");
|
| + mMessageMethodSetAsynchronous = null;
|
| + } catch (IllegalArgumentException e) {
|
| + Log.e(TAG, "Illegal argument for asynchronous message creation, disabling.");
|
| + mMessageMethodSetAsynchronous = null;
|
| + } catch (InvocationTargetException e) {
|
| + Log.e(TAG, "Invocation exception during asynchronous message creation, disabling.");
|
| + mMessageMethodSetAsynchronous = null;
|
| + } catch (RuntimeException e) {
|
| + Log.e(TAG, "Runtime exception during asynchronous message creation, disabling.");
|
| + mMessageMethodSetAsynchronous = null;
|
| + }
|
| }
|
| - return null;
|
| + return msg;
|
| }
|
|
|
| @CalledByNative
|
|
|