mirror of
https://github.com/google/pebble.git
synced 2025-05-19 09:54:55 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
617
src/fw/services/normal/app_inbox_service.c
Normal file
617
src/fw/services/normal/app_inbox_service.c
Normal file
|
@ -0,0 +1,617 @@
|
|||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "app_inbox_service.h"
|
||||
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "kernel/pebble_tasks.h"
|
||||
#include "process_management/process_manager.h"
|
||||
#include "os/mutex.h"
|
||||
#include "syscall/syscall_internal.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/buffer.h"
|
||||
#include "util/list.h"
|
||||
|
||||
typedef struct AppInboxNode {
|
||||
ListNode node;
|
||||
AppInboxServiceTag tag;
|
||||
AppInboxMessageHandler message_handler;
|
||||
AppInboxDroppedHandler dropped_handler;
|
||||
PebbleTask event_handler_task;
|
||||
|
||||
//! Indicates whether there is a writer.
|
||||
//! The writer can set it to anything they want, mostly for debugging purposes.
|
||||
void *writer;
|
||||
bool write_failed;
|
||||
bool has_pending_event;
|
||||
|
||||
uint32_t num_failed;
|
||||
uint32_t num_success;
|
||||
|
||||
struct {
|
||||
//! The size of `storage`.
|
||||
size_t size;
|
||||
|
||||
//! The positive offset relative relative to write_index, up until which the current
|
||||
//! (incomplete) message has been written.
|
||||
size_t current_offset;
|
||||
|
||||
//! Index after which the current message should get written.
|
||||
//! If this index is non-zero, there are completed message(s) in the buffer.
|
||||
size_t write_index;
|
||||
|
||||
///! Pointer to the beginning of the storage.
|
||||
uint8_t *storage;
|
||||
} buffer;
|
||||
} AppInboxNode;
|
||||
|
||||
typedef struct AppInboxConsumerInfo {
|
||||
AppInboxServiceTag tag;
|
||||
AppInboxMessageHandler message_handler;
|
||||
AppInboxDroppedHandler dropped_handler;
|
||||
uint32_t num_failed;
|
||||
uint32_t num_success;
|
||||
uint8_t *it;
|
||||
uint8_t *end;
|
||||
} AppInboxConsumerInfo;
|
||||
|
||||
|
||||
_Static_assert(sizeof(AppInboxServiceTag) <= sizeof(void *),
|
||||
"AppInboxServiceTag should fit inside a void *");
|
||||
|
||||
static AppInboxNode *s_app_inbox_head;
|
||||
|
||||
static PebbleRecursiveMutex *s_app_inbox_mutex;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Declarations of permitted handlers:
|
||||
|
||||
extern void app_message_receiver_message_handler(const uint8_t *data, size_t length,
|
||||
AppInboxConsumerInfo *consumer_info);
|
||||
extern void app_message_receiver_dropped_handler(uint32_t num_dropped_messages);
|
||||
|
||||
#ifdef UNITTEST
|
||||
extern void test_message_handler(const uint8_t *data, size_t length,
|
||||
AppInboxConsumerInfo *consumer_info);
|
||||
extern void test_dropped_handler(uint32_t num_dropped_messages);
|
||||
extern void test_alt_message_handler(const uint8_t *data, size_t length,
|
||||
AppInboxConsumerInfo *consumer_info);
|
||||
extern void test_alt_dropped_handler(uint32_t num_dropped_messages);
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Syscalls
|
||||
|
||||
static AppInboxServiceTag prv_tag_for_event_handlers(const AppInboxMessageHandler message_handler,
|
||||
const AppInboxDroppedHandler dropped_handler) {
|
||||
static const struct {
|
||||
AppInboxMessageHandler message_handler;
|
||||
AppInboxDroppedHandler dropped_handler;
|
||||
} s_event_handler_map[] = {
|
||||
[AppInboxServiceTagAppMessageReceiver] = {
|
||||
.message_handler = app_message_receiver_message_handler,
|
||||
.dropped_handler = app_message_receiver_dropped_handler,
|
||||
},
|
||||
#ifdef UNITTEST
|
||||
[AppInboxServiceTagUnitTest] = {
|
||||
.message_handler = test_message_handler,
|
||||
.dropped_handler = test_dropped_handler,
|
||||
},
|
||||
[AppInboxServiceTagUnitTestAlt] = {
|
||||
.message_handler = test_alt_message_handler,
|
||||
.dropped_handler = test_alt_dropped_handler,
|
||||
}
|
||||
#endif
|
||||
};
|
||||
for (AppInboxServiceTag tag = 0; tag < NumAppInboxServiceTag; ++tag) {
|
||||
if (s_event_handler_map[tag].message_handler == message_handler &&
|
||||
s_event_handler_map[tag].dropped_handler == dropped_handler) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return AppInboxServiceTagInvalid;
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(bool, sys_app_inbox_service_register, uint8_t *storage, size_t storage_size,
|
||||
AppInboxMessageHandler message_handler, AppInboxDroppedHandler dropped_handler) {
|
||||
if (PRIVILEGE_WAS_ELEVATED) {
|
||||
syscall_assert_userspace_buffer(storage, storage_size);
|
||||
}
|
||||
const AppInboxServiceTag service_tag = prv_tag_for_event_handlers(message_handler,
|
||||
dropped_handler);
|
||||
if (AppInboxServiceTagInvalid == service_tag) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "AppInbox event handlers not allowed <0x%"PRIx32", 0x%"PRIx32">",
|
||||
// Ugh.. no more format signature slots free for %p %p...
|
||||
(uint32_t)(uintptr_t)message_handler, (uint32_t)(uintptr_t)dropped_handler);
|
||||
syscall_failed();
|
||||
}
|
||||
|
||||
return app_inbox_service_register(storage, storage_size,
|
||||
message_handler, dropped_handler, service_tag);
|
||||
}
|
||||
|
||||
DEFINE_SYSCALL(uint32_t, sys_app_inbox_service_unregister, uint8_t *storage) {
|
||||
// No check is needed on the value of `storage `, we're not going to derefence it.
|
||||
return app_inbox_service_unregister_by_storage(storage);
|
||||
}
|
||||
|
||||
static bool prv_get_consumer_info(AppInboxServiceTag tag, AppInboxConsumerInfo *info_in_out);
|
||||
|
||||
DEFINE_SYSCALL(bool, sys_app_inbox_service_get_consumer_info,
|
||||
AppInboxServiceTag tag, AppInboxConsumerInfo *info_out) {
|
||||
if (PRIVILEGE_WAS_ELEVATED) {
|
||||
if (info_out) {
|
||||
syscall_assert_userspace_buffer(info_out, sizeof(*info_out));
|
||||
}
|
||||
}
|
||||
return prv_get_consumer_info(tag, info_out);
|
||||
}
|
||||
|
||||
static void prv_consume(AppInboxConsumerInfo *consumer_info);
|
||||
|
||||
DEFINE_SYSCALL(void, sys_app_inbox_service_consume, AppInboxConsumerInfo *consumer_info) {
|
||||
if (PRIVILEGE_WAS_ELEVATED) {
|
||||
syscall_assert_userspace_buffer(consumer_info, sizeof(*consumer_info));
|
||||
}
|
||||
prv_consume(consumer_info);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void prv_lock(void) {
|
||||
// Using one "global" lock for all app inboxes.
|
||||
// If needed, we could easily give each app inbox its own mutex, but it seems overkill right now.
|
||||
mutex_lock_recursive(s_app_inbox_mutex);
|
||||
}
|
||||
|
||||
static void prv_unlock(void) {
|
||||
mutex_unlock_recursive(s_app_inbox_mutex);
|
||||
}
|
||||
|
||||
static bool prv_list_filter_by_storage(ListNode *found_node, void *data) {
|
||||
return ((AppInboxNode *)found_node)->buffer.storage == (uint8_t *)data;
|
||||
}
|
||||
|
||||
static AppInboxNode *prv_find_inbox_by_storage(uint8_t *storage) {
|
||||
return (AppInboxNode *) list_find((ListNode *)s_app_inbox_head,
|
||||
prv_list_filter_by_storage, storage);
|
||||
}
|
||||
|
||||
static bool prv_list_filter_by_tag(ListNode *found_node, void *data) {
|
||||
return ((AppInboxNode *)found_node)->tag == (AppInboxServiceTag)(uintptr_t)data;
|
||||
}
|
||||
|
||||
static AppInboxNode *prv_find_inbox_by_tag(AppInboxServiceTag tag) {
|
||||
return (AppInboxNode *) list_find((ListNode *)s_app_inbox_head,
|
||||
prv_list_filter_by_tag, (void *)(uintptr_t)tag);
|
||||
}
|
||||
|
||||
static AppInboxNode *prv_find_inbox_by_tag_and_log_if_not_found(AppInboxServiceTag tag) {
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag(tag);
|
||||
if (!inbox) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "No AppInbox for tag <%d>", tag);
|
||||
}
|
||||
return inbox;
|
||||
}
|
||||
|
||||
//! We don't report "number of messages consumed", because that would force the system to parse
|
||||
//! the contents of the (app space) buffer, which might have been corrupted by the app.
|
||||
//! Note that it's in theory possible for a misbehaving app to pass in a consumed_up_to_ptr that is
|
||||
//! mid-way in a message. If it does so, it won't crash the kernel, but it will result in delivery
|
||||
//! of broken messages to the app, but it won't be our fault...
|
||||
static void prv_consume(AppInboxConsumerInfo *consumer_info) {
|
||||
prv_lock();
|
||||
{
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(consumer_info->tag);
|
||||
if (!inbox) {
|
||||
goto unlock;
|
||||
}
|
||||
uint8_t *const consumed_up_to_ptr = consumer_info->it;
|
||||
uint8_t * const completed_messages_end = (inbox->buffer.storage + inbox->buffer.write_index);
|
||||
if (consumed_up_to_ptr < inbox->buffer.storage ||
|
||||
consumed_up_to_ptr > completed_messages_end) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Out of bounds");
|
||||
goto unlock;
|
||||
}
|
||||
const size_t bytes_consumed = (consumed_up_to_ptr - inbox->buffer.storage);
|
||||
if (0 == bytes_consumed) {
|
||||
goto unlock;
|
||||
}
|
||||
uint8_t * const partial_message_end = completed_messages_end + inbox->buffer.current_offset;
|
||||
const size_t remaining_size = partial_message_end - consumed_up_to_ptr;
|
||||
consumer_info->it = inbox->buffer.storage;
|
||||
consumer_info->end = inbox->buffer.storage + remaining_size;
|
||||
if (remaining_size) {
|
||||
// New data has been written in the mean-time, move it all to the front of the buffer:
|
||||
memmove(inbox->buffer.storage, consumed_up_to_ptr, remaining_size);
|
||||
}
|
||||
inbox->buffer.write_index -= bytes_consumed;
|
||||
}
|
||||
unlock:
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
static bool prv_get_consumer_info(AppInboxServiceTag tag, AppInboxConsumerInfo *info_out) {
|
||||
if (!info_out) {
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
prv_lock();
|
||||
{
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag);
|
||||
if (!inbox) {
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
*info_out = (const AppInboxConsumerInfo) {
|
||||
.tag = tag,
|
||||
.message_handler = inbox->message_handler,
|
||||
.dropped_handler = inbox->dropped_handler,
|
||||
.num_failed = inbox->num_failed,
|
||||
.num_success = inbox->num_success,
|
||||
.it = inbox->buffer.storage,
|
||||
.end = inbox->buffer.storage + inbox->buffer.write_index,
|
||||
};
|
||||
|
||||
// Also mark that there is no event pending any more:
|
||||
inbox->has_pending_event = false;
|
||||
|
||||
// Reset counters because the info is communicated to app and it's about to consume the data.
|
||||
inbox->num_failed = 0;
|
||||
inbox->num_success = 0;
|
||||
|
||||
success = true;
|
||||
}
|
||||
unlock:
|
||||
prv_unlock();
|
||||
return success;
|
||||
}
|
||||
|
||||
//! @note Executes on app task, therefore we need to go through syscalls to access AppInbox!
|
||||
static void prv_callback_event_handler(void *ctx) {
|
||||
AppInboxServiceTag tag = (AppInboxServiceTag)(uintptr_t)ctx;
|
||||
AppInboxConsumerInfo info = {};
|
||||
size_t num_message_consumed = 0;
|
||||
if (!sys_app_inbox_service_get_consumer_info(tag, &info)) {
|
||||
// Inbox wasn't there any more
|
||||
return;
|
||||
}
|
||||
if (!info.message_handler) {
|
||||
// Shouldn't ever happen, but better not PBL_ASSERTN on app task
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "No AppInbox message handler!");
|
||||
return;
|
||||
}
|
||||
if (!info.num_success && !info.num_failed) {
|
||||
// Shouldn't ever happen, but better not PBL_ASSERTN on app task
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Got callback, but zero messages!?");
|
||||
// fall-through
|
||||
}
|
||||
|
||||
// These conditions are redundant, just for safety:
|
||||
while ((num_message_consumed < info.num_success) && (info.it < info.end)) {
|
||||
AppInboxMessageHeader *msg = (AppInboxMessageHeader *)info.it;
|
||||
|
||||
// Increment now so that if the message_handler calls into sys_app_inbox_service_consume(),
|
||||
// it will be pointing *after* the message that is just handled:
|
||||
info.it += (sizeof(AppInboxMessageHeader) + msg->length);
|
||||
|
||||
// Check for safety, just in case the app has corrupted the buffer in the mean time:
|
||||
if (msg->data + msg->length <= info.end) {
|
||||
info.message_handler(msg->data, msg->length, &info);
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Corrupted AppInbox message!");
|
||||
}
|
||||
++num_message_consumed;
|
||||
}
|
||||
|
||||
if (info.num_failed) {
|
||||
if (info.dropped_handler) {
|
||||
info.dropped_handler(info.num_failed);
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Dropped %"PRIu32" messages but no dropped_handler",
|
||||
info.num_failed);
|
||||
}
|
||||
}
|
||||
|
||||
// Report back up to which byte we've consumed the data.
|
||||
sys_app_inbox_service_consume(&info);
|
||||
}
|
||||
|
||||
bool app_inbox_service_register(uint8_t *storage, size_t storage_size,
|
||||
AppInboxMessageHandler message_handler,
|
||||
AppInboxDroppedHandler dropped_handler, AppInboxServiceTag tag) {
|
||||
AppInboxNode *new_node = (AppInboxNode *)kernel_zalloc(sizeof(AppInboxNode));
|
||||
if (!new_node) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Not enough memory to allocate AppInboxNode");
|
||||
return false;
|
||||
}
|
||||
|
||||
prv_lock();
|
||||
{
|
||||
bool has_error = false;
|
||||
|
||||
if (prv_find_inbox_by_storage(storage)) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "AppInbox already registered for storage <%p>", storage);
|
||||
has_error = true;
|
||||
}
|
||||
|
||||
// This check effectively caps the kernel RAM impact of this service,
|
||||
// so it's not possible to abuse the syscall and cause kernel OOM.
|
||||
if (prv_find_inbox_by_tag(tag)) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "AppInbox already registered for tag <%d>", tag);
|
||||
has_error = true;
|
||||
}
|
||||
|
||||
if (has_error) {
|
||||
kernel_free(new_node);
|
||||
new_node = NULL;
|
||||
} else {
|
||||
new_node->tag = tag;
|
||||
new_node->message_handler = message_handler;
|
||||
new_node->dropped_handler = dropped_handler;
|
||||
new_node->event_handler_task = pebble_task_get_current();
|
||||
new_node->buffer.storage = storage;
|
||||
new_node->buffer.size = storage_size;
|
||||
s_app_inbox_head = (AppInboxNode *)list_prepend((ListNode *)s_app_inbox_head,
|
||||
(ListNode *)new_node);
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
|
||||
return (new_node != NULL);
|
||||
}
|
||||
|
||||
uint32_t app_inbox_service_unregister_by_storage(uint8_t *storage) {
|
||||
uint32_t num_messages_lost = 0;
|
||||
prv_lock();
|
||||
{
|
||||
AppInboxNode *node = prv_find_inbox_by_storage(storage);
|
||||
if (node) {
|
||||
list_remove((ListNode *)node, (ListNode **)&s_app_inbox_head, NULL);
|
||||
num_messages_lost = node->num_failed + node->num_success + (node->writer ? 1 : 0);
|
||||
kernel_free(node);
|
||||
}
|
||||
}
|
||||
prv_unlock();
|
||||
return num_messages_lost;
|
||||
}
|
||||
|
||||
void app_inbox_service_unregister_all(void) {
|
||||
prv_lock();
|
||||
{
|
||||
AppInboxNode *node = s_app_inbox_head;
|
||||
while (node) {
|
||||
AppInboxNode *next = (AppInboxNode *) node->node.next;
|
||||
kernel_free(node);
|
||||
node = next;
|
||||
}
|
||||
s_app_inbox_head = NULL;
|
||||
}
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
static bool prv_is_inbox_being_written(AppInboxNode *inbox) {
|
||||
return (inbox->writer != NULL);
|
||||
}
|
||||
|
||||
static size_t prv_get_space_remaining(AppInboxNode *inbox) {
|
||||
return (inbox->buffer.size - inbox->buffer.write_index - inbox->buffer.current_offset);
|
||||
}
|
||||
|
||||
bool prv_check_space_remaining(AppInboxNode *inbox, size_t required_free_length) {
|
||||
const size_t space_remaining = prv_get_space_remaining(inbox);
|
||||
if (required_free_length > space_remaining) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Dropping data, not enough space %"PRIu32" vs %"PRIu32,
|
||||
(uint32_t)required_free_length, (uint32_t)space_remaining);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prv_send_event_if_needed(AppInboxNode *inbox) {
|
||||
if (!inbox || inbox->has_pending_event) {
|
||||
return;
|
||||
}
|
||||
PebbleEvent event = {
|
||||
.type = PEBBLE_CALLBACK_EVENT,
|
||||
.callback = {
|
||||
.callback = prv_callback_event_handler,
|
||||
.data = (void *)(uintptr_t) inbox->tag,
|
||||
},
|
||||
};
|
||||
const bool is_event_enqueued = process_manager_send_event_to_process(inbox->event_handler_task,
|
||||
&event);
|
||||
if (!is_event_enqueued) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Event queue full");
|
||||
}
|
||||
inbox->has_pending_event = is_event_enqueued;
|
||||
}
|
||||
|
||||
static void prv_mark_failed_if_no_writer(AppInboxNode *inbox) {
|
||||
if (!inbox->writer) {
|
||||
// See PBL-41464
|
||||
// App message has been reset (closed and opened again) while a message was being received.
|
||||
// Fail it because our state got lost.
|
||||
inbox->write_failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool app_inbox_service_begin(AppInboxServiceTag tag, size_t required_free_length, void *writer) {
|
||||
if (!writer) {
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
prv_lock();
|
||||
{
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag);
|
||||
if (!inbox) {
|
||||
goto unlock;
|
||||
}
|
||||
if (prv_is_inbox_being_written(inbox)) {
|
||||
++inbox->num_failed;
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Dropping data, already written by <%p>", inbox->writer);
|
||||
// Don't send event here, when the current write finishes, the drop(s) will be reported too.
|
||||
goto unlock;
|
||||
}
|
||||
if (!prv_check_space_remaining(inbox, required_free_length + sizeof(AppInboxMessageHeader))) {
|
||||
++inbox->num_failed;
|
||||
// If it doesn't fit, send event immediately, we don't know when the next write will happen.
|
||||
prv_send_event_if_needed(inbox);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
inbox->writer = writer;
|
||||
inbox->write_failed = false;
|
||||
// Leave space at the beginning for the header, which we'll write in the end
|
||||
inbox->buffer.current_offset = sizeof(AppInboxMessageHeader);
|
||||
success = true;
|
||||
}
|
||||
unlock:
|
||||
prv_unlock();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool app_inbox_service_write(AppInboxServiceTag tag, const uint8_t *data, size_t length) {
|
||||
bool success = false;
|
||||
prv_lock();
|
||||
{
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag);
|
||||
if (!inbox) {
|
||||
goto unlock;
|
||||
}
|
||||
prv_mark_failed_if_no_writer(inbox);
|
||||
if (inbox->write_failed) {
|
||||
goto unlock;
|
||||
}
|
||||
if (!prv_check_space_remaining(inbox, length)) {
|
||||
inbox->write_failed = true;
|
||||
goto unlock;
|
||||
}
|
||||
memcpy(inbox->buffer.storage + inbox->buffer.write_index + inbox->buffer.current_offset,
|
||||
data, length);
|
||||
inbox->buffer.current_offset += length;
|
||||
success = true;
|
||||
}
|
||||
unlock:
|
||||
prv_unlock();
|
||||
return success;
|
||||
}
|
||||
|
||||
static void prv_finish(AppInboxNode *inbox) {
|
||||
inbox->writer = NULL;
|
||||
inbox->buffer.current_offset = 0;
|
||||
}
|
||||
|
||||
void app_inbox_service_init(void) {
|
||||
s_app_inbox_mutex = mutex_create_recursive();
|
||||
}
|
||||
|
||||
bool app_inbox_service_end(AppInboxServiceTag tag) {
|
||||
bool success = false;
|
||||
prv_lock();
|
||||
{
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag);
|
||||
if (!inbox) {
|
||||
goto unlock;
|
||||
}
|
||||
prv_mark_failed_if_no_writer(inbox);
|
||||
if (inbox->write_failed) {
|
||||
++inbox->num_failed;
|
||||
} else {
|
||||
const AppInboxMessageHeader header = (const AppInboxMessageHeader) {
|
||||
.length = inbox->buffer.current_offset - sizeof(AppInboxMessageHeader),
|
||||
// Fill with something that might aid debugging one day:
|
||||
.padding = { 0xaa, 0xaa, 0xaa, 0xaa },
|
||||
};
|
||||
memcpy(inbox->buffer.storage + inbox->buffer.write_index, &header, sizeof(header));
|
||||
inbox->buffer.write_index += inbox->buffer.current_offset;
|
||||
++inbox->num_success;
|
||||
success = true;
|
||||
}
|
||||
prv_finish(inbox);
|
||||
|
||||
prv_send_event_if_needed(inbox);
|
||||
}
|
||||
unlock:
|
||||
prv_unlock();
|
||||
return success;
|
||||
}
|
||||
|
||||
void app_inbox_service_cancel(AppInboxServiceTag tag) {
|
||||
prv_lock();
|
||||
{
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag_and_log_if_not_found(tag);
|
||||
if (!inbox) {
|
||||
goto unlock;
|
||||
}
|
||||
prv_finish(inbox);
|
||||
}
|
||||
unlock:
|
||||
prv_unlock();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Unit Test Interfaces
|
||||
|
||||
bool app_inbox_service_has_inbox_for_tag(AppInboxServiceTag tag) {
|
||||
bool has_inbox;
|
||||
prv_lock();
|
||||
has_inbox = (prv_find_inbox_by_tag(tag) != NULL);
|
||||
prv_unlock();
|
||||
return has_inbox;
|
||||
}
|
||||
|
||||
bool app_inbox_service_has_inbox_for_storage(uint8_t *storage) {
|
||||
bool has_inbox;
|
||||
prv_lock();
|
||||
has_inbox = (prv_find_inbox_by_storage(storage) != NULL);
|
||||
prv_unlock();
|
||||
return has_inbox;
|
||||
}
|
||||
|
||||
bool app_inbox_service_is_being_written_for_tag(AppInboxServiceTag tag) {
|
||||
bool is_written = false;
|
||||
prv_lock();
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag(tag);
|
||||
if (inbox) {
|
||||
is_written = (inbox->writer != NULL);
|
||||
}
|
||||
prv_unlock();
|
||||
return is_written;
|
||||
}
|
||||
|
||||
uint32_t app_inbox_service_num_failed_for_tag(AppInboxServiceTag tag) {
|
||||
uint32_t num_failed = 0;
|
||||
prv_lock();
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag(tag);
|
||||
if (inbox) {
|
||||
num_failed = inbox->num_failed;
|
||||
}
|
||||
prv_unlock();
|
||||
return num_failed;
|
||||
}
|
||||
|
||||
uint32_t app_inbox_service_num_success_for_tag(AppInboxServiceTag tag) {
|
||||
uint32_t num_success = 0;
|
||||
prv_lock();
|
||||
AppInboxNode *inbox = prv_find_inbox_by_tag(tag);
|
||||
if (inbox) {
|
||||
num_success = inbox->num_success;
|
||||
}
|
||||
prv_unlock();
|
||||
return num_success;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue