From 4ac74edf387271314df4468a5741bbf24d202415 Mon Sep 17 00:00:00 2001 From: Bob Iannucci Date: Sun, 12 Apr 2026 20:41:25 -0700 Subject: [PATCH] fix(native): implement BinarySemaphorePosix with proper pthread synchronization (#9895) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(native): implement BinarySemaphorePosix with proper pthread synchronization The BinarySemaphorePosix class (used on all Linux/portduino/native builds) had stub implementations: give() was a no-op and take() just called delay(msec) and returned false. This broke the cooperative thread scheduler on native platforms — threads could not wake the main loop, radio RX interrupts were missed, and telemetry never transmitted over the mesh. Replace the stubs with a proper binary semaphore using pthread_mutex_t + pthread_cond_t + bool signaled: - take(msec): pthread_cond_timedwait with CLOCK_REALTIME timeout, consumes signal atomically (binary semaphore semantics) - give(): sets signaled=true, signals condition variable - giveFromISR(): delegates to give(), sets pxHigherPriorityTaskWoken Tested on Raspberry Pi 3 Model B (ARM64, Debian Bookworm) with Adafruit LoRa Radio Bonnet (SX1276). Before fix: no radio TX/RX, no telemetry on mesh. After fix: bidirectional LoRa, MQTT gateway, telemetry all working. Co-Authored-By: Claude Opus 4.6 * ARCH_PORTDUINO * Refactor BinarySemaphorePosix header for ARCH_PORTDUINO * Change preprocessor directive from ifndef to ifdef * Gate new Semaphore code to Portduino and fix STM compilation * Binary Semaphore Posix better error handling --------- Co-authored-by: Claude Opus 4.6 Co-authored-by: Ben Meadors Co-authored-by: Jonathan Bennett --- src/concurrency/BinarySemaphorePosix.cpp | 78 +++++++++++++++++++++++- src/concurrency/BinarySemaphorePosix.h | 13 +++- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/concurrency/BinarySemaphorePosix.cpp b/src/concurrency/BinarySemaphorePosix.cpp index dc49a489b..4bc60c31f 100644 --- a/src/concurrency/BinarySemaphorePosix.cpp +++ b/src/concurrency/BinarySemaphorePosix.cpp @@ -1,10 +1,85 @@ #include "concurrency/BinarySemaphorePosix.h" #include "configuration.h" +#include +#include + #ifndef HAS_FREE_RTOS namespace concurrency { +#ifdef ARCH_PORTDUINO + +BinarySemaphorePosix::BinarySemaphorePosix() +{ + if (pthread_mutex_init(&mutex, NULL) != 0) { + throw std::runtime_error("pthread_mutex_init failed"); + } + if (pthread_cond_init(&cond, NULL) != 0) { + pthread_mutex_destroy(&mutex); + throw std::runtime_error("pthread_cond_init failed"); + } + signaled = false; +} + +BinarySemaphorePosix::~BinarySemaphorePosix() +{ + pthread_cond_destroy(&cond); + pthread_mutex_destroy(&mutex); +} + +/** + * Returns false if we timed out + */ +bool BinarySemaphorePosix::take(uint32_t msec) +{ + pthread_mutex_lock(&mutex); + + if (!signaled) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + + ts.tv_sec += msec / 1000; + ts.tv_nsec += (msec % 1000) * 1000000L; + if (ts.tv_nsec >= 1000000000L) { + ts.tv_sec += 1; + ts.tv_nsec -= 1000000000L; + } + + while (!signaled) { + int rc = pthread_cond_timedwait(&cond, &mutex, &ts); + if (rc == ETIMEDOUT) + break; + if (rc != 0) { + // Some other error occurred + pthread_mutex_unlock(&mutex); + throw std::runtime_error("pthread_cond_timedwait failed: " + std::to_string(rc)); + } + } + } + + bool wasSignaled = signaled; + signaled = false; // consume the signal (binary semaphore) + + pthread_mutex_unlock(&mutex); + return wasSignaled; +} + +void BinarySemaphorePosix::give() +{ + pthread_mutex_lock(&mutex); + signaled = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); +} + +IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) +{ + give(); + if (pxHigherPriorityTaskWoken) + *pxHigherPriorityTaskWoken = true; +} +#else BinarySemaphorePosix::BinarySemaphorePosix() {} @@ -22,7 +97,8 @@ bool BinarySemaphorePosix::take(uint32_t msec) void BinarySemaphorePosix::give() {} IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) {} +#endif } // namespace concurrency -#endif \ No newline at end of file +#endif diff --git a/src/concurrency/BinarySemaphorePosix.h b/src/concurrency/BinarySemaphorePosix.h index 475b29874..80edb567b 100644 --- a/src/concurrency/BinarySemaphorePosix.h +++ b/src/concurrency/BinarySemaphorePosix.h @@ -2,6 +2,10 @@ #include "../freertosinc.h" +#ifdef ARCH_PORTDUINO +#include +#endif + namespace concurrency { @@ -9,7 +13,12 @@ namespace concurrency class BinarySemaphorePosix { - // SemaphoreHandle_t semaphore; + +#ifdef ARCH_PORTDUINO + pthread_mutex_t mutex; + pthread_cond_t cond; + bool signaled; +#endif public: BinarySemaphorePosix(); @@ -27,4 +36,4 @@ class BinarySemaphorePosix #endif -} // namespace concurrency \ No newline at end of file +} // namespace concurrency