Implement ValueManager and ValueComposer (#183)

* Fix ValueManager implementation
* Implement ValueComposer
* Add constructor for ValueManager
* Add value-expanders.h to flipper_v2.h set
* Move COPY_COMPOSE body into a .c file
* Add test for ValueManager
* Add destructors for ValueMutex, ValueManager and ValueComposer
* Use destructors in tests
* Move composition logic into perform_compose()
* Add docs for perform_compose()
* Add test for ValueComposer
* Replace atomic_bool with bool as g++ compiler doesn't support C11 atomics
* Add Event type
* Add semaphore support to the local target
* Add test for Event
* Update input records and relevant examples
* Rename Event to AppEvent in the cc1101-workaround example
* Rename Event to AppEvent in the irda example
* Use Event in ValueComposer to wait for update request
* Add perform_compose_internal() function
* fix Event/AppEvent

Co-authored-by: aanper <mail@s3f.ru>
This commit is contained in:
Vadim Kaushan
2020-10-26 12:26:15 +03:00
committed by GitHub
parent 69d97afea8
commit bb68fca20b
24 changed files with 641 additions and 96 deletions

24
core/api-basic/event.c Normal file
View File

@@ -0,0 +1,24 @@
#include "event.h"
#include <string.h>
bool init_event(Event* event) {
event->semaphore_id = osSemaphoreNew(1, 0, NULL);
return event->semaphore_id != NULL;
}
bool delete_event(Event* event) {
return osSemaphoreDelete(event->semaphore_id) == osOK;
}
void signal_event(Event* event) {
// Ignore the result, as we do not care about repeated event signalling.
osSemaphoreRelease(event->semaphore_id);
}
void wait_event(Event* event) {
wait_event_with_timeout(event, osWaitForever);
}
bool wait_event_with_timeout(Event* event, uint32_t timeout_ms) {
return osSemaphoreAcquire(event->semaphore_id, timeout_ms) == osOK;
}

36
core/api-basic/event.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "cmsis_os.h"
typedef struct {
osSemaphoreId_t semaphore_id;
} Event;
/*
Creates Event.
*/
bool init_event(Event* event);
/*
Free resources allocated by `init_event`.
This function doesn't free the memory occupied by `Event` itself.
*/
bool delete_event(Event* event);
/*
Signals the event.
If the event is already in "signalled" state, nothing happens.
*/
void signal_event(Event* event);
/*
Waits until the event is signalled.
*/
void wait_event(Event* event);
/*
Waits with a timeout until the event is signalled.
*/
bool wait_event_with_timeout(Event* event, uint32_t timeout_ms);

View File

@@ -1,4 +1,5 @@
#include "pubsub.h"
#include "flipper_v2.h"
bool init_pubsub(PubSub* pubsub) {
// mutex without name,

View File

@@ -1,6 +1,6 @@
#pragma once
#include "flipper_v2.h"
#include "cmsis_os.h"
#include "m-list.h"
/*

View File

@@ -0,0 +1,177 @@
#include "value-expanders.h"
bool init_composer(ValueComposer* composer, void* value) {
if(!init_mutex(&composer->value, value, 0)) return false;
for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
list_composer_cb_init(composer->layers[i]);
}
// mutex without name,
// no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
// with dynamic memory allocation
const osMutexAttr_t value_mutex_attr = {
.name = NULL, .attr_bits = 0, .cb_mem = NULL, .cb_size = 0U};
composer->mutex = osMutexNew(&value_mutex_attr);
if(composer->mutex == NULL) return false;
if(!init_event(&composer->request)) return false;
return true;
}
bool delete_composer(ValueComposer* composer) {
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
bool result = true;
result &= delete_mutex(&composer->value);
for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
list_composer_cb_clear(composer->layers[i]);
}
result &= osMutexDelete(composer->mutex) == osOK;
return result;
} else {
return false;
}
}
ValueComposerHandle*
add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, UiLayer layer) {
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
// put uninitialized item to the list
ValueComposerHandle* handle = list_composer_cb_push_raw(composer->layers[layer]);
handle->cb = cb;
handle->ctx = ctx;
handle->layer = layer;
handle->composer = composer;
// TODO unregister handle on app exit
//flapp_on_exit(remove_compose_layer, handle);
osMutexRelease(composer->mutex);
// Layers changed, request composition
signal_event(&composer->request);
return handle;
} else {
return NULL;
}
}
bool remove_compose_layer(ValueComposerHandle* handle) {
ValueComposer* composer = handle->composer;
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
bool result = false;
// iterate over items
list_composer_cb_it_t it;
for(list_composer_cb_it(it, composer->layers[handle->layer]); !list_composer_cb_end_p(it);
list_composer_cb_next(it)) {
const ValueComposerHandle* item = list_composer_cb_cref(it);
// if the iterator is equal to our element
if(item == handle) {
list_composer_cb_remove(composer->layers[handle->layer], it);
result = true;
break;
}
}
osMutexRelease(composer->mutex);
// Layers changed, request composition
signal_event(&composer->request);
return result;
} else {
return false;
}
}
void request_compose(ValueComposerHandle* handle) {
ValueComposer* composer = handle->composer;
signal_event(&composer->request);
}
void perform_compose(
ValueComposer* composer,
ValueComposerCallback start_cb,
ValueComposerCallback end_cb,
void* ctx) {
if(!wait_event_with_timeout(&composer->request, 0)) return;
void* state = acquire_mutex(&composer->value, 0);
if(state == NULL) return;
if(start_cb != NULL) start_cb(ctx, state);
perform_compose_internal(composer, state);
if(end_cb != NULL) end_cb(ctx, state);
release_mutex(&composer->value, state);
}
void perform_compose_internal(ValueComposer* composer, void* state) {
if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
// Compose all levels for now
for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
// iterate over items
list_composer_cb_it_t it;
for(list_composer_cb_it(it, composer->layers[i]); !list_composer_cb_end_p(it);
list_composer_cb_next(it)) {
const ValueComposerHandle* h = list_composer_cb_cref(it);
h->cb(h->ctx, state);
}
}
osMutexRelease(composer->mutex);
}
}
void COPY_COMPOSE(void* ctx, void* state) {
read_mutex((ValueMutex*)ctx, state, 0, osWaitForever);
}
bool init_managed(ValueManager* managed, void* value, size_t size) {
if(!init_pubsub(&managed->pubsub)) return false;
if(!init_mutex(&managed->value, value, size)) {
delete_pubsub(&managed->pubsub);
return false;
}
return true;
}
bool delete_managed(ValueManager* managed) {
bool result = true;
result &= delete_mutex(&managed->value);
result &= delete_pubsub(&managed->pubsub);
return result;
}
bool write_managed(ValueManager* managed, void* data, size_t len, uint32_t timeout) {
void* value = acquire_mutex(&managed->value, timeout);
if(value == NULL) return false;
memcpy(value, data, len);
notify_pubsub(&managed->pubsub, value);
if(!release_mutex(&managed->value, value)) return false;
return true;
}
bool commit_managed(ValueManager* managed, void* value) {
if(value != managed->value.value) return false;
notify_pubsub(&managed->pubsub, value);
if(!release_mutex(&managed->value, value)) return false;
return true;
}

View File

@@ -1,24 +0,0 @@
#include "value-expanders.h"
bool commit_managed(ValueManager* managed, void* value) {
if(value != managed->mutex->value) return false;
notify_pubsub(&managed->pubsub, value);
if(!osMutexGive(managed->mutex)) return false;
return true;
}
bool write_managed(ValueManager* managed, void* data, size_t len, uint32_t timeout) {
void* value = acquire_mutex(managed->mutex, timeout);
if(value == NULL) return false;
memcpy(value, data, len):
notify_pubsub(&managed->pubsub, value);
if(!release_mutex(managed->mutex, value)) return false;
return true;
}

View File

@@ -1,26 +1,76 @@
#pragma once
#include "flipper.h"
#include "valuemutex.h"
#include "pubsub.h"
#include "event.h"
#include "m-list.h"
/*
== Value composer ==
*/
typedef void(ValueComposerCallback)(void* ctx, void* state);
typedef struct ValueComposer ValueComposer;
void COPY_COMPOSE(void* ctx, void* state) {
read_mutex((ValueMutex*)ctx, state, 0);
}
typedef void (*ValueComposerCallback)(void* ctx, void* state);
typedef enum { UiLayerBelowNotify UiLayerNotify, UiLayerAboveNotify } UiLayer;
typedef enum { UiLayerBelowNotify, UiLayerNotify, UiLayerAboveNotify } UiLayer;
typedef struct {
ValueComposerCallback cb;
void* ctx;
UiLayer layer;
ValueComposer* composer;
} ValueComposerHandle;
LIST_DEF(list_composer_cb, ValueComposerHandle, M_POD_OPLIST);
struct ValueComposer {
ValueMutex value;
list_composer_cb_t layers[3];
osMutexId_t mutex;
Event request;
};
void COPY_COMPOSE(void* ctx, void* state);
bool init_composer(ValueComposer* composer, void* value);
/*
Free resources allocated by `init_composer`.
This function doesn't free the memory occupied by `ValueComposer` itself.
*/
bool delete_composer(ValueComposer* composer);
ValueComposerHandle*
add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, uint32_t layer);
add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, UiLayer layer);
bool remove_compose_layer(ValueComposerHandle* handle);
void request_compose(ValueComposerHandle* handle);
/*
Perform composition if requested.
`start_cb` and `end_cb` will be called before and after all layer callbacks, respectively.
Both `start_cb` and `end_cb` can be NULL. They can be used to set initial state (e.g. clear screen)
and commit the final state.
*/
void perform_compose(
ValueComposer* composer,
ValueComposerCallback start_cb,
ValueComposerCallback end_cb,
void* ctx);
/*
Perform composition.
This function should be called with value mutex acquired.
This function is here for convenience, so that developers can write their own compose loops.
See `perform_compose` function body for an example.
*/
void perform_compose_internal(ValueComposer* composer, void* state);
// See [LED](LED-API) or [Display](Display-API) API for examples.
/*
@@ -39,6 +89,14 @@ typedef struct {
PubSub pubsub;
} ValueManager;
bool init_managed(ValueManager* managed, void* value, size_t size);
/*
Free resources allocated by `init_managed`.
This function doesn't free the memory occupied by `ValueManager` itself.
*/
bool delete_managed(ValueManager* managed);
/*
acquire value, changes it and send notify with current value.
*/

View File

@@ -17,6 +17,14 @@ bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) {
return true;
}
bool delete_mutex(ValueMutex* valuemutex) {
if(osMutexAcquire(valuemutex->mutex, osWaitForever) == osOK) {
return osMutexDelete(valuemutex->mutex) == osOK;
} else {
return false;
}
}
void* acquire_mutex(ValueMutex* valuemutex, uint32_t timeout) {
if(osMutexAcquire(valuemutex->mutex, timeout) == osOK) {
return valuemutex->value;

View File

@@ -21,6 +21,12 @@ Creates ValueMutex.
*/
bool init_mutex(ValueMutex* valuemutex, void* value, size_t size);
/*
Free resources allocated by `init_mutex`.
This function doesn't free the memory occupied by `ValueMutex` itself.
*/
bool delete_mutex(ValueMutex* valuemutex);
/*
Call for work with data stored in mutex.
Returns pointer to data if success, NULL otherwise.

View File

@@ -11,6 +11,8 @@ extern "C" {
#include "cmsis_os2.h"
#include "api-basic/valuemutex.h"
#include "api-basic/pubsub.h"
#include "api-basic/value-expanders.h"
#include "api-basic/event.h"
#include "api-basic/memmgr.h"