Repository navigation
Handle a ready device off the tracking loop so a slow lookup cannot block it - #44
Merged
Merged
Conversation
Reserve the serial's slot before awaiting Process.start so two launches for one serial start one process, and let an exit handler clear and report only the process it belongs to, so a stopped mirror exiting after a relaunch cannot make the new mirror look closed. Adds a process-starter seam and the first launcher tests.
… is not a failure launch() returned false both when a mirror was already running and when nothing could be started, and handoff aborted on false. It now returns started or alreadyRunning and throws when scrcpy cannot be started; handoff treats alreadyRunning as success and goes on to close the USB mirror. The IPC launch reply keeps 'launched' and adds 'alreadyRunning'.
onReady was awaited inside the track-devices loop and began with an unbounded model lookup, so one stalled device froze every other snapshot, and one that threw was reported as 'adb server unreachable' and, because the device was already marked known, never auto-launched. Move the per-snapshot logic into DeviceTracker: ready handling is a guarded task, the model lookup times out and falls back to the serial, and the next snapshot never waits. The fake adb server can now stall the model reply.
stop() and stopAll() found nothing to kill while Process.start was pending, so the mirror started anyway and stayed open. Remember the request and apply it when the process exists; a launch requested after that stop wins.
A device slower than the lookup timeout was announced under its serial and kept that name until it was replugged. The timed-out lookup now keeps running; when it answers and the device is still there, the name is stored and listeners are told. The fake adb server can release a stalled model reply.
3 tasks
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.
Refs #15
Stacked on #43 (#18), which is stacked on #42 (#16): the three touch
daemon.dartand the launcher, so each starts from the previous one and the diff here is only the #15 commit. Merge in order #42, #43, this one, and retarget each remaining PR tomainbefore deleting a merged branch (deleting a base branch closes the dependent PR).What
Moves the per-snapshot device logic out of
bin/daemon.dart's tracking loop into a smallDeviceTracker, so a device becoming ready is handled off the loop, with a bounded model lookup and a fallback name.Why
In the old loop,
await onReady(device)ran insideawait for (snapshot in client.trackDevices()), andonReadybegan withclient.model(serial), which has no read timeout. Two failure modes from #15:getpropfroze the loop, so no disconnect, state change ordevicesbroadcast happened for any device until it returned. Confirmed here: with the fake server stalling the reply,AdbClient.model()is still pending after 2 s and would stay so.knownhad already been set, saw the device as already ready on the next snapshot and never auto-launched it.How
DeviceTracker.handleSnapshotis synchronous and does only bookkeeping: logging, finding newly ready and vanished devices, updatingknown, and callingonGone/onChanged. It cannot be blocked or thrown into by a device.guardedtask (from Guard fire-and-forget daemon paths so one failure cannot end the process #37): read the model with a timeout (default 5 s); on timeout or error log it and use the serial as the name, without storing the serial as a model; skip the announcement if the device vanished meanwhile; then callonReady(device, name). AnonReadythat throws is logged and does not affect the next snapshot.bin/daemon.dart: the loop is nowtracker.handleSnapshot(snapshot). ItsknownandmodelNamesvariables stay, so nothing else in the daemon changed:onChangedhands it the new map (a fresh map each time, so code iterating it across awaits is safe, as before). The auto-launch,deviceReadyevent and log line moved into theonReadycallback unchanged.FakeAdbServer.stallModelReplies(bool)accepts the model request and never answers.onChangedis called, so thedevicesbroadcast carries the model. Checked before fixing: the state recovered, the name did not (the new test failed), so this is fixed here.DeviceCoordinator(Extract DeviceCoordinator from bin/daemon.dart and add tests #22 stays deferred); a read timeout insideAdbClient(broader, affects every caller; see thegoWirelesstcpiptimeout question, handled separately).Verified by
device_tracker_test.darttests (two cover the late name: it is adopted and announced to listeners without re-announcing the device, and ignored if the device is gone), including the issue's acceptance cases: a stalledgetpropdoes not delay the next snapshot (a disconnect is reported while the lookup is still pending); after the timeout the device is announced under its serial; a failing lookup (unknown serial, FAIL from the fake server) neither throws out of the snapshot nor logs "adb server unreachable" and still reachesonReady; anonReadythat throws is logged and the tracker keeps working; a device that vanished while waiting is not announced; announce-once, re-announce after offline, immutableknown.dart analyzeclean in core, ipc, daemon. Tests: core 21, ipc 26, daemon 71 (58 on Return a LaunchResult so an already-running mirror is not a failed launch #43) pass locally on Windows. CI results are in the checks below.APPDATA,autoLaunchoff): the phone is announced asPixel 10aandlistDevicesreturns it with its model; the daemon then exited cleanly.Risk / follow-ups
deviceReadyevent's model) use the serial. Its state is unaffected, and the model name now catches up (below). The window title of an already-open mirror is not updated.AdbClienthas no read timeout. That is the same gap as thegoWirelesstcpiptimeout and is tracked separately.bin/daemon.dartis thinner but still not covered by an automated test; the tracker behind it is.goWirelesstcpiptimeout and the remaining Unhandled async errors in fire-and-forget paths crash the daemon #14 untested paths are tracked separately.