I encountered a race condition in my Android app, ...
# getting-started
c
I encountered a race condition in my Android app, and I’m unsure of the underlying cause. I have code roughly like that in the 🧵. I’ve kept the context of my use case a bit, as I am building a webrtc streaming app. The problem I encounter is that sometimes, the
CameraStream.videoSink
gets gc’ed, and so never receives any frames to send on to the webrtc stream. But it’s only sometimes, not all the time.
Copy code
interface Camera {
  fun addListener(listener: VideoSink)
}

class CameraImpl : Camera {
  private val listenersLock = Any()
  private val listeners = mutableListOf<WeakReference<VideoSink>>()
  override fun addListener(listener: VideoSink) {
    synchronized(listenersLock) {
      listeners.add(WeakReference(listener))
    }
  }

  // Other methods for dealing with camera
}


interface Stream {
  fun onFrame(frame: VideoFrame)
}

class CameraStream(
  private val camera: Camera,
  private val stream: Stream,
) {

  private val videoSink = VideoSink { frame ->
    stream.onFrame(frame)
  }

  init {
    camera.addListener(videoSink)
  }
}
The bug is generally reproducible on a fresh install, I think because the additional delay related to granting camera permission creates the timing needed to reproduce.
But I am unsure why
videoSink
gets gc’ed. The instance of
CameraStream
is long-lived, so I would expect it to retain
CameraStream.videoSink
. But I may misunderstand the Kotlin garbage collector.
Is this possibly a Kotlin bug?
l
Are you sure
CameraStream
reference is kept?
Maybe it's detached when asking for camera permissions, as the app goes to "background"
c
The
CameraStream
is instantiated and passed as a delegate to another class, and the reference to that class is definitely kept
I’ll see if I can put together a minimal reproducer