Adding async/await and UniTask support#319
Open
MaxHeimbrock wants to merge 5 commits into
Open
Conversation
Stage 1 of the UniTask migration: enable `await room.Connect(...)` and similar without taking on a UniTask dependency. The awaiter's continuation is invoked from the existing IsDone / IsCurrentReadDone / IsEos property setters, so all nine concrete instructions (Connect, PublishTrack, RPC, SendText/File, stream open/write/close, etc.) become awaitable with no change to their completion code paths. Race between FFI-thread completion and main-thread await registration is resolved with a sentinel-value Interlocked.CompareExchange on a single continuation slot. GetResult() is intentionally a no-op so the await surface keeps strict parity with `yield return` (callers still inspect IsError); a throwing variant can be layered on later. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Connect_FailsWithInvalidUrl_Awaitable failed intermittently in the full PlayMode suite: awaiting the ConnectInstruction resumes the instant IsDone is set, but the FFI emits its "error while connecting" log batch a frame or two later — after the test had already reset LogAssert.ignoreFailingMessages, so the late error surfaced as an unhandled message and failed the test. It only passed in isolation because the timing happened to line up. Replace it with two deterministic tests driven by a synthetic YieldInstruction subclass: one for the OnCompleted path (await registered while pending, then completed) and one for the IsCompleted fast path (already done before await). These exercise the GetAwaiter logic directly with no FFI, no dev server, and no LogAssert race. The real connect-fail path stays covered by the existing Connect_FailsWithInvalidUrl coroutine test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a629011 to
6145795
Compare
Stage 2 of the UniTask migration. The new LiveKit.UniTask asmdef hosts an AsUniTask extension on YieldInstruction and StreamYieldInstruction; the asmdef compiles only when com.cysharp.unitask is installed (the versionDefine auto-activates LIVEKIT_UNITASK). When UniTask is absent, the extension simply does not exist — no compile error, no runtime cost, no impact on Stage 1's awaiter. AsUniTask wraps the existing one-shot completion path in a UniTaskCompletionSource and adds CancellationToken support with "abandon awaiter" semantics: a cancel faults the UniTask with OperationCanceledException, but the underlying FFI request is not aborted. GetResult stays non-throwing for IsError parity with yield return / await; throwing variants can be layered on later. Includes a UniTask migration of Samples~/Meet to demonstrate the new path end-to-end (Connect / PublishLocalCamera / PublishLocalMicrophone all switch to async UniTask with cancellation tied to GetCancellationTokenOnDestroy). Long-running per-frame pumps stay on StartCoroutine since they aren't request/response. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Remove UniTask package
Stage 3 of the UniTask migration. Exposes ByteStreamReader/TextStreamReader incremental reads as IUniTaskAsyncEnumerable<TChunk> so chunks can be consumed with `await foreach`, building on Stage 1's StreamYieldInstruction awaiter and Stage 2's AsUniTask. A single generic extension AsAsyncEnumerable<TChunk>(this ReadIncrementalInstructionBase<TChunk>) covers both byte[] and string readers. The loop mirrors the coroutine consumer's observable behavior: await a chunk, yield it, re-check IsEos AFTER yielding (Reset() is disallowed past EoS), and Reset() for the next chunk. On EoS carrying a StreamError the enumerable throws that error — idiomatic for await foreach, the one place the UniTask surface throws rather than exposing IsError. Cancellation surfaces as OperationCanceledException with abandon-awaiter semantics. To let the separate LiveKit.UniTask assembly drive the loop, two members are widened to public (both already public on the sibling DataTrack.ReadFrameInstruction, behavior-preserving): StreamYieldInstruction.IsCurrentReadDone getter and ReadIncrementalInstructionBase<T>.LatestChunk. The runtime and test UniTask asmdefs gain a UniTask.Linq reference (source of UniTaskAsyncEnumerable.Create / IUniTaskAsyncEnumerable), and InternalsVisibleTo is extended to the PlayModeTests.UniTask assembly so the deterministic tests can construct a synthetic reader (the same FfiHandle-based seam the EditMode tests use). DataTrack frame streaming is intentionally out of scope (its ReadFrameInstruction has no awaiter and no Reset) — a possible follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stage 4 (capstone) of the UniTask migration. Adds a README section covering the three interchangeable async styles the SDK now supports, and states the policy: coroutines remain the default and fully supported; async/await and UniTask are additive opt-ins; the coroutine API is not deprecated. - async/await with no dependency (instructions are awaitable; inspect IsError, await does not throw — parity with yield return). - UniTask opt-in (com.cysharp.unitask + LIVEKIT_UNITASK): AsUniTask with CancellationToken, UniTask.WhenAll composition, and AsAsyncEnumerable for await foreach over incremental streams (throws StreamError on error EoS). Examples use the verified public signatures (Connect(url, token, RoomOptions), PublishTrack(track, options), ReadIncremental().AsAsyncEnumerable()) and point to the Meet sample (UniTask) and Basic sample (coroutines) as references. Docs-only; no code, no deprecation, no version bump. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6145795 to
632297b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
We currently only support Coroutines for async execution, with this PR we will support async/await and if clients import the UniTask package, we also support UniTasks natively.