Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b95efc2c authored by Mark Fasheh's avatar Mark Fasheh
Browse files

MessageStack: add quitting sentinel node

We use the timestamp of the quitting message to tell
DeliQueue whether it should remove all messages or just
remove messages past a given time ("removeAllFutureMessages").

Test: atest android.os.MessageStackTest
Bug: 421623328
Flag: EXEMPT new data structure isn't used yet; usages will be flagged.
Change-Id: I7caf36e9a5fc1efec1be7f733b4b2d7f2f176f92
parent ab70951a
Loading
Loading
Loading
Loading
+61 −4
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ public final class MessageStack {
    private final MessageHeap mAsyncHeap = new MessageHeap();

    // This points to the most-recently processed message. Comparison with mTopValue will indicate
    // whether some messages still need to be processed.
    // whether some messages still need to be processed. This value excludes the quitting sentinel.
    private Message mLooperProcessed = null;

    static {
@@ -56,17 +56,65 @@ public final class MessageStack {
        }
    }

    private static final Object QUITTING_NODE_OBJ = new Object();

    private boolean isQuittingMessage(Message m) {
        return m != null && m.obj == QUITTING_NODE_OBJ;
    }

    /**
     * Pushes a message onto the top of the stack with a CAS.
     * @return true if successfully pushed; false if the stack is quitting.
     */
    public void pushMessage(Message m) {
        // TODO: This should fail if the current top value is the shutdown sentinel.
    public boolean pushMessage(Message m) {
        Message current;
        do {
            current = mTopValue;
            if (isQuittingMessage(current)) {
                return false;
            }
            m.next = current;
        } while (!sTop.weakCompareAndSetRelease(this, current, m));
        return true;
    }

    /**
     * Pushes a quitting message onto the top of the stack.
     * After this call no more messages can be pushed onto the stack.
     * @return true if pushed, false if there was already a quitting message
     */
    public boolean pushQuitting(long when) {
        Message quittingMsg = Message.obtain();
        quittingMsg.obj = QUITTING_NODE_OBJ;
        quittingMsg.when = when;
        /* Should never go into the heap, initialize idx to an impossible value */
        quittingMsg.heapIndex = -1;
        final boolean ret = pushMessage(quittingMsg);
        if (!ret) {
            quittingMsg.recycleUnchecked();
        }

        return ret;
    }

    /**
     * Query if we are in a quitting state.
     * @return true if we have a quitting message on top of the stack.
     */
    public boolean isQuitting() {
        return isQuittingMessage((Message) sTop.getAcquire(this));
    }

    /**
     * Gets timestamp of quitting message.
     * @return timestamp, or throws an exception if no quitting message exists.
     */
    public long getQuittingTimestamp() throws IllegalStateException {
        Message m = (Message) sTop.getAcquire(this);
        if (!isQuittingMessage(m)) {
            throw new IllegalStateException();
        }
        return m.when;
    }

    /**
@@ -80,7 +128,8 @@ public final class MessageStack {

        while (current != null) {
            // Check that the message hasn't already been removed or processed elsewhere.
            if (!current.isRemoved()
            if (!isQuittingMessage(current)
                    && !current.isRemoved()
                    && compare.compareMessage(current, h, what, object, r, when)
                    && current.markRemoved()) {
                if (firstRemoved == null) {
@@ -107,6 +156,14 @@ public final class MessageStack {
     */
    public void heapSweep() {
        Message current = (Message) sTop.getAcquire(this);

        if (current != null && isQuittingMessage(current)) {
            if (current.next != null) {
                current.next.prev = current;
            }
            current = current.next;
        }

        Message prevLooperProcessed = mLooperProcessed;
        mLooperProcessed = current;

+20 −0
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@
package android.os;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import androidx.test.runner.AndroidJUnit4;

@@ -45,6 +47,24 @@ public final class MessageStackTest {
        assertEquals(10, stack.sizeForTest());
    }

    /**
     * Verify quitting state
     */
    @Test
    public void testQuitting() {
        MessageStack stack = new MessageStack();
        for (int i = 0; i < 10; i++) {
            stack.pushMessage(new Message());
        }

        assertFalse(stack.isQuitting());

        stack.pushQuitting(42);
        assertFalse(stack.pushMessage(new Message()));
        assertTrue(stack.isQuitting());
        assertEquals(stack.getQuittingTimestamp(), 42);
    }

    /**
     * Verify heap size after sweeping messages from a MessageStack.
     */