| 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 e1fbb0f4bed9538212313fdb374e81f0ac37f414..a3c81ae1046d50aa368a613ee63320700227c84e 100644
|
| --- a/base/android/java/src/org/chromium/base/SystemMessageHandler.java
|
| +++ b/base/android/java/src/org/chromium/base/SystemMessageHandler.java
|
| @@ -5,10 +5,19 @@
|
| 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;
|
|
|
| class SystemMessageHandler extends Handler {
|
|
|
| + private static final String TAG = "SystemMessageHandler";
|
| +
|
| private static final int SCHEDULED_WORK = 1;
|
| private static final int DELAYED_SCHEDULED_WORK = 2;
|
|
|
| @@ -16,12 +25,23 @@ 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;
|
| +
|
| private SystemMessageHandler(long messagePumpDelegateNative) {
|
| mMessagePumpDelegateNative = messagePumpDelegateNative;
|
| - }
|
| + tryEnableSyncBarrierDetection();
|
| + }
|
|
|
| @Override
|
| public void handleMessage(Message msg) {
|
| + updateWhetherQueueHasBlockingSyncBarrier();
|
| if (msg.what == DELAYED_SCHEDULED_WORK) {
|
| mDelayedScheduledTimeTicks = 0;
|
| }
|
| @@ -31,6 +51,8 @@ class SystemMessageHandler extends Handler {
|
| @SuppressWarnings("unused")
|
| @CalledByNative
|
| private void scheduleWork() {
|
| + updateWhetherQueueHasBlockingSyncBarrier();
|
| + if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:immediateWorkBlocked");
|
| sendEmptyMessage(SCHEDULED_WORK);
|
| }
|
|
|
| @@ -41,16 +63,99 @@ class SystemMessageHandler extends Handler {
|
| removeMessages(DELAYED_SCHEDULED_WORK);
|
| }
|
| mDelayedScheduledTimeTicks = delayedTimeTicks;
|
| + updateWhetherQueueHasBlockingSyncBarrier();
|
| + if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:delayedWorkBlocked");
|
| sendEmptyMessageDelayed(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();
|
| + }
|
| + return null;
|
| + }
|
| +
|
| @CalledByNative
|
| private static SystemMessageHandler create(long messagePumpDelegateNative) {
|
| return new SystemMessageHandler(messagePumpDelegateNative);
|
|
|