mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 20:09:17 +00:00
initial_advert_work race
This commit is contained in:
@@ -90,6 +90,7 @@ extern "C" void bt_ctlr_assert_handle(char *file, uint32_t line)
|
||||
#define MESH_EVENT_TX_DRAIN BIT(6) /* Outbound packet delay expired, run checkSend */
|
||||
#define MESH_EVENT_PREFS_DIRTY BIT(8) /* Prefs mutated off-main; main flushes to flash */
|
||||
#define MESH_EVENT_RTC_SAVE BIT(9) /* Hardware-RTC write requested off-main */
|
||||
#define MESH_EVENT_CONTACT_ITER BIT(10) /* Continue contact-dump iteration on main thread */
|
||||
|
||||
#ifdef ZEPHCORE_LORA
|
||||
/* Forward decl — data_store + companion_mesh_ptr statics are defined further
|
||||
@@ -108,7 +109,7 @@ static atomic_t pending_rtc_epoch = ATOMIC_INIT(0);
|
||||
#define MESH_EVENT_BASE (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE | \
|
||||
MESH_EVENT_BLE_RX | MESH_EVENT_HOUSEKEEPING | MESH_EVENT_UI_ACTION | \
|
||||
MESH_EVENT_GPS_ACTION | MESH_EVENT_TX_DRAIN | MESH_EVENT_PREFS_DIRTY | \
|
||||
MESH_EVENT_RTC_SAVE)
|
||||
MESH_EVENT_RTC_SAVE | MESH_EVENT_CONTACT_ITER)
|
||||
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
|
||||
#define MESH_EVENT_JOYSTICK_LOOP BIT(7) /* Joystick UI loop tick (50 ms) */
|
||||
#define MESH_EVENT_ALL (MESH_EVENT_BASE | MESH_EVENT_JOYSTICK_LOOP)
|
||||
@@ -134,7 +135,7 @@ static void request_rtc_save(uint32_t epoch)
|
||||
|
||||
/* Work items for event-driven processing */
|
||||
static void process_companion_rx(void); /* runs on MAIN thread (see ble_on_rx_frame) */
|
||||
static void contact_iter_work_fn(struct k_work *work);
|
||||
static void run_contact_iteration(void); /* runs on MAIN thread (see MESH_EVENT_CONTACT_ITER) */
|
||||
static void housekeeping_timer_fn(struct k_timer *timer);
|
||||
#if ZEPHCORE_USB_STACK
|
||||
static void companion_cli_run(const char *line); /* main-thread text-CLI exec */
|
||||
@@ -154,8 +155,6 @@ static void joystick_signal_tx(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
K_WORK_DEFINE(contact_iter_work, contact_iter_work_fn);
|
||||
|
||||
#if ZEPHCORE_USB_STACK
|
||||
/* Completed USB text-CLI lines are run on the MAIN thread (the USB adapter
|
||||
* assembles them on sysworkq). CommonCLI::handleCommand mutates mesh state
|
||||
@@ -169,10 +168,12 @@ K_MSGQ_DEFINE(companion_cli_queue, sizeof(struct companion_cli_line), 4, 4);
|
||||
#if ZEPHCORE_USB_STACK
|
||||
/* USB TX-drained callback (mirrors BLE on_tx_idle): the TX ring emptied, so
|
||||
* resume the contact pump to queue the next batch. Runs in the CDC TX
|
||||
* interrupt-callback context — k_work_submit is safe there. */
|
||||
* interrupt-callback context — k_event_post is ISR-safe. The contact dump
|
||||
* reads the contact table, which the main thread can mutate (addContact on
|
||||
* inbound advert), so it MUST run on the main thread, not here. */
|
||||
static void usb_on_tx_drain(void)
|
||||
{
|
||||
k_work_submit(&contact_iter_work);
|
||||
k_event_post(&mesh_events, MESH_EVENT_CONTACT_ITER);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -218,10 +219,11 @@ static void ble_on_rx_frame(const uint8_t *data, uint16_t len)
|
||||
}
|
||||
|
||||
/* BLE TX idle callback — called when TX queue is empty.
|
||||
* Continues contact iteration. */
|
||||
* Continues contact iteration on the MAIN thread (the dump reads the contact
|
||||
* table, which the main thread can mutate — so it can't run on sysworkq). */
|
||||
static void ble_on_tx_idle(void)
|
||||
{
|
||||
k_work_submit(&contact_iter_work);
|
||||
k_event_post(&mesh_events, MESH_EVENT_CONTACT_ITER);
|
||||
k_event_post(&mesh_events, MESH_EVENT_TX_DRAIN);
|
||||
}
|
||||
|
||||
@@ -390,10 +392,12 @@ static void process_companion_rx(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Contact iteration work - runs when TX queue has space */
|
||||
static void contact_iter_work_fn(struct k_work *work)
|
||||
/* Continue the contact-dump iteration when TX has space. Runs on the MAIN
|
||||
* thread (driven by MESH_EVENT_CONTACT_ITER, posted from the BLE/USB tx-idle
|
||||
* callbacks) so reading the contact table never races a main-thread
|
||||
* addContact() from an inbound advert. */
|
||||
static void run_contact_iteration(void)
|
||||
{
|
||||
ARG_UNUSED(work);
|
||||
#ifdef ZEPHCORE_LORA
|
||||
if (!companion_mesh_ptr) {
|
||||
return;
|
||||
@@ -484,6 +488,12 @@ static void mesh_event_loop(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Continue contact-dump iteration on the main thread (see
|
||||
* run_contact_iteration / MESH_EVENT_CONTACT_ITER). */
|
||||
if (events & MESH_EVENT_CONTACT_ITER) {
|
||||
run_contact_iteration();
|
||||
}
|
||||
|
||||
/* Packet processing — only on radio/BLE/TX events */
|
||||
if (companion_mesh_ptr &&
|
||||
(events & (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE |
|
||||
@@ -1176,7 +1186,8 @@ int main(void)
|
||||
*
|
||||
* Other processing still uses work queues:
|
||||
* - BLE TX: write_frame -> tx_drain_work (event-driven via notify callback)
|
||||
* - Contact iteration: contact_iter_work (on TX queue space)
|
||||
* - Contact iteration: MESH_EVENT_CONTACT_ITER -> run_contact_iteration()
|
||||
* on the main thread, paced by the BLE/USB tx-idle callbacks
|
||||
*
|
||||
* TX completion is now event-driven: radio polls at 5ms intervals internally
|
||||
* and fires callback when done. Much better than 100ms main loop polling!
|
||||
|
||||
@@ -89,7 +89,8 @@ static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
|
||||
#define MESH_EVENT_GPS_ACTION BIT(4) /* GPS state change (must run on main thread!) */
|
||||
#define MESH_EVENT_TX_DRAIN BIT(5) /* Outbound packet delay expired, run checkSend */
|
||||
#define MESH_EVENT_RTC_SAVE BIT(6) /* Hardware-RTC write requested off-main */
|
||||
#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE | MESH_EVENT_CLI_RX | MESH_EVENT_HOUSEKEEPING | MESH_EVENT_GPS_ACTION | MESH_EVENT_TX_DRAIN | MESH_EVENT_RTC_SAVE)
|
||||
#define MESH_EVENT_INIT_ADVERT BIT(7) /* Deferred boot advert — send on main thread */
|
||||
#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE | MESH_EVENT_CLI_RX | MESH_EVENT_HOUSEKEEPING | MESH_EVENT_GPS_ACTION | MESH_EVENT_TX_DRAIN | MESH_EVENT_RTC_SAVE | MESH_EVENT_INIT_ADVERT)
|
||||
|
||||
/* Housekeeping interval - infrequent to preserve power savings */
|
||||
#define HOUSEKEEPING_INTERVAL_MS CONFIG_ZEPHCORE_HOUSEKEEPING_INTERVAL_MS
|
||||
@@ -271,16 +272,14 @@ static void tx_drain_work_fn(struct k_work *work)
|
||||
k_event_post(&mesh_events, MESH_EVENT_TX_DRAIN);
|
||||
}
|
||||
|
||||
/* Deferred initial advertisement — gives GPS time to get a fix at boot */
|
||||
/* Deferred initial advertisement — gives GPS time to get a fix at boot.
|
||||
* Runs on sysworkq (the 10 s delay timer), so it must NOT send the advert
|
||||
* here: sendSelfAdvertisement() mutates the packet pool / dispatcher shared
|
||||
* with loop(). Post an event and let the main thread send it. */
|
||||
static void initial_advert_work_fn(struct k_work *work)
|
||||
{
|
||||
ARG_UNUSED(work);
|
||||
#ifdef ZEPHCORE_LORA
|
||||
if (repeater_mesh_ptr) {
|
||||
LOG_INF("Sending deferred initial advertisement");
|
||||
repeater_mesh_ptr->sendSelfAdvertisement(500, false);
|
||||
}
|
||||
#endif
|
||||
k_event_post(&mesh_events, MESH_EVENT_INIT_ADVERT);
|
||||
}
|
||||
|
||||
static void tx_queued_callback(uint32_t delay_ms, void *user_data)
|
||||
@@ -392,6 +391,13 @@ static void repeater_event_loop(void)
|
||||
process_cli_commands();
|
||||
}
|
||||
|
||||
/* Deferred boot advert — sent on the main thread (see
|
||||
* initial_advert_work_fn); loop() below drains the queued packet. */
|
||||
if (repeater_mesh_ptr && (events & MESH_EVENT_INIT_ADVERT)) {
|
||||
LOG_INF("Sending deferred initial advertisement");
|
||||
repeater_mesh_ptr->sendSelfAdvertisement(500, false);
|
||||
}
|
||||
|
||||
/* Packet processing — only on radio/CLI/TX events */
|
||||
if (repeater_mesh_ptr &&
|
||||
(events & (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE |
|
||||
|
||||
@@ -90,7 +90,8 @@ static const struct gpio_dt_spec led1 = GPIO_DT_SPEC_GET(LED1_NODE, gpios);
|
||||
#define MESH_EVENT_TX_DRAIN BIT(5) /* Outbound packet delay expired, run checkSend */
|
||||
#define MESH_EVENT_PUSH_TICK BIT(6) /* Room server: drive the post-sync push engine */
|
||||
#define MESH_EVENT_RTC_SAVE BIT(7) /* Hardware-RTC write requested off-main */
|
||||
#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE | MESH_EVENT_CLI_RX | MESH_EVENT_HOUSEKEEPING | MESH_EVENT_GPS_ACTION | MESH_EVENT_TX_DRAIN | MESH_EVENT_PUSH_TICK | MESH_EVENT_RTC_SAVE)
|
||||
#define MESH_EVENT_INIT_ADVERT BIT(8) /* Deferred boot advert — send on main thread */
|
||||
#define MESH_EVENT_ALL (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE | MESH_EVENT_CLI_RX | MESH_EVENT_HOUSEKEEPING | MESH_EVENT_GPS_ACTION | MESH_EVENT_TX_DRAIN | MESH_EVENT_PUSH_TICK | MESH_EVENT_RTC_SAVE | MESH_EVENT_INIT_ADVERT)
|
||||
|
||||
/* Housekeeping interval - infrequent to preserve power savings */
|
||||
#define HOUSEKEEPING_INTERVAL_MS CONFIG_ZEPHCORE_HOUSEKEEPING_INTERVAL_MS
|
||||
@@ -284,16 +285,14 @@ static void tx_drain_work_fn(struct k_work *work)
|
||||
k_event_post(&mesh_events, MESH_EVENT_TX_DRAIN);
|
||||
}
|
||||
|
||||
/* Deferred initial advertisement — gives GPS time to get a fix at boot */
|
||||
/* Deferred initial advertisement — gives GPS time to get a fix at boot.
|
||||
* Runs on sysworkq (the 10 s delay timer), so it must NOT send the advert
|
||||
* here: sendSelfAdvertisement() mutates the packet pool / dispatcher shared
|
||||
* with loop(). Post an event and let the main thread send it. */
|
||||
static void initial_advert_work_fn(struct k_work *work)
|
||||
{
|
||||
ARG_UNUSED(work);
|
||||
#ifdef ZEPHCORE_LORA
|
||||
if (room_mesh_ptr) {
|
||||
LOG_INF("Sending deferred initial advertisement");
|
||||
room_mesh_ptr->sendSelfAdvertisement(500, false);
|
||||
}
|
||||
#endif
|
||||
k_event_post(&mesh_events, MESH_EVENT_INIT_ADVERT);
|
||||
}
|
||||
|
||||
static void tx_queued_callback(uint32_t delay_ms, void *user_data)
|
||||
@@ -409,6 +408,13 @@ static void room_event_loop(void)
|
||||
process_cli_commands();
|
||||
}
|
||||
|
||||
/* Deferred boot advert — sent on the main thread (see
|
||||
* initial_advert_work_fn); loop() below drains the queued packet. */
|
||||
if (room_mesh_ptr && (events & MESH_EVENT_INIT_ADVERT)) {
|
||||
LOG_INF("Sending deferred initial advertisement");
|
||||
room_mesh_ptr->sendSelfAdvertisement(500, false);
|
||||
}
|
||||
|
||||
/* Packet processing — only on radio/CLI/TX events */
|
||||
if (room_mesh_ptr &&
|
||||
(events & (MESH_EVENT_LORA_RX | MESH_EVENT_LORA_TX_DONE |
|
||||
|
||||
Reference in New Issue
Block a user