Own the map download progress lifecycle - #107
Merged
Merged
Conversation
FrogAi
force-pushed
the
codex/own-download-lifecycle
branch
from
August 9, 2026 01:06
19f9a4d to
0e78596
Compare
This was referenced Aug 9, 2026
FrogAi
force-pushed
the
codex/own-download-lifecycle
branch
from
August 10, 2026 03:16
27341ca to
1a61d9d
Compare
FrogAi
force-pushed
the
codex/own-download-lifecycle
branch
from
September 4, 2026 21:28
1a61d9d to
68f78ee
Compare
FrogAi
added a commit
to FrogAi/mapd
that referenced
this pull request
Sep 4, 2026
Retain the original PR commits and the tested rewrite. The resulting file tree is identical to 68f78ee.
Replace the earlier implementation with the simplified version.
FrogAi
force-pushed
the
codex/own-download-lifecycle
branch
from
September 4, 2026 21:50
90f2caa to
8e5e677
Compare
This was referenced Sep 5, 2026
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.
The upstream settings handler starts a download worker before recording ownership, so two commands arriving before the next progress poll can start overlapping downloads. The handler now claims ownership before launching the worker to close that gap.
Each run gets its own cancellation channel, published progress is copied, and the terminal update is retained. Idle or late cancellation cannot carry into a later run, and the UI can observe completion even when an older progress update is queued.
This follows the existing serial settings owner, single worker, and capacity-one progress queue. Cancellation still takes effect between archives; an in-flight archive finishes first.
Root cause, ownership, reproducible checks
Root cause
On upstream
7201c6b, the main loop handles openpilot and CLI input serially before polling progress.Handle(download)checksdownloadActiveand launches a worker, but that flag is only updated whenGetDownloadProgress()consumes a message. Both commands can therefore see an idle downloader and launch workers sharing temporary files, output paths, progress, and cancellation.The cancellation channel also outlives individual runs, so an idle or late token can remain for the next run. Sending
DownloadProgresscopies slice and map headers, leaving nested location details shared with the worker. Later changes can mutate a retained snapshot, while a full one-slot queue can hide the finalActive=falsemessage behind an older active update.Change and rationale
The existing settings owner sets
downloadActive=trueand creates a buffered cancellation channel before starting the worker. It ignores another download command until it consumes terminal progress and releases the channel. Keeping ownership through that handoff prevents a new run from sharing the old progress slot.publishProgress()copies the location list, details map, and each detail value. It removes any stale queued snapshot and makes a nonblocking send. Under the actual single-producer, capacity-one contract, the newest snapshot, including completion, remains available. Intermediate updates are still coalesced; progress is not a lossless event history. No manager, mutex, or extra goroutine is needed.Verification
Checked head
8e5e677, treee435907c7ce22a6e75ce571d757609300a94abf5, against upstream7201c6b4b4ec1b0b9ea21daa8c05b80fdd7e01ee. The production diff is confined tosettings/settings.goandsettings/download.go.The complete five-test reproducer below invokes real
HandleandDownload. Its replacement HTTP transport blocks at controlled request boundaries and returns valid empty tar/gzip responses. Paths are temporary, and the fixture fails visibly if device output/custom-menu paths exist. It neither starts the daemon nor sends an external HTTP request.0downloads and no HTTP request2downloads1download; no second requestThe upstream late-token row follows the shared channel lifetime in source; the current test exercises that token and the subsequent run. The last two rows describe the cancellation boundary, not in-flight interruption. A cancellation after the final archive starts does not retroactively mark that successful completion canceled. Direct callers of
Downloadremain responsible for coordinating their own calls and channels.Executed on 2026-09-05 with Linux amd64 and Go 1.25.1. Fresh upstream runs reproduced missing synchronous ownership, idle-cancel leakage, snapshot mutation, and lost terminal progress. All five tests passed on the current implementation. The current focused race check also passed, including serialization of a retained snapshot while the worker advances; package vet passed.
The actual run used cached modules with
GOPROXY=off,GOSUMDB=off, and container networking disabled. These are synthetic ownership/publication checks, not a device download session or an extraction audit. HTTP behavior, extraction, URLs, commands, and IPC fields are unchanged; this PR adds no in-flight HTTP cancellation or request timeout.Integration with #136
This PR and #136 overlap in the downloader loop. The combined resolution must retain #136's selected-row iteration and call this PR's
publishProgress()inside that loop, before the shared cancellation/download body. Choosing either whole side would drop the other change.Combined validation: exact source tree
a4c306906627db3ac7a8ab768651c8628d55465acombines #1019d61f06a1288ec4ea6f74f7d56a3057444316a13, #10320e7c25b054b6399360676a7f539a39b4fbf855c, #105bfcfe77be066634e36054327b20cfa6541063b54, #1078e5e677d1196838069e9665d4e9d962bcc1e116b, #1166fd5bbd6cf617c24a7fefd5e302fd36688a1a63b, #13630e8ce98ea7a4c8401dbb5bfc62120c84fc689e4. The only overlapping file issettings/download.go; the resolution retains #136's selected-row loop and #107's progress publication inside it.On 2026-09-05, combined Linux amd64 tests (including the scratch regression fixtures), race checks, vet and build passed. Under ARM64 emulation, the existing Makefile build stage (
make GO_CAPNP_PATH=/usr/local/go-capnp/std), committed repository tests, vet and both CLI help commands passed with Go 1.25.1;go.mod/go.sumstayed unchanged and the resulting executable is AArch64. The ARM64 run does not include the extra amd64 scratch tests. It used an isolated retained build image, not a new dependency-install/image rebuild or physical device. No production archive payload or live params were accessed. #105 still requires runtime-first rollout before regenerated tiles are distributed.Reproduce from a fresh clone
Save the two complete blocks below beside each other as
reproducer_test.goandreproduce.sh. Requirements are Linux, Bash, Git, tar, and Go 1.25.1. Use an isolated environment without/data/media/0or/data/openpilot/mapd_download_menu.json. The runner archives each pinned revision into a separate temporary directory, adds only the test fixture, and leaves raw results in its printed output directory.Populate a module cache from the pinned revisions before the runner disables dependency fetching. The setup command downloads repository/module dependencies; the tests use only the synthetic transport:
reproduce.sh:reproducer_test.go: