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:
		| @@ -1,88 +1,95 @@ | ||||
| #include "pubsub.h" | ||||
| #include <furi.h> | ||||
| #include "memmgr.h" | ||||
| #include "check.h" | ||||
|  | ||||
| #include <m-list.h> | ||||
| #include <cmsis_os2.h> | ||||
|  | ||||
| struct FuriPubSubSubscription { | ||||
|     FuriPubSubCallback callback; | ||||
|     void* callback_context; | ||||
| }; | ||||
|  | ||||
| LIST_DEF(FuriPubSubSubscriptionList, FuriPubSubSubscription, M_POD_OPLIST); | ||||
|  | ||||
| struct FuriPubSub { | ||||
|     FuriPubSubSubscriptionList_t items; | ||||
|     osMutexId_t mutex; | ||||
| }; | ||||
|  | ||||
| FuriPubSub* furi_pubsub_alloc() { | ||||
|     FuriPubSub* pubsub = furi_alloc(sizeof(FuriPubSub)); | ||||
|  | ||||
| bool init_pubsub(PubSub* pubsub) { | ||||
|     // mutex without name, | ||||
|     // no attributes (unfortunatly robust mutex is not supported by FreeRTOS), | ||||
|     // with dynamic memory allocation | ||||
|     pubsub->mutex = osMutexNew(NULL); | ||||
|     if(pubsub->mutex == NULL) return false; | ||||
|     furi_assert(pubsub->mutex); | ||||
|  | ||||
|     // construct list | ||||
|     list_pubsub_cb_init(pubsub->items); | ||||
|     FuriPubSubSubscriptionList_init(pubsub->items); | ||||
|  | ||||
|     return true; | ||||
|     return pubsub; | ||||
| } | ||||
|  | ||||
| bool delete_pubsub(PubSub* pubsub) { | ||||
|     if(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK) { | ||||
|         bool result = osMutexDelete(pubsub->mutex) == osOK; | ||||
|         list_pubsub_cb_clear(pubsub->items); | ||||
|         return result; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| void furi_pubsub_free(FuriPubSub* pubsub) { | ||||
|     furi_assert(pubsub); | ||||
|  | ||||
|     furi_check(FuriPubSubSubscriptionList_size(pubsub->items) == 0); | ||||
|  | ||||
|     FuriPubSubSubscriptionList_clear(pubsub->items); | ||||
|  | ||||
|     furi_check(osMutexDelete(pubsub->mutex) == osOK); | ||||
|  | ||||
|     free(pubsub); | ||||
| } | ||||
|  | ||||
| PubSubItem* subscribe_pubsub(PubSub* pubsub, PubSubCallback cb, void* ctx) { | ||||
|     if(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK) { | ||||
|         // put uninitialized item to the list | ||||
|         PubSubItem* item = list_pubsub_cb_push_raw(pubsub->items); | ||||
| FuriPubSubSubscription* | ||||
|     furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context) { | ||||
|     furi_check(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK); | ||||
|     // put uninitialized item to the list | ||||
|     FuriPubSubSubscription* item = FuriPubSubSubscriptionList_push_raw(pubsub->items); | ||||
|  | ||||
|         // initialize item | ||||
|         item->cb = cb; | ||||
|         item->ctx = ctx; | ||||
|         item->self = pubsub; | ||||
|     // initialize item | ||||
|     item->callback = callback; | ||||
|     item->callback_context = callback_context; | ||||
|  | ||||
|         // TODO unsubscribe pubsub on app exit | ||||
|         //flapp_on_exit(unsubscribe_pubsub, item); | ||||
|     furi_check(osMutexRelease(pubsub->mutex) == osOK); | ||||
|  | ||||
|         osMutexRelease(pubsub->mutex); | ||||
|  | ||||
|         return item; | ||||
|     } else { | ||||
|         return NULL; | ||||
|     } | ||||
|     return item; | ||||
| } | ||||
|  | ||||
| bool unsubscribe_pubsub(PubSubItem* pubsub_id) { | ||||
|     if(osMutexAcquire(pubsub_id->self->mutex, osWaitForever) == osOK) { | ||||
|         bool result = false; | ||||
| void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription) { | ||||
|     furi_assert(pubsub); | ||||
|     furi_assert(pubsub_subscription); | ||||
|  | ||||
|         // iterate over items | ||||
|         list_pubsub_cb_it_t it; | ||||
|         for(list_pubsub_cb_it(it, pubsub_id->self->items); !list_pubsub_cb_end_p(it); | ||||
|             list_pubsub_cb_next(it)) { | ||||
|             const PubSubItem* item = list_pubsub_cb_cref(it); | ||||
|     furi_check(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK); | ||||
|     bool result = false; | ||||
|  | ||||
|             // if the iterator is equal to our element | ||||
|             if(item == pubsub_id) { | ||||
|                 list_pubsub_cb_remove(pubsub_id->self->items, it); | ||||
|                 result = true; | ||||
|                 break; | ||||
|             } | ||||
|     // iterate over items | ||||
|     FuriPubSubSubscriptionList_it_t it; | ||||
|     for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it); | ||||
|         FuriPubSubSubscriptionList_next(it)) { | ||||
|         const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it); | ||||
|  | ||||
|         // if the iterator is equal to our element | ||||
|         if(item == pubsub_subscription) { | ||||
|             FuriPubSubSubscriptionList_remove(pubsub->items, it); | ||||
|             result = true; | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         osMutexRelease(pubsub_id->self->mutex); | ||||
|         return result; | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     furi_check(osMutexRelease(pubsub->mutex) == osOK); | ||||
|     furi_check(result); | ||||
| } | ||||
|  | ||||
| bool notify_pubsub(PubSub* pubsub, void* arg) { | ||||
|     if(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK) { | ||||
|         // iterate over subscribers | ||||
|         list_pubsub_cb_it_t it; | ||||
|         for(list_pubsub_cb_it(it, pubsub->items); !list_pubsub_cb_end_p(it); | ||||
|             list_pubsub_cb_next(it)) { | ||||
|             const PubSubItem* item = list_pubsub_cb_cref(it); | ||||
|             item->cb(arg, item->ctx); | ||||
|         } | ||||
| void furi_pubsub_publish(FuriPubSub* pubsub, void* message) { | ||||
|     furi_check(osMutexAcquire(pubsub->mutex, osWaitForever) == osOK); | ||||
|  | ||||
|         osMutexRelease(pubsub->mutex); | ||||
|         return true; | ||||
|     } else { | ||||
|         return false; | ||||
|     // iterate over subscribers | ||||
|     FuriPubSubSubscriptionList_it_t it; | ||||
|     for(FuriPubSubSubscriptionList_it(it, pubsub->items); !FuriPubSubSubscriptionList_end_p(it); | ||||
|         FuriPubSubSubscriptionList_next(it)) { | ||||
|         const FuriPubSubSubscription* item = FuriPubSubSubscriptionList_cref(it); | ||||
|         item->callback(message, item->callback_context); | ||||
|     } | ||||
|  | ||||
|     furi_check(osMutexRelease(pubsub->mutex) == osOK); | ||||
| } | ||||
|   | ||||
| @@ -1,95 +1,64 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "cmsis_os.h" | ||||
| #include "m-list.h" | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| /** | ||||
| == PubSub == | ||||
| /** FuriPubSub Callback type */ | ||||
| typedef void (*FuriPubSubCallback)(const void* message, void* context); | ||||
|  | ||||
|  * PubSub allows users to subscribe on notifies and notify subscribers. | ||||
|  * Notifier side can pass `void*` arg to subscriber callback, | ||||
|  * and also subscriber can set `void*` context pointer that pass into | ||||
|  * callback (you can see callback signature below). | ||||
| /** FuriPubSub type */ | ||||
| typedef struct FuriPubSub FuriPubSub; | ||||
|  | ||||
| /** FuriPubSubSubscription type */ | ||||
| typedef struct FuriPubSubSubscription FuriPubSubSubscription; | ||||
|  | ||||
| /** Allocate FuriPubSub | ||||
|  * | ||||
|  * Reentrable, Not threadsafe, one owner | ||||
|  * | ||||
|  * @return     pointer to FuriPubSub instance | ||||
|  */ | ||||
| FuriPubSub* furi_pubsub_alloc(); | ||||
|  | ||||
| typedef void (*PubSubCallback)(const void*, void*); | ||||
| typedef struct PubSubType PubSub; | ||||
|  | ||||
| typedef struct { | ||||
|     PubSubCallback cb; | ||||
|     void* ctx; | ||||
|     PubSub* self; | ||||
| } PubSubItem; | ||||
|  | ||||
| LIST_DEF(list_pubsub_cb, PubSubItem, M_POD_OPLIST); | ||||
|  | ||||
| struct PubSubType { | ||||
|     list_pubsub_cb_t items; | ||||
|     osMutexId_t mutex; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * To create PubSub you should create PubSub instance and call `init_pubsub`. | ||||
| /** Free FuriPubSub | ||||
|  *  | ||||
|  * @param      pubsub  FuriPubSub instance | ||||
|  */ | ||||
| bool init_pubsub(PubSub* pubsub); | ||||
| void furi_pubsub_free(FuriPubSub* pubsub); | ||||
|  | ||||
| /** | ||||
|  * Since we use dynamic memory - we must explicity delete pubsub | ||||
| /** Subscribe to FuriPubSub | ||||
|  *  | ||||
|  * Threadsafe, Reentrable | ||||
|  *  | ||||
|  * @param      pubsub            pointer to FuriPubSub instance | ||||
|  * @param[in]  callback          The callback | ||||
|  * @param      callback_context  The callback context | ||||
|  * | ||||
|  * @return     pointer to FuriPubSubSubscription instance | ||||
|  */ | ||||
| bool delete_pubsub(PubSub* pubsub); | ||||
| FuriPubSubSubscription* | ||||
|     furi_pubsub_subscribe(FuriPubSub* pubsub, FuriPubSubCallback callback, void* callback_context); | ||||
|  | ||||
| /** | ||||
|  * Use `subscribe_pubsub` to register your callback. | ||||
| /** Unsubscribe from FuriPubSub | ||||
|  *  | ||||
|  * No use of `pubsub_subscription` allowed after call of this method | ||||
|  * Threadsafe, Reentrable. | ||||
|  * | ||||
|  * @param      pubsub               pointer to FuriPubSub instance | ||||
|  * @param      pubsub_subscription  pointer to FuriPubSubSubscription instance | ||||
|  */ | ||||
| PubSubItem* subscribe_pubsub(PubSub* pubsub, PubSubCallback cb, void* ctx); | ||||
| void furi_pubsub_unsubscribe(FuriPubSub* pubsub, FuriPubSubSubscription* pubsub_subscription); | ||||
|  | ||||
| /** | ||||
|  * Use `unsubscribe_pubsub` to unregister callback. | ||||
| /** Publish message to FuriPubSub | ||||
|  * | ||||
|  * Threadsafe, Reentrable. | ||||
|  *  | ||||
|  * @param      pubsub   pointer to FuriPubSub instance | ||||
|  * @param      message  message pointer to publish | ||||
|  */ | ||||
| bool unsubscribe_pubsub(PubSubItem* pubsub_id); | ||||
|  | ||||
| /** | ||||
|  * Use `notify_pubsub` to notify subscribers. | ||||
|  */ | ||||
| bool notify_pubsub(PubSub* pubsub, void* arg); | ||||
| void furi_pubsub_publish(FuriPubSub* pubsub, void* message); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /* | ||||
|  | ||||
| ```C | ||||
| // MANIFEST | ||||
| // name="test" | ||||
| // stack=128 | ||||
|  | ||||
| void example_pubsub_handler(void* arg, void* ctx) { | ||||
|     printf("get %d from %s\n", *(uint32_t*)arg, (const char*)ctx); | ||||
| } | ||||
|  | ||||
| void pubsub_test() { | ||||
|     const char* app_name = "test app"; | ||||
|  | ||||
|     PubSub example_pubsub; | ||||
|     init_pubsub(&example_pubsub); | ||||
|  | ||||
|     if(!subscribe_pubsub(&example_pubsub, example_pubsub_handler, (void*)app_name)) { | ||||
|         printf("critical error\n"); | ||||
|         flapp_exit(NULL); | ||||
|     } | ||||
|  | ||||
|     uint32_t counter = 0; | ||||
|     while(1) { | ||||
|         notify_pubsub(&example_pubsub, (void*)&counter); | ||||
|         counter++; | ||||
|  | ||||
|         osDelay(100); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| */ | ||||
|   | ||||
| @@ -84,8 +84,8 @@ bool furi_record_destroy(const char* name) { | ||||
|     FuriRecordData* record_data = FuriRecordDataDict_get(furi_record->records, name_str); | ||||
|     furi_assert(record_data); | ||||
|     if(record_data->holders_count == 0) { | ||||
|         FuriRecordDataDict_erase(furi_record->records, name_str); | ||||
|         furi_check(osOK == osEventFlagsDelete(record_data->flags)); | ||||
|         FuriRecordDataDict_erase(furi_record->records, name_str); | ||||
|         ret = true; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,9 @@ | ||||
| #include "check.h" | ||||
| #include "memmgr.h" | ||||
|  | ||||
| #include <FreeRTOS.h> | ||||
| #include <task.h> | ||||
|  | ||||
| #include <furi-hal.h> | ||||
| #include <m-dict.h> | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user