From 4444eed16704ce15948468efb718a9626c580d6c Mon Sep 17 00:00:00 2001 From: Steven Portley Date: Tue, 3 Feb 2026 14:01:33 -0800 Subject: [PATCH] Add a proper claim/release API to libhoth There are more than one implementations of a retry loop to connect to a libhoth transport. This moves the higher quality one to a public API that can be re-used. The de-duplication will be in a separate PR. --- examples/htool_console.c | 51 ++--------------------------------- transports/libhoth_device.c | 54 +++++++++++++++++++++++++++++++++++++ transports/libhoth_device.h | 7 +++++ 3 files changed, 63 insertions(+), 49 deletions(-) diff --git a/examples/htool_console.c b/examples/htool_console.c index 90a6dc7..be687f5 100644 --- a/examples/htool_console.c +++ b/examples/htool_console.c @@ -61,53 +61,6 @@ void restore_terminal(int fd, const struct termios* old_termios) { tcsetattr(fd, TCSANOW, old_termios); } -// Try to claim `dev`. If `dev` is already claimed, then try to claim later by -// waiting an exponentially backed off amount of time. -static int claim_device(struct libhoth_device* dev, uint32_t timeout_us) { - enum { - // The maximum time to sleep per attempt. - // Limited by `usleep()` to <1 second. - MAX_SINGLE_SLEEP_US = 1000 * 1000 - 1, - BACKOFF_FACTOR = 2, - INITIAL_WAIT_US = 10 * 1000, - }; - - uint32_t wait_us = INITIAL_WAIT_US; - uint32_t total_waiting_us = 0; - - while (true) { - int status = dev->claim(dev); - - if (status != LIBHOTH_ERR_INTERFACE_BUSY) { - // We either claimed the device or encountered an unexpected error. Let - // the caller know. - return status; - } - - if (total_waiting_us >= timeout_us) { - // We've exhausted our waiting budget. We couldn't claim the device - // within the configured timeout. - return LIBHOTH_ERR_INTERFACE_BUSY; - } - - usleep(wait_us); - - if (total_waiting_us <= UINT32_MAX - wait_us) { - total_waiting_us += wait_us; - } else { - // Saturate at integer upper bound to prevent overflow. - total_waiting_us = UINT32_MAX; - } - - if (wait_us <= MAX_SINGLE_SLEEP_US / BACKOFF_FACTOR) { - wait_us *= BACKOFF_FACTOR; - } else { - // Saturate at the `usleep()` max sleep bound. - wait_us = MAX_SINGLE_SLEEP_US; - } - } -} - int htool_console_run(struct libhoth_device* dev, const struct libhoth_htool_console_opts* opts) { printf("%sStarting Interactive Console\n", kAnsiRed); @@ -164,12 +117,12 @@ int htool_console_run(struct libhoth_device* dev, break; } - dev->release(dev); + libhoth_release_device(dev); // Give an opportunity for other clients to use the interface. usleep(1000 * opts->yield_ms); - status = claim_device(dev, 1000 * 1000 * opts->claim_timeout_secs); + status = libhoth_claim_device(dev, 1000 * 1000 * opts->claim_timeout_secs); if (status != LIBHOTH_OK) { break; } diff --git a/transports/libhoth_device.c b/transports/libhoth_device.c index 8255763..42828b6 100644 --- a/transports/libhoth_device.c +++ b/transports/libhoth_device.c @@ -14,7 +14,9 @@ #include "transports/libhoth_device.h" +#include #include +#include #include "libhoth_device.h" @@ -57,3 +59,55 @@ int libhoth_device_close(struct libhoth_device* dev) { free(dev); return status; } + +int libhoth_claim_device(struct libhoth_device* dev, uint32_t timeout_us) { + enum { + // The maximum time to sleep per attempt. + // Limited by `usleep()` to <1 second. + MAX_SINGLE_SLEEP_US = 1000 * 1000 - 1, + BACKOFF_FACTOR = 2, + INITIAL_WAIT_US = 10 * 1000, + }; + + uint32_t wait_us = INITIAL_WAIT_US; + uint32_t total_waiting_us = 0; + + while (true) { + int status = dev->claim(dev); + + if (status != LIBHOTH_ERR_INTERFACE_BUSY) { + // We either claimed the device or encountered an unexpected error. Let + // the caller know. + return status; + } + + if (total_waiting_us >= timeout_us) { + // We've exhausted our waiting budget. We couldn't claim the device + // within the configured timeout. + return LIBHOTH_ERR_INTERFACE_BUSY; + } + + usleep(wait_us); + + if (total_waiting_us <= UINT32_MAX - wait_us) { + total_waiting_us += wait_us; + } else { + // Saturate at integer upper bound to prevent overflow. + total_waiting_us = UINT32_MAX; + } + + if (wait_us <= MAX_SINGLE_SLEEP_US / BACKOFF_FACTOR) { + wait_us *= BACKOFF_FACTOR; + } else { + // Saturate at the `usleep()` max sleep bound. + wait_us = MAX_SINGLE_SLEEP_US; + } + } + + // Unreachable + return LIBHOTH_ERR_FAIL; +} + +int libhoth_release_device(struct libhoth_device* dev) { + return dev->release(dev); +} diff --git a/transports/libhoth_device.h b/transports/libhoth_device.h index 607cf5f..7423c1d 100644 --- a/transports/libhoth_device.h +++ b/transports/libhoth_device.h @@ -16,6 +16,7 @@ #define _LIBHOTH_TRANSPORTS_LIBHOTH_DEVICE_H_ #include +#include #ifdef __cplusplus extern "C" { @@ -75,6 +76,12 @@ int libhoth_device_reconnect(struct libhoth_device* dev); int libhoth_device_close(struct libhoth_device* dev); +// Try to claim `dev`. If `dev` is already claimed, then try to claim later by +// waiting an exponentially backed off amount of time. +int libhoth_claim_device(struct libhoth_device* dev, uint32_t timeout_us); + +int libhoth_release_device(struct libhoth_device* dev); + #ifdef __cplusplus } #endif