Skip to content

Commit 472ea60

Browse files
Jeff BrownAndroid (Google) Code Review
authored andcommitted
Merge "Add a barrier mechanism to the MessageQueue."
2 parents 5dd53e1 + e799cb7 commit 472ea60

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)