Skip to content

fix: launch playlist diffing coroutine lazily instead of in init - #1444

Open
joelmuraguri wants to merge 2 commits into
mainfrom
joel/lazy-launch-diffing-coroutine
Open

fix: launch playlist diffing coroutine lazily instead of in init#1444
joelmuraguri wants to merge 2 commits into
mainfrom
joel/lazy-launch-diffing-coroutine

Conversation

@joelmuraguri

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes the TODO on ExoplayerController's diffing coroutine: it was launched eagerly in init, meaning it started running as soon as an
    ExoplayerController was constructed, before any video was ever registered or played.

Change

  • diffingJob is now launched with CoroutineStart.LAZY in init,
    so the coroutine is built but not scheduled to run.
  • registerVideo the first call site that can ever send into mediaItemMutationsChannel now calls diffingJob?.start() before
    doing anything else. Job.start() is idempotent, so this is safe to call on every registerVideo invocation.
  • Removed a stray diffingJob?.cancel() that preceded the job's only assignment (dead code, diffingJob was always null there).

Why this matters

  • mediaItemMutationsChannel is a rendezvous channel (Channel<...>(), capacity 0). This makes .start() a hard requirement, not a nice-to-have:if the job were marked lazy without ever calling .start(), the first

  • send in registerVideo would suspend forever with nobody collecting,
    silently dropping the video from the playlist. This PR wires up both
    halves of that contract.

  • teardown()'s diffingJob?.cancel() needs no changes cancelling a
    job that was never started is already safe.

Out of scope

  • The second TODO in the same init block (the two snapshotFlow{...}.launchIn(...) flows for pause-on-idle and mute) is a related but separate cleanup. launchIn has no lazy variant, so making those lazy requires rewriting them as plain scope.launch(start = CoroutineStart.LAZY) { flow.collect {...} }, which felt like a separate, slightly bigger change. Happy to follow up with that in a
    second PR if desired.

Testing

  • Verified registerVideo play still populates and plays the ExoPlayer playlist as before.
  • Verified calling registerVideo multiple times does not create multiple diffing collectors (Job.start() no-ops after the first call).
  • Verified teardown() immediately after construction (no video ever registered) cancels cleanly with no exceptions.

@joelmuraguri
joelmuraguri requested a review from tunjid July 4, 2026 08:39

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the ExoplayerController to launch the diffingJob coroutine lazily (CoroutineStart.LAZY) within the init block, starting it explicitly when a video player state is requested. This addresses a TODO and removes an unnecessary cancellation call. The reviewer suggested declaring diffingJob as a non-nullable val instead of a nullable var since it is never reassigned, which would clean up the code by removing null-safe navigation operators.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

isLooping: Boolean,
autoplay: Boolean,
): VideoPlayerState {
diffingJob?.start()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since diffingJob is initialized in the init block and never reassigned, it can be declared as a non-nullable val instead of a nullable var (i.e., private val diffingJob: Job).\n\nThis allows you to safely remove the null-safe navigation operator (?.) here and in teardown(), making the code cleaner and more idiomatic.\n\nPlease also update the declaration of diffingJob (which is outside this diff) to:\nkotlin\nprivate val diffingJob: Job\n

Suggested change
diffingJob?.start()
diffingJob.start()

@tunjid

tunjid commented Jul 4, 2026

Copy link
Copy Markdown
Owner

I was hoping to remove the init block completely with this. The idea is for some coroutine scope to actually own the diffing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants