[Android] Prevent concurrent access in renderers callbacks

Geoffrey Métais git at videolan.org
Wed May 23 15:32:41 CEST 2018


vlc-android | branch: 3.0.x | Geoffrey Métais <geoffrey.metais at gmail.com> | Wed May 23 15:27:05 2018 +0200| [41e649a9955937406811339e79acd8276326f350] | committer: Geoffrey Métais

Prevent concurrent access in renderers callbacks

> https://code.videolan.org/videolan/vlc-android/commit/41e649a9955937406811339e79acd8276326f350
---

 .../src/org/videolan/vlc/RendererDelegate.kt       | 43 ++++++++++++++++------
 1 file changed, 31 insertions(+), 12 deletions(-)

diff --git a/vlc-android/src/org/videolan/vlc/RendererDelegate.kt b/vlc-android/src/org/videolan/vlc/RendererDelegate.kt
index 5d295b036..0eeb789cd 100644
--- a/vlc-android/src/org/videolan/vlc/RendererDelegate.kt
+++ b/vlc-android/src/org/videolan/vlc/RendererDelegate.kt
@@ -22,6 +22,8 @@ package org.videolan.vlc
 import kotlinx.coroutines.experimental.CoroutineStart
 import kotlinx.coroutines.experimental.android.UI
 import kotlinx.coroutines.experimental.async
+import kotlinx.coroutines.experimental.channels.Channel
+import kotlinx.coroutines.experimental.channels.actor
 import kotlinx.coroutines.experimental.launch
 import org.videolan.libvlc.RendererDiscoverer
 import org.videolan.libvlc.RendererItem
@@ -34,8 +36,8 @@ object RendererDelegate : RendererDiscoverer.EventListener, ExternalMonitor.Netw
     private val TAG = "VLC/RendererDelegate"
     private val mDiscoverers = ArrayList<RendererDiscoverer>()
     val renderers = ArrayList<RendererItem>()
-    private val mListeners = LinkedList<RendererListener>()
-    private val mPlayers = LinkedList<RendererPlayer>()
+    private val listeners = LinkedList<RendererListener>()
+    private val players = LinkedList<RendererPlayer>()
 
     @Volatile private var started = false
     var selectedRenderer: RendererItem? = null
@@ -65,13 +67,13 @@ object RendererDelegate : RendererDiscoverer.EventListener, ExternalMonitor.Netw
         }
     }
 
-    suspend fun stop() {
+    fun stop() {
         if (!started) return
         started = false
         for (discoverer in mDiscoverers) discoverer.stop()
         clear()
         onRenderersChanged()
-        for (player in mPlayers) player.onRendererChanged(null)
+        cbActor.offer(RendererChanged(null))
     }
 
     private fun clear() {
@@ -93,20 +95,37 @@ object RendererDelegate : RendererDiscoverer.EventListener, ExternalMonitor.Netw
         onRenderersChanged()
     }
 
-    fun addListener(listener: RendererListener) = mListeners.add(listener)
+    fun addListener(listener: RendererListener) = cbActor.offer(AddListener(listener))
 
-    fun removeListener(listener: RendererListener) = mListeners.remove(listener)
+    fun removeListener(listener: RendererListener) = cbActor.offer(RemoveListener(listener))
 
-    private fun onRenderersChanged() {
-        for (listener in mListeners) listener.onRenderersChanged(renderers.isEmpty())
-    }
+    private fun onRenderersChanged() = cbActor.offer(RenderersChanged())
 
     fun selectRenderer(item: RendererItem?) {
         selectedRenderer = item
-        for (player in mPlayers) player.onRendererChanged(item)
+        cbActor.offer(RendererChanged(item))
     }
 
-    fun addPlayerListener(listener: RendererPlayer) = mPlayers.add(listener)
+    fun addPlayerListener(player: RendererPlayer) = cbActor.offer(AddPlayer(player))
+
+    fun removePlayerListener(player: RendererPlayer) = cbActor.offer(RemovePlayer(player))
 
-    fun removePlayerListener(listener: RendererPlayer) = mPlayers.remove(listener)
+    private val cbActor = actor<CbAction>(UI, Channel.UNLIMITED) {
+        for (action in channel) when (action) {
+            is AddPlayer -> players.add(action.player)
+            is RemovePlayer -> players.remove(action.player)
+            is RendererChanged -> for (player in players) player.onRendererChanged(action.renderer)
+            is AddListener -> listeners.add(action.listener)
+            is RemoveListener -> listeners.remove(action.listener)
+            is RenderersChanged -> for (listener in listeners) listener.onRenderersChanged(renderers.isEmpty())
+        }
+    }
 }
+
+sealed class CbAction
+private class AddPlayer(val player: RendererDelegate.RendererPlayer) : CbAction()
+private class RemovePlayer(val player: RendererDelegate.RendererPlayer) : CbAction()
+private class RendererChanged(val renderer: RendererItem?) : CbAction()
+private class AddListener(val listener: RendererDelegate.RendererListener) : CbAction()
+private class RemoveListener(val listener: RendererDelegate.RendererListener) : CbAction()
+private class RenderersChanged : CbAction()



More information about the Android mailing list