Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.base; | 5 package org.chromium.base; |
| 6 | 6 |
| 7 import android.os.Handler; | 7 import android.os.Handler; |
| 8 import android.os.Message; | 8 import android.os.Message; |
| 9 import android.os.MessageQueue; | |
| 10 import android.util.Log; | |
| 11 | |
| 12 import java.lang.reflect.Field; | |
| 13 import java.lang.reflect.InvocationTargetException; | |
| 14 import java.lang.reflect.Method; | |
| 9 | 15 |
| 10 class SystemMessageHandler extends Handler { | 16 class SystemMessageHandler extends Handler { |
| 11 | 17 |
| 18 private static final String TAG = "SystemMessageHandler"; | |
| 19 | |
| 12 private static final int SCHEDULED_WORK = 1; | 20 private static final int SCHEDULED_WORK = 1; |
| 13 private static final int DELAYED_SCHEDULED_WORK = 2; | 21 private static final int DELAYED_SCHEDULED_WORK = 2; |
| 14 | 22 |
| 15 // Native class pointer set by the constructor of the SharedClient native cl ass. | 23 // Native class pointer set by the constructor of the SharedClient native cl ass. |
| 16 private long mMessagePumpDelegateNative = 0; | 24 private long mMessagePumpDelegateNative = 0; |
| 17 private long mDelayedScheduledTimeTicks = 0; | 25 private long mDelayedScheduledTimeTicks = 0; |
| 18 | 26 |
| 27 // The following members are used to detect and trace the presence of sync | |
| 28 // barriers in Android's MessageQueue. Note that this detection is | |
| 29 // experimental, temporary and intended only for diagnostic purposes. | |
| 30 private MessageQueue mMessageQueue; | |
| 31 private Field mMessageQueueMessageField; | |
| 32 private Field mMessageTargetField; | |
| 33 private boolean mQueueHasSyncBarrier; | |
| 34 private long mSyncBarrierTraceId; | |
| 35 | |
| 19 private SystemMessageHandler(long messagePumpDelegateNative) { | 36 private SystemMessageHandler(long messagePumpDelegateNative) { |
| 20 mMessagePumpDelegateNative = messagePumpDelegateNative; | 37 mMessagePumpDelegateNative = messagePumpDelegateNative; |
| 21 } | 38 tryEnableSyncBarrierDetection(); |
| 39 } | |
| 22 | 40 |
| 23 @Override | 41 @Override |
| 24 public void handleMessage(Message msg) { | 42 public void handleMessage(Message msg) { |
| 43 updateWhetherQueueHasBlockingSyncBarrier(); | |
| 25 if (msg.what == DELAYED_SCHEDULED_WORK) { | 44 if (msg.what == DELAYED_SCHEDULED_WORK) { |
| 26 mDelayedScheduledTimeTicks = 0; | 45 mDelayedScheduledTimeTicks = 0; |
| 27 } | 46 } |
| 28 nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTic ks); | 47 nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTic ks); |
| 29 } | 48 } |
| 30 | 49 |
| 31 @SuppressWarnings("unused") | 50 @SuppressWarnings("unused") |
| 32 @CalledByNative | 51 @CalledByNative |
| 33 private void scheduleWork() { | 52 private void scheduleWork() { |
| 53 updateWhetherQueueHasBlockingSyncBarrier(); | |
| 54 if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:immed iateWorkBlocked"); | |
| 34 sendEmptyMessage(SCHEDULED_WORK); | 55 sendEmptyMessage(SCHEDULED_WORK); |
| 35 } | 56 } |
| 36 | 57 |
| 37 @SuppressWarnings("unused") | 58 @SuppressWarnings("unused") |
| 38 @CalledByNative | 59 @CalledByNative |
| 39 private void scheduleDelayedWork(long delayedTimeTicks, long millis) { | 60 private void scheduleDelayedWork(long delayedTimeTicks, long millis) { |
| 40 if (mDelayedScheduledTimeTicks != 0) { | 61 if (mDelayedScheduledTimeTicks != 0) { |
| 41 removeMessages(DELAYED_SCHEDULED_WORK); | 62 removeMessages(DELAYED_SCHEDULED_WORK); |
| 42 } | 63 } |
| 43 mDelayedScheduledTimeTicks = delayedTimeTicks; | 64 mDelayedScheduledTimeTicks = delayedTimeTicks; |
| 65 updateWhetherQueueHasBlockingSyncBarrier(); | |
| 66 if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:delay edWorkBlocked"); | |
| 44 sendEmptyMessageDelayed(DELAYED_SCHEDULED_WORK, millis); | 67 sendEmptyMessageDelayed(DELAYED_SCHEDULED_WORK, millis); |
| 45 } | 68 } |
| 46 | 69 |
| 47 @SuppressWarnings("unused") | 70 @SuppressWarnings("unused") |
| 48 @CalledByNative | 71 @CalledByNative |
| 49 private void removeAllPendingMessages() { | 72 private void removeAllPendingMessages() { |
| 73 updateWhetherQueueHasBlockingSyncBarrier(); | |
| 50 removeMessages(SCHEDULED_WORK); | 74 removeMessages(SCHEDULED_WORK); |
| 51 removeMessages(DELAYED_SCHEDULED_WORK); | 75 removeMessages(DELAYED_SCHEDULED_WORK); |
| 52 } | 76 } |
| 53 | 77 |
| 78 private void updateWhetherQueueHasBlockingSyncBarrier() { | |
| 79 if (mMessageQueue == null) return; | |
| 80 // As barrier detection is only used for tracing, early out when tracing | |
| 81 // is disabled to avoid any potential performance penalties. | |
| 82 if (!TraceEvent.enabled()) { | |
| 83 mQueueHasSyncBarrier = false; | |
| 84 return; | |
| 85 } | |
| 86 final Message queueHead = (Message)getField(mMessageQueue, mMessageQueue MessageField); | |
|
nyquist
2014/09/23 00:08:39
These final qualifiers are unnecessary I believe.
jdduke (slow)
2014/09/23 16:20:46
Done.
| |
| 87 final boolean queueHasSyncbarrier = isSyncBarrierMessage(queueHead); | |
|
nyquist
2014/09/23 00:08:38
queueHasSyncBarrier
jdduke (slow)
2014/09/23 16:20:46
Done.
| |
| 88 setQueueHasSyncBarrier(queueHasSyncbarrier); | |
| 89 } | |
| 90 | |
| 91 private boolean isSyncBarrierMessage(Message message) { | |
| 92 if (message == null) return false; | |
| 93 // Sync barrier messages have null targets. | |
| 94 return getField(message, mMessageTargetField) == null; | |
| 95 } | |
| 96 | |
| 97 private void tryEnableSyncBarrierDetection() { | |
| 98 assert mMessageQueue == null; | |
| 99 | |
| 100 boolean success = false; | |
| 101 try { | |
| 102 Class<?> looperClass = Class.forName("android.os.Looper"); | |
| 103 Method getQueueMethod = looperClass.getMethod("getQueue", new Class[ ]{}); | |
| 104 mMessageQueue = (MessageQueue)(getQueueMethod.invoke(getLooper())); | |
|
nyquist
2014/09/23 00:08:38
Just (MessageQueue) getQueueMethod.invoke(getLoope
jdduke (slow)
2014/09/23 16:20:46
Done.
| |
| 105 | |
| 106 mMessageQueueMessageField = mMessageQueue.getClass().getDeclaredFiel d("mMessages"); | |
| 107 mMessageQueueMessageField.setAccessible(true); | |
| 108 | |
| 109 mMessageTargetField = new Message().getClass().getDeclaredField("tar get"); | |
|
nyquist
2014/09/23 00:08:38
Could this just be Message.class.getDeclaredField(
jdduke (slow)
2014/09/23 16:20:46
Done.
| |
| 110 mMessageTargetField.setAccessible(true); | |
| 111 | |
| 112 mSyncBarrierTraceId = hashCode(); | |
| 113 | |
| 114 success = true; | |
| 115 } catch (ClassNotFoundException e) { | |
| 116 Log.e(TAG, "Failed to find class: " + e); | |
| 117 } catch (NoSuchMethodException e) { | |
| 118 Log.e(TAG, "Failed to load method: " + e); | |
| 119 } catch (NoSuchFieldException e) { | |
| 120 Log.e(TAG, "Failed to load field: " + e); | |
| 121 } catch (InvocationTargetException e) { | |
| 122 Log.e(TAG, "Failed invocation: " + e); | |
| 123 } catch (IllegalAccessException e) { | |
| 124 Log.e(TAG, "Illegal access to reflected invocation: " + e); | |
| 125 } catch (RuntimeException e) { | |
|
nyquist
2014/09/23 00:08:39
This is masking IllegalArgumentException thrown by
jdduke (slow)
2014/09/23 16:20:46
Not intentional, good catch.
| |
| 126 Log.e(TAG, e.toString()); | |
| 127 } finally { | |
| 128 if (!success) disableSyncBarrierDetection(); | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 private void disableSyncBarrierDetection() { | |
| 133 Log.e(TAG, "Unexpected error with sync barrier detection, disabling."); | |
| 134 mMessageQueue = null; | |
| 135 mMessageQueueMessageField = null; | |
| 136 mMessageTargetField = null; | |
| 137 setQueueHasSyncBarrier(false); | |
| 138 } | |
| 139 | |
| 140 private void setQueueHasSyncBarrier(boolean queueHasSyncbarrier) { | |
|
nyquist
2014/09/23 00:08:39
queueHasSyncBarrier
jdduke (slow)
2014/09/23 16:20:46
Done.
| |
| 141 if (queueHasSyncbarrier == mQueueHasSyncBarrier) return; | |
| 142 mQueueHasSyncBarrier = queueHasSyncbarrier; | |
| 143 if (mQueueHasSyncBarrier) { | |
| 144 TraceEvent.startAsync("SyncBarrier", mSyncBarrierTraceId); | |
| 145 } else { | |
| 146 TraceEvent.finishAsync("SyncBarrier", mSyncBarrierTraceId); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 private Object getField(Object object, Field field) { | |
| 151 try { | |
| 152 return field.get(object); | |
| 153 } catch (IllegalAccessException e) { | |
| 154 Log.e(TAG, "Failed field access: " + e); | |
| 155 disableSyncBarrierDetection(); | |
| 156 } | |
| 157 return null; | |
| 158 } | |
| 159 | |
| 54 @CalledByNative | 160 @CalledByNative |
| 55 private static SystemMessageHandler create(long messagePumpDelegateNative) { | 161 private static SystemMessageHandler create(long messagePumpDelegateNative) { |
| 56 return new SystemMessageHandler(messagePumpDelegateNative); | 162 return new SystemMessageHandler(messagePumpDelegateNative); |
| 57 } | 163 } |
| 58 | 164 |
| 59 private native void nativeDoRunLoopOnce( | 165 private native void nativeDoRunLoopOnce( |
| 60 long messagePumpDelegateNative, long delayedScheduledTimeTicks); | 166 long messagePumpDelegateNative, long delayedScheduledTimeTicks); |
| 61 } | 167 } |
| OLD | NEW |