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); |