Skip to content

ESP32 native espflash timeout leaks the serial port to the esptool fallback (EBUSY) #1614

Closed

Description

@zackees

Summary

When the native espflash verify (or write) hits its wall-clock timeout, Esp32Deployer returns immediately and the caller falls back to esptool, but the timed-out spawn_blocking thread still owns the serial port. The esptool fallback then fails with EBUSY, and the port stays held for tens of seconds after the deploy has already failed.

The code comments claim the opposite:

On timeout the blocking task is detached (the Flasher drops, closing the serial port)

tokio::time::timeout only drops the JoinHandle. A spawn_blocking closure cannot be cancelled: it runs until espflash returns on its own, and the Flasher (and its port fd) lives that whole time.

Affected sites (crates/fbuild-deploy/src/esp32/deployer.rs)

  • try_verify_deployment_native: 30 s cap
  • try_deploy_native: 180 s cap
  • try_deploy_regions_native: 180 s cap

Evidence (fbuild 2.5.30, Linux, ESP32-C6 on USB-Serial-JTAG 303a:1001)

Daemon log from fbuild deploy -e esp32c6 -p /dev/ttyACM1:

18:04:01 verifying firmware.bin on /dev/ttyACM1 via espflash (esp32c6)
18:04:31 WARN native verify-flash failed (deploy failed: native verify timed out after 30s on /dev/ttyACM1); falling back to esptool
18:04:31 WARN verify-flash pre-check failed (... esptool verify-flash failed (exit 2): ERROR: Could not open /dev/ttyACM1, the port is busy or doesn't exist.
18:04:31 flashing ... via esptool (esp32c6)
   -> [Errno 16] could not open port /dev/ttyACM1: Device or resource busy

Sampling /proc/*/fd every 0.5 s during a repro:

11:14:24  fbuild-daemon holds ttyACM1        (native verify starts)
11:14:43  deploy fails: esptool EBUSY        (≈30 s timeout fired, fallback ran)
11:15:23  holder released                    (≈40 s after the timeout)

No other process held the port. The deploy fails deterministically whenever native verify times out, and the user-visible error ("port is busy") points at the wrong cause.

This is a concrete root cause for at least part of #1429 ("Release was observed within ~5 s in one instance and not at all in others").

Fix

Add one helper for port-owning blocking work: run the closure under the wall-clock budget, and on timeout keep awaiting the JoinHandle for a bounded release grace so the port is released before the caller falls back. If the grace also expires, return an error that names the still-held port instead of letting esptool fail with a misleading EBUSY. Route all three sites through it and correct the comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions