* 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 <noreply@anthropic.com> * 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 <noreply@anthropic.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
105 lines
2.3 KiB
C++
105 lines
2.3 KiB
C++
#include "concurrency/BinarySemaphorePosix.h"
|
|
#include "configuration.h"
|
|
|
|
#include <errno.h>
|
|
#include <sys/time.h>
|
|
|
|
#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() {}
|
|
|
|
BinarySemaphorePosix::~BinarySemaphorePosix() {}
|
|
|
|
/**
|
|
* Returns false if we timed out
|
|
*/
|
|
bool BinarySemaphorePosix::take(uint32_t msec)
|
|
{
|
|
delay(msec); // FIXME
|
|
return false;
|
|
}
|
|
|
|
void BinarySemaphorePosix::give() {}
|
|
|
|
IRAM_ATTR void BinarySemaphorePosix::giveFromISR(BaseType_t *pxHigherPriorityTaskWoken) {}
|
|
#endif
|
|
|
|
} // namespace concurrency
|
|
|
|
#endif
|