fix(garmin): give startup login its own result queue, not the shared one
_startup_login's background thread and _handle_authenticate's shared the module-level _login_result_queue with no correlation. Since _startup_login can now time out at 10s while its thread keeps running (from the previous fix in this wave), a slow-cold-starting subprocess's first explicit "Connect to Garmin" call could accidentally dequeue the startup thread's stale result instead of its own fresh one, orphaning the loser's result to corrupt a later authenticate/complete_mfa call. _handle_authenticate and _handle_complete_mfa still correctly share _login_result_queue -- they're two halves of one explicit, MFA-capable login flow. _startup_login is a background tokenstore resume with no MFA involved, so it now uses its own private, function-local queue.Queue() instead, making cross-contamination structurally impossible. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,17 @@ def _startup_login():
|
||||
must never block indefinitely here -- if it times out, the thread
|
||||
keeps running and will update _auth_state whenever it eventually
|
||||
finishes (success or failure), same as today, just without wedging
|
||||
the whole subprocess unresponsive in the meantime."""
|
||||
the whole subprocess unresponsive in the meantime.
|
||||
|
||||
Deliberately uses its own private, function-local result queue rather
|
||||
than the module-level _login_result_queue that _handle_authenticate and
|
||||
_handle_complete_mfa share -- those two are legitimately two halves of
|
||||
one explicit, MFA-capable login flow and must share a queue for the MFA
|
||||
handoff to work, but this is a plain background tokenstore resume with
|
||||
no MFA involved. Sharing the queue here would let this thread's stale
|
||||
result (arriving after the 10s timeout below) be dequeued by an
|
||||
unrelated, later authenticate/complete_mfa call instead of that call's
|
||||
own fresh result."""
|
||||
global _client, _auth_state
|
||||
email = os.environ.get("GARMIN_EMAIL")
|
||||
password = os.environ.get("GARMIN_PASSWORD")
|
||||
@@ -50,18 +60,19 @@ def _startup_login():
|
||||
return
|
||||
|
||||
_client = Garmin(email, password)
|
||||
result_queue = queue.Queue() # private to this call, never shared with authenticate/complete_mfa
|
||||
|
||||
def _do_login():
|
||||
try:
|
||||
_client.login(tokenstore=TOKENSTORE)
|
||||
_login_result_queue.put(("success", None))
|
||||
result_queue.put(("success", None))
|
||||
except Exception as exc:
|
||||
_login_result_queue.put(("error", str(exc)))
|
||||
result_queue.put(("error", str(exc)))
|
||||
|
||||
threading.Thread(target=_do_login, daemon=True).start()
|
||||
|
||||
try:
|
||||
status, err = _login_result_queue.get(timeout=10)
|
||||
status, err = result_queue.get(timeout=10)
|
||||
if status == "success":
|
||||
_auth_state = "authenticated"
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user