Refactor app loading and window management (#609)
This commit is contained in:
committed by
GitHub
parent
dc3f6104b8
commit
37c507544b
@@ -0,0 +1,115 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#include <app/event.h>
|
||||
|
||||
#include <tactility/concurrent/mutex.h>
|
||||
#include <tactility/time.h>
|
||||
|
||||
/**
|
||||
* Intrusive singly-linked list of subscriptions, keyed by app_instance_id.
|
||||
* Guarded by a single coarse-grained mutex, notifying a subscriber here never invokes caller code
|
||||
* (just a struct copy and an xTaskNotifyGive), so there is no reentrancy concern requiring a snapshot-then-unlock dance.
|
||||
*/
|
||||
static AppEventSubscription* subscriptions = nullptr;
|
||||
|
||||
struct AppEventMutex {
|
||||
Mutex handle {};
|
||||
AppEventMutex() { mutex_construct(&handle); }
|
||||
~AppEventMutex() { mutex_destruct(&handle); }
|
||||
};
|
||||
|
||||
static AppEventMutex subscriptions_mutex;
|
||||
|
||||
extern "C" {
|
||||
|
||||
error_t app_event_subscribe(AppEventSubscription* sub) {
|
||||
sub->task = xTaskGetCurrentTaskHandle();
|
||||
sub->head = 0;
|
||||
sub->count = 0;
|
||||
|
||||
mutex_lock(&subscriptions_mutex.handle);
|
||||
sub->next = subscriptions;
|
||||
subscriptions = sub;
|
||||
mutex_unlock(&subscriptions_mutex.handle);
|
||||
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
error_t app_event_unsubscribe(AppEventSubscription* sub) {
|
||||
error_t result = ERROR_NOT_FOUND;
|
||||
|
||||
mutex_lock(&subscriptions_mutex.handle);
|
||||
for (AppEventSubscription** link = &subscriptions; *link != nullptr; link = &(*link)->next) {
|
||||
if (*link == sub) {
|
||||
*link = sub->next;
|
||||
result = ERROR_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&subscriptions_mutex.handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
error_t app_event_emit(uint32_t app_instance_id, const AppEvent* event) {
|
||||
AppEvent stamped_event = *event;
|
||||
stamped_event.timestamp = get_micros_since_boot();
|
||||
|
||||
error_t result = ERROR_NOT_FOUND;
|
||||
|
||||
mutex_lock(&subscriptions_mutex.handle);
|
||||
for (AppEventSubscription* sub = subscriptions; sub != nullptr; sub = sub->next) {
|
||||
if (sub->app_instance_id != app_instance_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sub->count >= APP_EVENT_QUEUE_CAPACITY) {
|
||||
result = ERROR_RESOURCE;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t tail = (sub->head + sub->count) % APP_EVENT_QUEUE_CAPACITY;
|
||||
sub->queue[tail] = stamped_event;
|
||||
sub->count++;
|
||||
if (result != ERROR_RESOURCE) {
|
||||
result = ERROR_NONE;
|
||||
}
|
||||
xTaskNotifyGive(sub->task);
|
||||
}
|
||||
mutex_unlock(&subscriptions_mutex.handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool try_pop(AppEventSubscription* sub, AppEvent* out_event) {
|
||||
mutex_lock(&subscriptions_mutex.handle);
|
||||
bool has_event = sub->count > 0;
|
||||
if (has_event) {
|
||||
*out_event = sub->queue[sub->head];
|
||||
sub->head = (sub->head + 1) % APP_EVENT_QUEUE_CAPACITY;
|
||||
sub->count--;
|
||||
}
|
||||
mutex_unlock(&subscriptions_mutex.handle);
|
||||
return has_event;
|
||||
}
|
||||
|
||||
error_t app_event_await(AppEventSubscription* sub, AppEvent* out_event, TickType_t timeout) {
|
||||
if (try_pop(sub, out_event)) {
|
||||
// Drain any notification credit this (or an earlier) push accumulated on this task's
|
||||
// FreeRTOS notification value: each app_event_emit() calls xTaskNotifyGive() regardless
|
||||
// of whether the consumer takes this fast path or the blocking path below, so without
|
||||
// this the credit would carry over and cause a future ulTaskNotifyTake() below to
|
||||
// return immediately for a notification that was already accounted for here.
|
||||
ulTaskNotifyTake(pdTRUE, 0);
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
if (ulTaskNotifyTake(pdTRUE, timeout) == 0) {
|
||||
return ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
// Single-consumer by design (one task per subscription), so a wakeup implies the event
|
||||
// this call was notified for is still there for us to pop.
|
||||
return try_pop(sub, out_event) ? ERROR_NONE : ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
Reference in New Issue
Block a user