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 private MessageQueue mMessageQueue; | |
28 private Field mMessageQueueMessageField; | |
29 private Field mMessageTargetField; | |
30 | |
31 private final long mSyncBarrierTraceId; | |
32 private boolean mQueueHasBarrier; | |
33 | |
19 private SystemMessageHandler(long messagePumpDelegateNative) { | 34 private SystemMessageHandler(long messagePumpDelegateNative) { |
20 mMessagePumpDelegateNative = messagePumpDelegateNative; | 35 mMessagePumpDelegateNative = messagePumpDelegateNative; |
21 } | 36 mSyncBarrierTraceId = hashCode(); |
37 tryEnableSyncBarrierDetection(); | |
38 } | |
22 | 39 |
23 @Override | 40 @Override |
24 public void handleMessage(Message msg) { | 41 public void handleMessage(Message msg) { |
42 updateWhetherQueueHasBlockingSyncBarrier(); | |
25 if (msg.what == DELAYED_SCHEDULED_WORK) { | 43 if (msg.what == DELAYED_SCHEDULED_WORK) { |
26 mDelayedScheduledTimeTicks = 0; | 44 mDelayedScheduledTimeTicks = 0; |
27 } | 45 } |
28 nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTic ks); | 46 nativeDoRunLoopOnce(mMessagePumpDelegateNative, mDelayedScheduledTimeTic ks); |
29 } | 47 } |
30 | 48 |
31 @SuppressWarnings("unused") | 49 @SuppressWarnings("unused") |
32 @CalledByNative | 50 @CalledByNative |
33 private void scheduleWork() { | 51 private void scheduleWork() { |
52 updateWhetherQueueHasBlockingSyncBarrier(); | |
53 if (mQueueHasBarrier) TraceEvent.instant("SystemMessageHandler:immediate WorkBlocked"); | |
34 sendEmptyMessage(SCHEDULED_WORK); | 54 sendEmptyMessage(SCHEDULED_WORK); |
35 } | 55 } |
36 | 56 |
37 @SuppressWarnings("unused") | 57 @SuppressWarnings("unused") |
38 @CalledByNative | 58 @CalledByNative |
39 private void scheduleDelayedWork(long delayedTimeTicks, long millis) { | 59 private void scheduleDelayedWork(long delayedTimeTicks, long millis) { |
40 if (mDelayedScheduledTimeTicks != 0) { | 60 if (mDelayedScheduledTimeTicks != 0) { |
41 removeMessages(DELAYED_SCHEDULED_WORK); | 61 removeMessages(DELAYED_SCHEDULED_WORK); |
42 } | 62 } |
43 mDelayedScheduledTimeTicks = delayedTimeTicks; | 63 mDelayedScheduledTimeTicks = delayedTimeTicks; |
64 updateWhetherQueueHasBlockingSyncBarrier(); | |
65 if (mQueueHasBarrier) TraceEvent.instant("SystemMessageHandler:delayedWo rkBlocked"); | |
44 sendEmptyMessageDelayed(DELAYED_SCHEDULED_WORK, millis); | 66 sendEmptyMessageDelayed(DELAYED_SCHEDULED_WORK, millis); |
45 } | 67 } |
46 | 68 |
47 @SuppressWarnings("unused") | 69 @SuppressWarnings("unused") |
48 @CalledByNative | 70 @CalledByNative |
49 private void removeAllPendingMessages() { | 71 private void removeAllPendingMessages() { |
72 updateWhetherQueueHasBlockingSyncBarrier(); | |
50 removeMessages(SCHEDULED_WORK); | 73 removeMessages(SCHEDULED_WORK); |
51 removeMessages(DELAYED_SCHEDULED_WORK); | 74 removeMessages(DELAYED_SCHEDULED_WORK); |
52 } | 75 } |
53 | 76 |
77 private void updateWhetherQueueHasBlockingSyncBarrier() { | |
epennerAtGoogle
2014/09/22 21:26:48
If there is any cost to analyzing a message, we co
jdduke (slow)
2014/09/22 21:32:57
Ohh, I like that, didn't realize that was exposed
| |
78 if (mMessageQueue == null) return; | |
79 final Message queueHead = (Message)getField(mMessageQueue, mMessageQueue MessageField); | |
80 final boolean queueHasBarrier = isBarrierMessage(queueHead); | |
81 setQueueHasBarrier(queueHasBarrier); | |
82 } | |
83 | |
84 private boolean isBarrierMessage(Message message) { | |
85 if (message == null) return false; | |
86 // Sync barrier messages have null targets. | |
87 return getField(message, mMessageTargetField) == null; | |
88 } | |
89 | |
90 private void tryEnableSyncBarrierDetection() { | |
91 boolean success = false; | |
92 try { | |
93 Class<?> looperClass = Class.forName("android.os.Looper"); | |
94 Method getQueueMethod = looperClass.getMethod("getQueue", new Class[ ]{}); | |
95 mMessageQueue = (MessageQueue)(getQueueMethod.invoke(getLooper())); | |
96 | |
97 mMessageQueueMessageField = mMessageQueue.getClass().getDeclaredFiel d("mMessages"); | |
98 mMessageQueueMessageField.setAccessible(true); | |
99 | |
100 mMessageTargetField = new Message().getClass().getDeclaredField("tar get"); | |
101 mMessageTargetField.setAccessible(true); | |
102 | |
103 success = true; | |
104 } catch (ClassNotFoundException e) { | |
105 Log.e(TAG, "Failed to find class: " + e); | |
106 } catch (NoSuchMethodException e) { | |
107 Log.e(TAG, "Failed to load method: " + e); | |
108 } catch (NoSuchFieldException e) { | |
109 Log.e(TAG, "Failed to load field: " + e); | |
110 } catch (InvocationTargetException e) { | |
111 Log.e(TAG, "Failed invocation: " + e); | |
112 } catch (IllegalAccessException e) { | |
113 Log.e(TAG, "Illegal access to reflected invocation: " + e); | |
114 } catch (RuntimeException e) { | |
115 Log.e(TAG, e.toString()); | |
116 } finally { | |
117 if (!success) disableSyncBarrierDetection(); | |
118 } | |
119 } | |
120 | |
121 private void disableSyncBarrierDetection() { | |
122 Log.e(TAG, "Unexpected error with sync barrier detection, disabling."); | |
123 mMessageQueue = null; | |
124 mMessageQueueMessageField = null; | |
125 mMessageTargetField = null; | |
126 setQueueHasBarrier(false); | |
127 } | |
128 | |
129 private void setQueueHasBarrier(boolean queueHasBarrier) { | |
130 if (queueHasBarrier == mQueueHasBarrier) return; | |
131 mQueueHasBarrier = queueHasBarrier; | |
132 if (mQueueHasBarrier) { | |
133 TraceEvent.startAsync("SyncBarrier", mSyncBarrierTraceId); | |
134 } else { | |
135 TraceEvent.finishAsync("SyncBarrier", mSyncBarrierTraceId); | |
136 } | |
137 } | |
138 | |
139 private Object getField(Object object, Field field) { | |
140 try { | |
141 return field.get(object); | |
142 } catch (IllegalAccessException e) { | |
143 Log.e(TAG, "Failed field access: " + e); | |
144 disableSyncBarrierDetection(); | |
145 } | |
146 return null; | |
147 } | |
148 | |
54 @CalledByNative | 149 @CalledByNative |
55 private static SystemMessageHandler create(long messagePumpDelegateNative) { | 150 private static SystemMessageHandler create(long messagePumpDelegateNative) { |
56 return new SystemMessageHandler(messagePumpDelegateNative); | 151 return new SystemMessageHandler(messagePumpDelegateNative); |
57 } | 152 } |
58 | 153 |
59 private native void nativeDoRunLoopOnce( | 154 private native void nativeDoRunLoopOnce( |
60 long messagePumpDelegateNative, long delayedScheduledTimeTicks); | 155 long messagePumpDelegateNative, long delayedScheduledTimeTicks); |
61 } | 156 } |
OLD | NEW |