Skip to content

fix(manager): a login ends only once its service started or its dir is gone - #20

Merged
LYJW131 merged 2 commits into
litefrom
fix/manager-login-lifecycle
Nov 3, 2026
Merged

LYJW131 merged 2 commits into
litefrom
fix/manager-login-lifecycle

Conversation

@LYJW131

@LYJW131 LYJW131 commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Follows the Devin review of #18.

  • A canceled login whose child had already logged in returned before the account's service started (it ran on its own goroutine), and a retry could wipe the starting account's dir. The service now starts within the login (st.exited covers it), an abort waits for it, and a replacing login answers already login when the old one got through.
  • A failed login released the account before removing its dir; the dir now goes under the registry lock while the failed login still holds the account.
  • A reaped login child is forgotten, so an abort never signals a reused pid.

POST /shutdown stays unauthenticated on purpose: it exists only in the QEMU guest, whose port amdl forwards to 127.0.0.1, where /login and /logout are just as open.

Tests: go test -race ./....

🤖 Generated with Claude Code


Devin Review

…s gone

Review of #18 found two windows where a retry could lose its files:

- A login whose child had already logged in ended (exited) before the
  account's service started, which ran on its own goroutine; an abort then
  returned at once and a retry wiped the starting account's dir. The
  service now starts within the login, an abort waits for it (up to the
  service's ready timeout), and a replacing login finds the account
  logged in instead.
- A failed login released the account before removing its dir, so a retry
  could reserve it and write there first. The dir now goes under the
  registry's lock while the failed login still holds the account.

A reaped login child is forgotten, so an abort never signals a reused pid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: LYJW131 <153256373+LYJW131@users.noreply.github.com>

@devin-ai-integration devin-ai-integration 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.

Devin Review found 2 potential issues.

Devin Review

Comment thread manager/login.go
// abortWait bounds how long an abort waits for the login to end: the killed
// child reaped and its half-made instance dir removed, or, for a child that
// had already logged in, the account's service started.
const abortWait = liteReadyTimeout + 15*time.Second

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 Login timeout response misses write deadline

If service startup is pending at 90 seconds, abortLogin can wait another 75 seconds. The login response then exceeds handleLogin's 105-second write deadline, so the client receives no timeout answer.

Learn more

A login request has a 90-second timer, and its HTTP handler gives the response a 105-second write deadline handleLogin. After the timer fires, waitForLoginFinish calls abortLogin synchronously. If the login child exited with tokens but the service is still starting, st.cmd is nil and abortLogin waits for service readiness rather than stopping it. That wait can extend past the HTTP deadline even though the service is operating within its own 60-second readiness window.

Example: The child finishes at 85 seconds, and the service becomes ready 30 seconds later. The login timer fires at 90 seconds; the handler cannot write its timeout response at 115 seconds because its write deadline was 105 seconds.

Recommended fix: Bound the HTTP handler's remaining wait by its write deadline, or separate cancellation response timing from the service-startup wait without permitting a retry to wipe the starting instance's directory. Test a child completing near loginWait with a service taking longer than loginWriteTimeout - loginWait to become ready.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed: the timeout answers first and the abort runs after it; a retry waits for it.

Comment thread manager/login.go
Comment on lines +289 to 292
if st.isAborted() && !hasTokenFiles(id) {
loginFailed(id, st, errLoginCanceled)
return
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Cancellation can leave an account running

If tokens exist when a request is canceled, the service can still start after the request reports cancellation. Callers that retry can receive already login rather than a fresh login; clarify this response contract.

(Refers to this code)

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Documented in the README: a cancel after the child logged in does not undo the login, and a retry answers already login.

The abort can wait for a service that is starting, past the request's
write deadline; it now runs after the answer, and a retry waits for it.
The README says that a cancel after the child logged in does not undo
the login.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: LYJW131 <153256373+LYJW131@users.noreply.github.com>
@LYJW131
LYJW131 merged commit bb4d4d8 into lite Nov 3, 2026
6 checks passed
LYJW131 added a commit to AMDL-Web/apple-music-downloader that referenced this pull request Nov 3, 2026
…their service started

Its manager starts a logged-in account's service within the login, so a
canceled login's retry cannot wipe the starting account's dir, removes a
failed login's dir before it frees the account, and answers a timed-out
login before its abort ends (AMDL-Web/wrapper#20).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: LYJW131 <153256373+LYJW131@users.noreply.github.com>
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.

1 participant