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 |