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

Commit 315db32e authored by Jeff DeCew's avatar Jeff DeCew
Browse files

Add CopyOnLoopListenerSet which may be faster

Bug: 341479400
Test: atest SystemUITests
Flag: EXEMPT bugfix / unused utility
Change-Id: Ice43df54311aa1d045e1f007f92c65b95cf38196
parent 45aaef93
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.util

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class CopyOnLoopListenerSetTest : ListenerSetTest() {
    override fun makeRunnableListenerSet(): IListenerSet<Runnable> = CopyOnLoopListenerSet()
}
+21 −26
Original line number Diff line number Diff line
@@ -168,15 +168,14 @@ open class ListenerSetTest : SysuiTestCase() {
        // Setup and preconditions
        val runnablesCalled = mutableListOf<Int>()
        // runnable1 is configured to remove itself
        val runnable1 = object : Runnable {
        val runnable1 =
            object : Runnable {
                override fun run() {
                    runnableSet.remove(this)
                    runnablesCalled.add(1)
                }
            }
        val runnable2 = Runnable {
            runnablesCalled.add(2)
        }
        val runnable2 = Runnable { runnablesCalled.add(2) }
        assertThat(runnableSet).isEmpty()
        runnableSet.addIfAbsent(runnable1)
        runnableSet.addIfAbsent(runnable2)
@@ -194,17 +193,13 @@ open class ListenerSetTest : SysuiTestCase() {
    fun addIfAbsent_isReentrantSafe() {
        // Setup and preconditions
        val runnablesCalled = mutableListOf<Int>()
        val runnable99 = Runnable {
            runnablesCalled.add(99)
        }
        val runnable99 = Runnable { runnablesCalled.add(99) }
        // runnable1 is configured to add runnable99
        val runnable1 = Runnable {
            runnableSet.addIfAbsent(runnable99)
            runnablesCalled.add(1)
        }
        val runnable2 = Runnable {
            runnablesCalled.add(2)
        }
        val runnable2 = Runnable { runnablesCalled.add(2) }
        assertThat(runnableSet).isEmpty()
        runnableSet.addIfAbsent(runnable1)
        runnableSet.addIfAbsent(runnable2)
+0 −0

File moved.

+46 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.util

/**
 * A collection of listeners, observers, callbacks, etc.
 *
 * This container is optimized for frequent mutation and infrequent iteration, with reentrant-safety
 * guarantees but without thread-safety guarantees. Specifically, to ensure that
 * [ConcurrentModificationException] is not thrown when listeners mutate the set, this iterator will
 * not reflect changes made to the set after the iterator is constructed.
 */
class CopyOnLoopListenerSet<E : Any>
/** Private constructor takes the internal list so that we can use auto-delegation */
private constructor(private val listeners: ArrayList<E>) :
    Collection<E> by listeners, IListenerSet<E> {

    /** Create a new instance */
    constructor() : this(ArrayList())

    @Suppress("UNCHECKED_CAST")
    override fun iterator(): Iterator<E> = listeners.toArray().iterator() as Iterator<E>

    override fun addIfAbsent(element: E): Boolean =
        if (element in listeners) {
            false
        } else {
            listeners.add(element)
        }

    override fun remove(element: E): Boolean = listeners.remove(element)
}