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.Looper; |
8 import android.os.Message; | 9 import android.os.Message; |
| 10 import android.os.MessageQueue; |
| 11 import android.util.Log; |
| 12 |
| 13 import java.lang.reflect.Field; |
| 14 import java.lang.reflect.InvocationTargetException; |
| 15 import java.lang.reflect.Method; |
9 | 16 |
10 class SystemMessageHandler extends Handler { | 17 class SystemMessageHandler extends Handler { |
11 | 18 |
| 19 private static final String TAG = "SystemMessageHandler"; |
| 20 |
12 private static final int SCHEDULED_WORK = 1; | 21 private static final int SCHEDULED_WORK = 1; |
13 private static final int DELAYED_SCHEDULED_WORK = 2; | 22 private static final int DELAYED_SCHEDULED_WORK = 2; |
14 | 23 |
15 // Native class pointer set by the constructor of the SharedClient native cl
ass. | 24 // Native class pointer set by the constructor of the SharedClient native cl
ass. |
16 private long mMessagePumpDelegateNative = 0; | 25 private long mMessagePumpDelegateNative = 0; |
17 private long mDelayedScheduledTimeTicks = 0; | 26 private long mDelayedScheduledTimeTicks = 0; |
18 | 27 |
| 28 // The following members are used to detect and trace the presence of sync |
| 29 // barriers in Android's MessageQueue. Note that this detection is |
| 30 // experimental, temporary and intended only for diagnostic purposes. |
| 31 private MessageQueue mMessageQueue; |
| 32 private Field mMessageQueueMessageField; |
| 33 private Field mMessageTargetField; |
| 34 private boolean mQueueHasSyncBarrier; |
| 35 private long mSyncBarrierTraceId; |
| 36 |
19 private SystemMessageHandler(long messagePumpDelegateNative) { | 37 private SystemMessageHandler(long messagePumpDelegateNative) { |
20 mMessagePumpDelegateNative = messagePumpDelegateNative; | 38 mMessagePumpDelegateNative = messagePumpDelegateNative; |
21 } | 39 tryEnableSyncBarrierDetection(); |
| 40 } |
22 | 41 |
23 @Override | 42 @Override |
24 public void handleMessage(Message msg) { | 43 public void handleMessage(Message msg) { |
| 44 updateWhetherQueueHasBlockingSyncBarrier(); |
25 if (msg.what == DELAYED_SCHEDULED_WORK) { | 45 if (msg.what == DELAYED_SCHEDULED_WORK) { |
26 mDelayedScheduledTimeTicks = 0; | 46 mDelayedScheduledTimeTicks = 0; |
27 } | 47 } |
28 nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTic
ks); | 48 nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTic
ks); |
29 } | 49 } |
30 | 50 |
31 @SuppressWarnings("unused") | 51 @SuppressWarnings("unused") |
32 @CalledByNative | 52 @CalledByNative |
33 private void scheduleWork() { | 53 private void scheduleWork() { |
| 54 updateWhetherQueueHasBlockingSyncBarrier(); |
| 55 if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:immed
iateWorkBlocked"); |
34 sendEmptyMessage(SCHEDULED_WORK); | 56 sendEmptyMessage(SCHEDULED_WORK); |
35 } | 57 } |
36 | 58 |
37 @SuppressWarnings("unused") | 59 @SuppressWarnings("unused") |
38 @CalledByNative | 60 @CalledByNative |
39 private void scheduleDelayedWork(long delayedTimeTicks, long millis) { | 61 private void scheduleDelayedWork(long delayedTimeTicks, long millis) { |
40 if (mDelayedScheduledTimeTicks != 0) { | 62 if (mDelayedScheduledTimeTicks != 0) { |
41 removeMessages(DELAYED_SCHEDULED_WORK); | 63 removeMessages(DELAYED_SCHEDULED_WORK); |
42 } | 64 } |
43 mDelayedScheduledTimeTicks = delayedTimeTicks; | 65 mDelayedScheduledTimeTicks = delayedTimeTicks; |
| 66 updateWhetherQueueHasBlockingSyncBarrier(); |
| 67 if (mQueueHasSyncBarrier) TraceEvent.instant("SystemMessageHandler:delay
edWorkBlocked"); |
44 sendEmptyMessageDelayed(DELAYED_SCHEDULED_WORK, millis); | 68 sendEmptyMessageDelayed(DELAYED_SCHEDULED_WORK, millis); |
45 } | 69 } |
46 | 70 |
47 @SuppressWarnings("unused") | 71 @SuppressWarnings("unused") |
48 @CalledByNative | 72 @CalledByNative |
49 private void removeAllPendingMessages() { | 73 private void removeAllPendingMessages() { |
| 74 updateWhetherQueueHasBlockingSyncBarrier(); |
50 removeMessages(SCHEDULED_WORK); | 75 removeMessages(SCHEDULED_WORK); |
51 removeMessages(DELAYED_SCHEDULED_WORK); | 76 removeMessages(DELAYED_SCHEDULED_WORK); |
52 } | 77 } |
53 | 78 |
| 79 private void updateWhetherQueueHasBlockingSyncBarrier() { |
| 80 if (mMessageQueue == null) return; |
| 81 // As barrier detection is only used for tracing, early out when tracing |
| 82 // is disabled to avoid any potential performance penalties. |
| 83 if (!TraceEvent.enabled()) { |
| 84 mQueueHasSyncBarrier = false; |
| 85 return; |
| 86 } |
| 87 Message queueHead = (Message)getField(mMessageQueue, mMessageQueueMessag
eField); |
| 88 setqueueHasSyncBarrier(isSyncBarrierMessage(queueHead)); |
| 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 Method getQueueMethod = Looper.class.getMethod("getQueue", new Class
[]{}); |
| 103 mMessageQueue = (MessageQueue)getQueueMethod.invoke(getLooper()); |
| 104 |
| 105 mMessageQueueMessageField = mMessageQueue.getClass().getDeclaredFiel
d("mMessages"); |
| 106 mMessageQueueMessageField.setAccessible(true); |
| 107 |
| 108 mMessageTargetField = Message.class.getDeclaredField("target"); |
| 109 mMessageTargetField.setAccessible(true); |
| 110 |
| 111 mSyncBarrierTraceId = hashCode(); |
| 112 |
| 113 success = true; |
| 114 } catch (NoSuchMethodException e) { |
| 115 Log.e(TAG, "Failed to load method: " + e); |
| 116 } catch (NoSuchFieldException e) { |
| 117 Log.e(TAG, "Failed to load field: " + e); |
| 118 } catch (InvocationTargetException e) { |
| 119 Log.e(TAG, "Failed invocation: " + e); |
| 120 } catch (IllegalAccessException e) { |
| 121 Log.e(TAG, "Illegal access to reflected invocation: " + e); |
| 122 } catch (IllegalArgumentException e) { |
| 123 Log.e(TAG, "Illegal argument to reflected invocation: " + e); |
| 124 } catch (RuntimeException e) { |
| 125 Log.e(TAG, e.toString()); |
| 126 } finally { |
| 127 if (!success) disableSyncBarrierDetection(); |
| 128 } |
| 129 } |
| 130 |
| 131 private void disableSyncBarrierDetection() { |
| 132 Log.e(TAG, "Unexpected error with sync barrier detection, disabling."); |
| 133 mMessageQueue = null; |
| 134 mMessageQueueMessageField = null; |
| 135 mMessageTargetField = null; |
| 136 setqueueHasSyncBarrier(false); |
| 137 } |
| 138 |
| 139 private void setqueueHasSyncBarrier(boolean queueHasSyncBarrier) { |
| 140 if (queueHasSyncBarrier == mQueueHasSyncBarrier) return; |
| 141 mQueueHasSyncBarrier = queueHasSyncBarrier; |
| 142 if (mQueueHasSyncBarrier) { |
| 143 TraceEvent.startAsync("SyncBarrier", mSyncBarrierTraceId); |
| 144 } else { |
| 145 TraceEvent.finishAsync("SyncBarrier", mSyncBarrierTraceId); |
| 146 } |
| 147 } |
| 148 |
| 149 private Object getField(Object object, Field field) { |
| 150 try { |
| 151 return field.get(object); |
| 152 } catch (IllegalAccessException e) { |
| 153 Log.e(TAG, "Failed field access: " + e); |
| 154 disableSyncBarrierDetection(); |
| 155 } |
| 156 return null; |
| 157 } |
| 158 |
54 @CalledByNative | 159 @CalledByNative |
55 private static SystemMessageHandler create(long messagePumpDelegateNative) { | 160 private static SystemMessageHandler create(long messagePumpDelegateNative) { |
56 return new SystemMessageHandler(messagePumpDelegateNative); | 161 return new SystemMessageHandler(messagePumpDelegateNative); |
57 } | 162 } |
58 | 163 |
59 private native void nativeDoRunLoopOnce( | 164 private native void nativeDoRunLoopOnce( |
60 long messagePumpDelegateNative, long delayedScheduledTimeTicks); | 165 long messagePumpDelegateNative, long delayedScheduledTimeTicks); |
61 } | 166 } |
OLD | NEW |