RPC: Implement storage_stat_request (#800)

* RPC: Update protobuf sources
* RPC: Implement storage_stat_request
* RPC: Test storage_stat_request
* FuriRecord: fix use after free in destroy method.
* Furi: refactor PubSub and it's usage. Fix allocation in RPC.
* FuriCore: fix memory leak in pubsub
* FuriCore: update unsubscribe method signature in pubsub, make subscription structure lighter.
* FuriCore: remove dead code

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
Anna Prosvetova
2021-11-01 23:35:54 +03:00
committed by GitHub
parent b397442d89
commit e9e76e144c
37 changed files with 350 additions and 214 deletions

View File

@@ -69,8 +69,8 @@ Bt* bt_alloc() {
// Power
bt->power = furi_record_open("power");
PubSub* power_pubsub = power_get_pubsub(bt->power);
subscribe_pubsub(power_pubsub, bt_battery_level_changed_callback, bt);
FuriPubSub* power_pubsub = power_get_pubsub(bt->power);
furi_pubsub_subscribe(power_pubsub, bt_battery_level_changed_callback, bt);
// RPC
bt->rpc = furi_record_open("rpc");

View File

@@ -410,7 +410,7 @@ Gui* gui_alloc() {
gui->input_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
gui->input_events = furi_record_open("input_events");
furi_check(gui->input_events);
subscribe_pubsub(gui->input_events, gui_input_events_callback, gui);
furi_pubsub_subscribe(gui->input_events, gui_input_events_callback, gui);
// Cli
gui->cli = furi_record_open("cli");
cli_add_command(

View File

@@ -50,7 +50,7 @@ struct Gui {
// Input
osMessageQueueId_t input_queue;
PubSub* input_events;
FuriPubSub* input_events;
uint8_t ongoing_input;
ViewPort* ongoing_input_view_port;

View File

@@ -2,7 +2,6 @@
#include "icon_i.h"
#include <furi.h>
#include <timers.h>
IconAnimation* icon_animation_alloc(const Icon* icon) {
furi_assert(icon);

View File

@@ -28,11 +28,11 @@ void input_press_timer_callback(void* arg) {
input_pin->press_counter++;
if(input_pin->press_counter == INPUT_LONG_PRESS_COUNTS) {
event.type = InputTypeLong;
notify_pubsub(&input->event_pubsub, &event);
furi_pubsub_publish(input->event_pubsub, &event);
} else if(input_pin->press_counter > INPUT_LONG_PRESS_COUNTS) {
input_pin->press_counter--;
event.type = InputTypeRepeat;
notify_pubsub(&input->event_pubsub, &event);
furi_pubsub_publish(input->event_pubsub, &event);
}
}
@@ -89,7 +89,7 @@ void input_cli_send(Cli* cli, string_t args, void* context) {
return;
}
// Publish input event
notify_pubsub(&input->event_pubsub, &event);
furi_pubsub_publish(input->event_pubsub, &event);
}
const char* input_get_key_name(InputKey key) {
@@ -120,8 +120,8 @@ const char* input_get_type_name(InputType type) {
int32_t input_srv() {
input = furi_alloc(sizeof(Input));
input->thread = osThreadGetId();
init_pubsub(&input->event_pubsub);
furi_record_create("input_events", &input->event_pubsub);
input->event_pubsub = furi_pubsub_alloc();
furi_record_create("input_events", input->event_pubsub);
input->cli = furi_record_open("cli");
if(input->cli) {
@@ -168,14 +168,14 @@ int32_t input_srv() {
input_timer_stop(input->pin_states[i].press_timer);
if(input->pin_states[i].press_counter < INPUT_LONG_PRESS_COUNTS) {
event.type = InputTypeShort;
notify_pubsub(&input->event_pubsub, &event);
furi_pubsub_publish(input->event_pubsub, &event);
}
input->pin_states[i].press_counter = 0;
}
// Send Press/Release event
event.type = input->pin_states[i].state ? InputTypePress : InputTypeRelease;
notify_pubsub(&input->event_pubsub, &event);
furi_pubsub_publish(input->event_pubsub, &event);
}
}

View File

@@ -18,7 +18,7 @@ typedef enum {
InputTypeRepeat, /**< Repeat event, emmited with INPUT_REPEATE_PRESS period after InputTypeLong event */
} InputType;
/** Input Event, dispatches with PubSub */
/** Input Event, dispatches with FuriPubSub */
typedef struct {
uint32_t sequence;
InputKey key;

View File

@@ -6,8 +6,6 @@
#pragma once
#include "input.h"
#include <FreeRTOS.h>
#include <timers.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -35,7 +33,7 @@ typedef struct {
/** Input state */
typedef struct {
osThreadId_t thread;
PubSub event_pubsub;
FuriPubSub* event_pubsub;
InputPinState* pin_states;
Cli* cli;
volatile uint32_t counter;

4
applications/loader/loader.c Executable file → Normal file
View File

@@ -140,7 +140,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
Loader* instance = context;
if(thread_state == FuriThreadStateRunning) {
instance->free_heap_size = xPortGetFreeHeapSize();
instance->free_heap_size = memmgr_get_free_heap();
} else if(thread_state == FuriThreadStateStopped) {
/*
* Current Leak Sanitizer assumes that memory is allocated and freed
@@ -153,7 +153,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
* both values should be taken into account.
*/
delay(20);
int heap_diff = instance->free_heap_size - xPortGetFreeHeapSize();
int heap_diff = instance->free_heap_size - memmgr_get_free_heap();
FURI_LOG_I(
LOADER_LOG_TAG,
"Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.",

View File

@@ -427,7 +427,7 @@ static NotificationApp* notification_app_alloc() {
// display backlight control
app->event_record = furi_record_open("input_events");
subscribe_pubsub(app->event_record, input_event_callback, app);
furi_pubsub_subscribe(app->event_record, input_event_callback, app);
notification_message(app, &sequence_display_on);
return app;

View File

@@ -44,7 +44,7 @@ typedef struct {
struct NotificationApp {
osMessageQueueId_t queue;
PubSub* event_record;
FuriPubSub* event_record;
osTimerId_t display_timer;
NotificationLedLayer display;

View File

@@ -31,7 +31,7 @@ Power* power_alloc() {
power->gui = furi_record_open("gui");
// Pubsub
init_pubsub(&power->event_pubsub);
power->event_pubsub = furi_pubsub_alloc();
// State initialization
power->state = PowerStateNotCharging;
@@ -60,10 +60,6 @@ Power* power_alloc() {
void power_free(Power* power) {
furi_assert(power);
// Records
furi_record_close("notification");
furi_record_close("gui");
// Gui
view_dispatcher_remove_view(power->view_dispatcher, PowerViewOff);
power_off_free(power->power_off);
@@ -73,6 +69,14 @@ void power_free(Power* power) {
// State
osMutexDelete(power->info_mtx);
// FuriPubSub
furi_pubsub_free(power->event_pubsub);
// Records
furi_record_close("notification");
furi_record_close("gui");
free(power);
}
@@ -83,14 +87,14 @@ static void power_check_charging_state(Power* power) {
notification_internal_message(power->notification, &sequence_charged);
power->state = PowerStateCharged;
power->event.type = PowerEventTypeFullyCharged;
notify_pubsub(&power->event_pubsub, &power->event);
furi_pubsub_publish(power->event_pubsub, &power->event);
}
} else {
if(power->state != PowerStateCharging) {
notification_internal_message(power->notification, &sequence_charging);
power->state = PowerStateCharging;
power->event.type = PowerEventTypeStartCharging;
notify_pubsub(&power->event_pubsub, &power->event);
furi_pubsub_publish(power->event_pubsub, &power->event);
}
}
} else {
@@ -98,7 +102,7 @@ static void power_check_charging_state(Power* power) {
notification_internal_message(power->notification, &sequence_not_charging);
power->state = PowerStateNotCharging;
power->event.type = PowerEventTypeStopCharging;
notify_pubsub(&power->event_pubsub, &power->event);
furi_pubsub_publish(power->event_pubsub, &power->event);
}
}
}
@@ -156,7 +160,7 @@ static void power_check_battery_level_change(Power* power) {
power->battery_level = power->info.charge;
power->event.type = PowerEventTypeBatteryLevelChanged;
power->event.data.battery_level = power->battery_level;
notify_pubsub(&power->event_pubsub, &power->event);
furi_pubsub_publish(power->event_pubsub, &power->event);
}
}

View File

@@ -62,4 +62,4 @@ void power_get_info(Power* power, PowerInfo* info);
/** Get power event pubsub handler
* @param power - Power instance
*/
PubSub* power_get_pubsub(Power* power);
FuriPubSub* power_get_pubsub(Power* power);

View File

@@ -1,4 +1,5 @@
#include "power_i.h"
#include <furi.h>
#include "furi-hal-power.h"
#include "furi-hal-bootloader.h"
@@ -30,7 +31,7 @@ void power_get_info(Power* power, PowerInfo* info) {
osMutexRelease(power->info_mtx);
}
PubSub* power_get_pubsub(Power* power) {
FuriPubSub* power_get_pubsub(Power* power) {
furi_assert(power);
return &power->event_pubsub;
return power->event_pubsub;
}

View File

@@ -25,7 +25,7 @@ struct Power {
ViewPort* battery_view_port;
Gui* gui;
NotificationApp* notification;
PubSub event_pubsub;
FuriPubSub* event_pubsub;
PowerEvent event;
PowerState state;

View File

@@ -113,9 +113,9 @@ void rpc_system_gui_send_input_event_request_process(const PB_Main* request, voi
return;
}
PubSub* input_events = furi_record_open("input_events");
FuriPubSub* input_events = furi_record_open("input_events");
furi_check(input_events);
notify_pubsub(input_events, &event);
furi_pubsub_publish(input_events, &event);
furi_record_close("input_events");
rpc_send_and_release_empty(rpc_gui->rpc, request->command_id, PB_CommandStatus_OK);
}
@@ -142,11 +142,13 @@ void* rpc_system_gui_alloc(Rpc* rpc) {
rpc_handler.message_handler = rpc_system_gui_send_input_event_request_process;
rpc_add_handler(rpc, PB_Main_gui_send_input_event_request_tag, &rpc_handler);
return NULL;
return rpc_gui;
}
void rpc_system_gui_free(void* ctx) {
furi_assert(ctx);
RpcGuiSystem* rpc_gui = ctx;
furi_assert(rpc_gui->gui);
gui_set_framebuffer_callback(rpc_gui->gui, NULL, NULL);
furi_record_close("gui");
free(rpc_gui);

View File

@@ -1,5 +1,6 @@
#pragma once
#include "rpc.h"
#include "storage/filesystem-api-defines.h"
#include <pb.h>
#include <pb_decode.h>
#include <pb_encode.h>
@@ -29,3 +30,5 @@ void rpc_system_gui_free(void* ctx);
void rpc_print_message(const PB_Main* message);
void rpc_cli_command_start_session(Cli* cli, string_t args, void* context);
PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error);

View File

@@ -51,7 +51,7 @@ static void rpc_system_storage_reset_state(RpcStorageSystem* rpc_storage, bool s
}
}
static PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error) {
PB_CommandStatus rpc_system_storage_get_error(FS_Error fs_error) {
PB_CommandStatus pb_error;
switch(fs_error) {
case FSE_OK:
@@ -96,6 +96,40 @@ static PB_CommandStatus rpc_system_storage_get_file_error(File* file) {
return rpc_system_storage_get_error(storage_file_get_error(file));
}
static void rpc_system_storage_stat_process(const PB_Main* request, void* context) {
furi_assert(request);
furi_assert(context);
furi_assert(request->which_content == PB_Main_storage_stat_request_tag);
RpcStorageSystem* rpc_storage = context;
rpc_system_storage_reset_state(rpc_storage, true);
PB_Main* response = furi_alloc(sizeof(PB_Main));
response->command_id = request->command_id;
Storage* fs_api = furi_record_open("storage");
const char* path = request->content.storage_stat_request.path;
FileInfo fileinfo;
FS_Error error = storage_common_stat(fs_api, path, &fileinfo);
response->command_status = rpc_system_storage_get_error(error);
response->which_content = PB_Main_empty_tag;
if(error == FSE_OK) {
response->which_content = PB_Main_storage_stat_response_tag;
response->content.storage_stat_response.has_file = true;
response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ?
PB_Storage_File_FileType_DIR :
PB_Storage_File_FileType_FILE;
response->content.storage_stat_response.file.size = fileinfo.size;
}
rpc_send_and_release(rpc_storage->rpc, response);
free(response);
furi_record_close("storage");
}
static void rpc_system_storage_list_root(const PB_Main* request, void* context) {
RpcStorageSystem* rpc_storage = context;
const char* hard_coded_dirs[] = {"any", "int", "ext"};
@@ -140,11 +174,10 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex
PB_Main response = {
.command_id = request->command_id,
.has_next = false,
.which_content = PB_Main_storage_list_request_tag,
.which_content = PB_Main_storage_list_response_tag,
.command_status = PB_CommandStatus_OK,
};
PB_Storage_ListResponse* list = &response.content.storage_list_response;
response.which_content = PB_Main_storage_list_response_tag;
bool finish = false;
int i = 0;
@@ -434,6 +467,9 @@ void* rpc_system_storage_alloc(Rpc* rpc) {
.context = rpc_storage,
};
rpc_handler.message_handler = rpc_system_storage_stat_process;
rpc_add_handler(rpc, PB_Main_storage_stat_request_tag, &rpc_handler);
rpc_handler.message_handler = rpc_system_storage_list_process;
rpc_add_handler(rpc, PB_Main_storage_list_request_tag, &rpc_handler);

View File

@@ -1,8 +1,10 @@
#pragma once
#include <furi.h>
#include "filesystem-api-internal.h"
#include <m-string.h>
#include <m-array.h>
#include <m-list.h>
#ifdef __cplusplus
extern "C" {

View File

@@ -3,6 +3,7 @@
#include <furi.h>
#include <furi-hal.h>
#include <stream_buffer.h>
#include <lib/toolbox/args.h>
#include <lib/subghz/subghz_parser.h>
#include <lib/subghz/subghz_keystore.h>

View File

@@ -16,39 +16,30 @@ void test_pubsub_handler(const void* arg, void* ctx) {
}
void test_furi_pubsub() {
bool result;
PubSub test_pubsub;
PubSubItem* test_pubsub_item;
FuriPubSub* test_pubsub = NULL;
FuriPubSubSubscription* test_pubsub_subscription = NULL;
// init pubsub case
result = init_pubsub(&test_pubsub);
mu_assert(result, "init pubsub failed");
test_pubsub = furi_pubsub_alloc();
mu_assert_pointers_not_eq(test_pubsub, NULL);
// subscribe pubsub case
test_pubsub_item = subscribe_pubsub(&test_pubsub, test_pubsub_handler, (void*)&context_value);
mu_assert_pointers_not_eq(test_pubsub_item, NULL);
test_pubsub_subscription =
furi_pubsub_subscribe(test_pubsub, test_pubsub_handler, (void*)&context_value);
mu_assert_pointers_not_eq(test_pubsub_subscription, NULL);
/// notify pubsub case
result = notify_pubsub(&test_pubsub, (void*)&notify_value_0);
mu_assert(result, "notify pubsub failed");
furi_pubsub_publish(test_pubsub, (void*)&notify_value_0);
mu_assert_int_eq(pubsub_value, notify_value_0);
mu_assert_int_eq(pubsub_context_value, context_value);
// unsubscribe pubsub case
result = unsubscribe_pubsub(test_pubsub_item);
mu_assert(result, "unsubscribe pubsub failed");
result = unsubscribe_pubsub(test_pubsub_item);
mu_assert(!result, "unsubscribe pubsub not failed");
furi_pubsub_unsubscribe(test_pubsub, test_pubsub_subscription);
/// notify unsubscribed pubsub case
result = notify_pubsub(&test_pubsub, (void*)&notify_value_1);
mu_assert(result, "notify pubsub failed");
furi_pubsub_publish(test_pubsub, (void*)&notify_value_1);
mu_assert_int_not_eq(pubsub_value, notify_value_1);
// delete pubsub case
result = delete_pubsub(&test_pubsub);
mu_assert(result, "unsubscribe pubsub failed");
// TODO test case that the pubsub_delete will remove pubsub from heap
furi_pubsub_free(test_pubsub);
}

View File

@@ -218,6 +218,9 @@ static void test_rpc_create_simple_message(
message->which_content = tag;
message->has_next = false;
switch(tag) {
case PB_Main_storage_stat_request_tag:
message->content.storage_stat_request.path = str_copy;
break;
case PB_Main_storage_list_request_tag:
message->content.storage_list_request.path = str_copy;
break;
@@ -369,6 +372,19 @@ static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) {
mu_check(result_locked == expected_locked);
break;
}
case PB_Main_storage_stat_response_tag: {
bool result_has_msg_file = result->content.storage_stat_response.has_file;
bool expected_has_msg_file = expected->content.storage_stat_response.has_file;
mu_check(result_has_msg_file == expected_has_msg_file);
if(result_has_msg_file) {
PB_Storage_File* result_msg_file = &result->content.storage_stat_response.file;
PB_Storage_File* expected_msg_file = &expected->content.storage_stat_response.file;
test_rpc_compare_file(result_msg_file, expected_msg_file);
} else {
mu_check(0);
}
} break;
case PB_Main_storage_read_response_tag: {
bool result_has_msg_file = result->content.storage_read_response.has_file;
bool expected_has_msg_file = expected->content.storage_read_response.has_file;
@@ -455,11 +471,10 @@ static void test_rpc_storage_list_create_expected_list(
PB_Main response = {
.command_id = command_id,
.has_next = false,
.which_content = PB_Main_storage_list_request_tag,
.which_content = PB_Main_storage_list_response_tag,
/* other fields (e.g. msg_files ptrs) explicitly initialized by 0 */
};
PB_Storage_ListResponse* list = &response.content.storage_list_response;
response.which_content = PB_Main_storage_list_response_tag;
bool finish = false;
int i = 0;
@@ -649,9 +664,8 @@ static bool test_is_exists(const char* path) {
Storage* fs_api = furi_record_open("storage");
FileInfo fileinfo;
FS_Error result = storage_common_stat(fs_api, path, &fileinfo);
furi_check((result == FSE_OK) || (result == FSE_NOT_EXIST));
furi_record_close("storage");
return result == FSE_OK;
}
@@ -687,6 +701,59 @@ static void test_create_file(const char* path, size_t size) {
furi_check(test_is_exists(path));
}
static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) {
PB_Main request;
MsgList_t expected_msg_list;
MsgList_init(expected_msg_list);
test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id);
Storage* fs_api = furi_record_open("storage");
FileInfo fileinfo;
FS_Error error = storage_common_stat(fs_api, path, &fileinfo);
furi_record_close("storage");
PB_Main* response = MsgList_push_new(expected_msg_list);
response->command_id = command_id;
response->command_status = rpc_system_storage_get_error(error);
response->has_next = false;
response->which_content = PB_Main_empty_tag;
if(error == FSE_OK) {
response->which_content = PB_Main_storage_stat_response_tag;
response->content.storage_stat_response.has_file = true;
response->content.storage_stat_response.file.type = (fileinfo.flags & FSF_DIRECTORY) ?
PB_Storage_File_FileType_DIR :
PB_Storage_File_FileType_FILE;
response->content.storage_stat_response.file.size = fileinfo.size;
}
test_rpc_encode_and_feed_one(&request);
test_rpc_decode_and_compare(expected_msg_list);
pb_release(&PB_Main_msg, &request);
test_rpc_free_msg_list(expected_msg_list);
}
#define TEST_DIR_STAT_NAME TEST_DIR "stat_dir"
#define TEST_DIR_STAT TEST_DIR_STAT_NAME "/"
MU_TEST(test_storage_stat) {
test_create_dir(TEST_DIR_STAT_NAME);
test_create_file(TEST_DIR_STAT "empty.txt", 0);
test_create_file(TEST_DIR_STAT "l33t.txt", 1337);
test_rpc_storage_stat_run("/", ++command_id);
test_rpc_storage_stat_run("/int", ++command_id);
test_rpc_storage_stat_run("/ext", ++command_id);
test_rpc_storage_stat_run(TEST_DIR_STAT "empty.txt", ++command_id);
test_rpc_storage_stat_run(TEST_DIR_STAT "l33t.txt", ++command_id);
test_rpc_storage_stat_run(TEST_DIR_STAT "missing", ++command_id);
test_rpc_storage_stat_run(TEST_DIR_STAT_NAME, ++command_id);
test_rpc_storage_stat_run(TEST_DIR_STAT, ++command_id);
}
MU_TEST(test_storage_read) {
test_create_file(TEST_DIR "empty.txt", 0);
test_create_file(TEST_DIR "file1.txt", 1);
@@ -1138,6 +1205,7 @@ MU_TEST_SUITE(test_rpc_status) {
MU_TEST_SUITE(test_rpc_storage) {
MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown);
MU_RUN_TEST(test_storage_stat);
MU_RUN_TEST(test_storage_list);
MU_RUN_TEST(test_storage_read);
MU_RUN_TEST(test_storage_write_read);

View File

@@ -1,4 +1,5 @@
#include "m-string.h"
#include <stdio.h>
#include <furi.h>
#include <furi-hal.h>