11package org.schabi.newpipe.player.ui
22
3- import androidx.core. util.Consumer
3+ import org.schabi.newpipe. util.GuardedByMutex
44import java.util.Optional
55
66class PlayerUiList (vararg initialPlayerUis : PlayerUi ) {
7- val playerUis = mutableListOf<PlayerUi >()
7+ var playerUis = GuardedByMutex ( mutableListOf<PlayerUi >() )
88
99 /* *
1010 * Creates a [PlayerUiList] starting with the provided player uis. The provided player uis
@@ -16,7 +16,9 @@ class PlayerUiList(vararg initialPlayerUis: PlayerUi) {
1616 * @param initialPlayerUis the player uis this list should start with; the order will be kept
1717 */
1818 init {
19- playerUis.addAll(listOf (* initialPlayerUis))
19+ playerUis.runWithLockSync {
20+ lockData.addAll(listOf (* initialPlayerUis))
21+ }
2022 }
2123
2224 /* *
@@ -41,7 +43,9 @@ class PlayerUiList(vararg initialPlayerUis: PlayerUi) {
4143 }
4244 }
4345
44- playerUis.add(playerUi)
46+ playerUis.runWithLockSync {
47+ lockData.add(playerUi)
48+ }
4549 }
4650
4751 /* *
@@ -52,12 +56,24 @@ class PlayerUiList(vararg initialPlayerUis: PlayerUi) {
5256 * @param T the class type parameter </T>
5357 * */
5458 fun <T > destroyAll (playerUiType : Class <T ?>) {
55- for (ui in playerUis) {
56- if (playerUiType.isInstance(ui)) {
57- ui.destroyPlayer()
58- ui.destroy()
59- playerUis.remove(ui)
59+ val toDestroy = mutableListOf<PlayerUi >()
60+
61+ // short blocking removal from class to prevent interfering from other threads
62+ playerUis.runWithLockSync {
63+ val new = mutableListOf<PlayerUi >()
64+ for (ui in lockData) {
65+ if (playerUiType.isInstance(ui)) {
66+ toDestroy.add(ui)
67+ } else {
68+ new.add(ui)
69+ }
6070 }
71+ lockData = new
72+ }
73+ // then actually destroy the UIs
74+ for (ui in toDestroy) {
75+ ui.destroyPlayer()
76+ ui.destroy()
6177 }
6278 }
6379
@@ -67,18 +83,19 @@ class PlayerUiList(vararg initialPlayerUis: PlayerUi) {
6783 * @param T the class type parameter
6884 * @return the first player UI of the required type found in the list, or null
6985 </T> */
70- fun <T > get (playerUiType : Class <T >): T ? {
71- for (ui in playerUis) {
72- if (playerUiType.isInstance(ui)) {
73- when (val r = playerUiType.cast(ui)) {
74- // try all UIs before returning null
75- null -> continue
76- else -> return r
86+ fun <T > get (playerUiType : Class <T >): T ? =
87+ playerUis.runWithLockSync {
88+ for (ui in lockData) {
89+ if (playerUiType.isInstance(ui)) {
90+ when (val r = playerUiType.cast(ui)) {
91+ // try all UIs before returning null
92+ null -> continue
93+ else -> return @runWithLockSync r
94+ }
7795 }
7896 }
97+ return @runWithLockSync null
7998 }
80- return null
81- }
8299
83100 /* *
84101 * @param playerUiType the class of the player UI to return;
@@ -96,7 +113,11 @@ class PlayerUiList(vararg initialPlayerUis: PlayerUi) {
96113 * @param consumer the consumer to call with player UIs
97114 */
98115 fun call (consumer : java.util.function.Consumer <PlayerUi >) {
99- for (ui in playerUis) {
116+ // copy the list out of the mutex before calling the consumer which might block
117+ val new = playerUis.runWithLockSync {
118+ lockData.toMutableList()
119+ }
120+ for (ui in new) {
100121 consumer.accept(ui)
101122 }
102123 }
0 commit comments