Skip to content

Commit e799cb7

Browse files
author
Jeff Brown
committed
Add a barrier mechanism to the MessageQueue.
The synchronization barrier enables selectively blocking execution of synchronous messages until the barrier is released. Asynchronous messages may continue running in the meantime. The barrier is intended to be used to implement more sophisticated scheduling policies related to view hierarchy traversals. While traversals are pending, most messages posted to the message queue must be held up. This is to satisfy the invariant that traversals will occur before subsequently posted messages are handled. The exception to this rule are "asynchronous" messages that represent external events or interrupts that come from other components such as VSYNC pulses, input events or sensor events. Because these messages are typically delivered at arbitrary times, they are independent of traversals or other typical synchronization boundaries. Messages can now be flagged as asynchronous to indicate that they are weakly ordered. Bug: 5721047 Change-Id: I1446dcfbc896f33b48355adc28967ace8c8c9b9b
1 parent 91e3289 commit e799cb7

File tree

2 files changed

+136
-18
lines changed

2 files changed

+136
-18
lines changed

core/java/android/os/Message.java

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,13 @@ public final class Message implements Parcelable {
7575
public Messenger replyTo;
7676

7777
/** If set message is in use */
78-
/*package*/ static final int FLAG_IN_USE = 1;
78+
/*package*/ static final int FLAG_IN_USE = 1 << 0;
7979

80-
/** Flags reserved for future use (All are reserved for now) */
81-
/*package*/ static final int FLAGS_RESERVED = ~FLAG_IN_USE;
80+
/** If set message is asynchronous */
81+
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
8282

8383
/** Flags to clear in the copyFrom method */
84-
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAGS_RESERVED | FLAG_IN_USE;
84+
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
8585

8686
/*package*/ int flags;
8787

@@ -363,6 +363,48 @@ public void sendToTarget() {
363363
target.sendMessage(this);
364364
}
365365

366+
/**
367+
* Returns true if the message is asynchronous.
368+
*
369+
* Asynchronous messages represent interrupts or events that do not require global ordering
370+
* with represent to synchronous messages. Asynchronous messages are not subject to
371+
* the synchronization barriers introduced by {@link MessageQueue#acquireSyncBarrier()}.
372+
*
373+
* @return True if the message is asynchronous.
374+
*
375+
* @see #setAsynchronous(boolean)
376+
* @see MessageQueue#acquireSyncBarrier()
377+
* @see MessageQueue#releaseSyncBarrier()
378+
*
379+
* @hide
380+
*/
381+
public boolean isAsynchronous() {
382+
return (flags & FLAG_ASYNCHRONOUS) != 0;
383+
}
384+
385+
/**
386+
* Sets whether the message is asynchronous.
387+
*
388+
* Asynchronous messages represent interrupts or events that do not require global ordering
389+
* with represent to synchronous messages. Asynchronous messages are not subject to
390+
* the synchronization barriers introduced by {@link MessageQueue#acquireSyncBarrier()}.
391+
*
392+
* @param async True if the message is asynchronous.
393+
*
394+
* @see #isAsynchronous()
395+
* @see MessageQueue#acquireSyncBarrier()
396+
* @see MessageQueue#releaseSyncBarrier()
397+
*
398+
* @hide
399+
*/
400+
public void setAsynchronous(boolean async) {
401+
if (async) {
402+
flags |= FLAG_ASYNCHRONOUS;
403+
} else {
404+
flags &= ~FLAG_ASYNCHRONOUS;
405+
}
406+
}
407+
366408
/*package*/ void clearForRecycle() {
367409
flags = 0;
368410
what = 0;

core/java/android/os/MessageQueue.java

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public class MessageQueue {
3939
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
4040
private boolean mBlocked;
4141

42+
// Indicates the barrier nesting level.
43+
private int mBarrierNestCount;
44+
4245
@SuppressWarnings("unused")
4346
private int mPtr; // used by native code
4447

@@ -93,7 +96,53 @@ public final void removeIdleHandler(IdleHandler handler) {
9396
mIdleHandlers.remove(handler);
9497
}
9598
}
96-
99+
100+
/**
101+
* Acquires a synchronization barrier.
102+
*
103+
* While a synchronization barrier is active, only asynchronous messages are
104+
* permitted to execute. Synchronous messages are retained but are not executed
105+
* until the synchronization barrier is released.
106+
*
107+
* This method is used to immediately postpone execution of all synchronous messages
108+
* until a condition is met that releases the barrier. Asynchronous messages are
109+
* exempt from the barrier and continue to be executed as usual.
110+
*
111+
* This call nests and must be matched by an equal number of calls to
112+
* {@link #releaseSyncBarrier}.
113+
*
114+
* @hide
115+
*/
116+
public final void acquireSyncBarrier() {
117+
synchronized (this) {
118+
mBarrierNestCount += 1;
119+
}
120+
}
121+
122+
/**
123+
* Releases a synchronization barrier.
124+
*
125+
* This class undoes one invocation of {@link #acquireSyncBarrier}.
126+
*
127+
* @throws IllegalStateException if the barrier is not acquired.
128+
*
129+
* @hide
130+
*/
131+
public final void releaseSyncBarrier() {
132+
synchronized (this) {
133+
if (mBarrierNestCount == 0) {
134+
throw new IllegalStateException("The message queue synchronization barrier "
135+
+ "has not been acquired.");
136+
}
137+
138+
mBarrierNestCount -= 1;
139+
if (!mBlocked || mMessages == null) {
140+
return;
141+
}
142+
}
143+
nativeWake(mPtr);
144+
}
145+
97146
MessageQueue() {
98147
nativeInit();
99148
}
@@ -120,28 +169,49 @@ final Message next() {
120169
synchronized (this) {
121170
// Try to retrieve the next message. Return if found.
122171
final long now = SystemClock.uptimeMillis();
123-
final Message msg = mMessages;
124-
if (msg != null) {
172+
173+
Message prevMsg = null;
174+
Message msg = mMessages;
175+
for (;;) {
176+
if (msg == null) {
177+
// No more messages.
178+
nextPollTimeoutMillis = -1;
179+
break;
180+
}
181+
125182
final long when = msg.when;
126-
if (now >= when) {
183+
if (now < when) {
184+
// Next message is not ready. Set a timeout to wake up when it is ready.
185+
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
186+
break;
187+
}
188+
189+
if (mBarrierNestCount == 0 || msg.isAsynchronous()) {
190+
// Got a message.
127191
mBlocked = false;
128-
mMessages = msg.next;
192+
if (prevMsg != null) {
193+
prevMsg.next = msg.next;
194+
} else {
195+
mMessages = msg.next;
196+
}
129197
msg.next = null;
130198
if (false) Log.v("MessageQueue", "Returning message: " + msg);
131199
msg.markInUse();
132200
return msg;
133-
} else {
134-
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
135201
}
136-
} else {
137-
nextPollTimeoutMillis = -1;
202+
203+
// We have a message that we could return except that it is
204+
// blocked by the sync barrier. In particular, this means that
205+
// we are not idle yet, so we do not want to run the idle handlers.
206+
prevMsg = msg;
207+
msg = msg.next;
138208
}
139209

140-
// If first time, then get the number of idlers to run.
141-
if (pendingIdleHandlerCount < 0) {
210+
// If first time idle, then get the number of idlers to run.
211+
if (pendingIdleHandlerCount < 0 && msg == mMessages) {
142212
pendingIdleHandlerCount = mIdleHandlers.size();
143213
}
144-
if (pendingIdleHandlerCount == 0) {
214+
if (pendingIdleHandlerCount <= 0) {
145215
// No idle handlers to run. Loop and wait some more.
146216
mBlocked = true;
147217
continue;
@@ -205,18 +275,24 @@ final boolean enqueueMessage(Message msg, long when) {
205275
//Log.d("MessageQueue", "Enqueing: " + msg);
206276
Message p = mMessages;
207277
if (p == null || when == 0 || when < p.when) {
278+
// New head, wake up the event queue if blocked.
208279
msg.next = p;
209280
mMessages = msg;
210-
needWake = mBlocked; // new head, might need to wake up
281+
needWake = mBlocked;
211282
} else {
283+
// Inserted within the middle of the queue. Usually we don't have to wake
284+
// up the event queue unless the message is asynchronous and it might be
285+
// possible for it to be returned out of sequence relative to an earlier
286+
// synchronous message at the head of the queue.
212287
Message prev = null;
213288
while (p != null && p.when <= when) {
214289
prev = p;
215290
p = p.next;
216291
}
217292
msg.next = prev.next;
218293
prev.next = msg;
219-
needWake = false; // still waiting on head, no need to wake up
294+
needWake = mBlocked && mBarrierNestCount != 0 && msg.isAsynchronous()
295+
&& !mMessages.isAsynchronous();
220296
}
221297
}
222298
if (needWake) {

0 commit comments

Comments
 (0)