diff --git a/.github/workflows/pull-request-acceptance.yml b/.github/workflows/pull-request-acceptance.yml index b29fad5..eed9f55 100644 --- a/.github/workflows/pull-request-acceptance.yml +++ b/.github/workflows/pull-request-acceptance.yml @@ -17,17 +17,11 @@ jobs: uses: actions/checkout@v3 - name: Install Ninja uses: seanmiddleditch/gha-setup-ninja@master - - name: Build Tests And Examples + - name: Build And Test working-directory: extra run: | - cmake -B build -S . -G Ninja - cmake --build build - - name: Baremetal Tests - working-directory: extra - run: ./build/test/baremetal_eventrouter_test - - name: FreeRTOS Tests - working-directory: extra - run: ./build/test/freertos_eventrouter_test - - name: POSIX Tests - working-directory: extra - run: ./build/test/posix_eventrouter_test + for implementation in baremetal freertos posix; do + cmake -B build -S . -G Ninja -DIMPLEMENTATION=$implementation + cmake --build build + cmake --build build --target test + done diff --git a/eventrouter/internal/event_handler.h b/eventrouter/internal/event_handler.h index 76439c8..2de6819 100644 --- a/eventrouter/internal/event_handler.h +++ b/eventrouter/internal/event_handler.h @@ -29,7 +29,8 @@ extern "C" /// A function which accepts events and returns qualitative information /// about how that event was received. - typedef ErEventHandlerRet_t (*ErEventHandler_t)(struct ErEvent_t *a_event); + typedef ErEventHandlerRet_t (*ErEventHandler_t)(struct ErEvent_t* a_event, + void* a_context); #ifdef __cplusplus } diff --git a/eventrouter/internal/eventrouter_baremetal.c b/eventrouter/internal/eventrouter_baremetal.c index 936fa61..db64659 100644 --- a/eventrouter/internal/eventrouter_baremetal.c +++ b/eventrouter/internal/eventrouter_baremetal.c @@ -130,7 +130,8 @@ void ErCallHandlers(ErEvent_t *a_event) if (module_is_subscribed) { // Deliver the event to the subscribed module. - const ErEventHandlerRet_t ret = module->m_handler(a_event); + const ErEventHandlerRet_t ret = + module->m_handler(a_event, module->m_context); if (ret == ER_EVENT_HANDLER_RET__KEPT) { @@ -175,7 +176,8 @@ void ErReturnToSender(ErEvent_t *a_event) ErListRemove(&s_context.m_events.m_kept, &a_event->m_next); // All subscribed modules have received the event; return to its sender. - a_event->m_sending_module->m_handler(a_event); + ErModule_t *sender = a_event->m_sending_module; + sender->m_handler(a_event, sender->m_context); } } @@ -226,8 +228,8 @@ ErEvent_t *ErGetEventToDeliver(void) ErList_t *node = &s_context.m_events.m_deliver_now; if (node->m_next != NULL) { - ret = er_container_of(node->m_next, ErEvent_t, m_next); - node->m_next = node->m_next->m_next; + ret = er_container_of(node->m_next, ErEvent_t, m_next); + node->m_next = node->m_next->m_next; ret->m_next.m_next = NULL; } diff --git a/eventrouter/internal/eventrouter_os.c b/eventrouter/internal/eventrouter_os.c index e3f1d97..9363dc9 100644 --- a/eventrouter/internal/eventrouter_os.c +++ b/eventrouter/internal/eventrouter_os.c @@ -65,6 +65,10 @@ static pthread_cond_t s_init_gate_cond = PTHREAD_COND_INITIALIZER; //============================================================================== #if ER_IMPLEMENTATION == ER_IMPL_FREERTOS + +#include "FreeRTOS.h" +#include "queue.h" + static bool IsInIsr(void); // Forward declaration. static void DefaultSendEvent(ErQueueHandle_t a_queue, void *a_event) @@ -543,7 +547,8 @@ void ErCallHandlers(ErEvent_t *a_event) if (module_is_subscribed) { // Deliver the event to the subscribed module. - const ErEventHandlerRet_t ret = module->m_handler(a_event); + const ErEventHandlerRet_t ret = + module->m_handler(a_event, module->m_context); if (ret == ER_EVENT_HANDLER_RET__KEPT) { @@ -628,7 +633,8 @@ void ErReturnToSender(ErEvent_t *a_event) // support the optimization mentioned a few lines up. if (atomic_load(&a_event->m_reference_count) == 0) { - a_event->m_sending_module->m_handler(a_event); + ErModule_t *sender = a_event->m_sending_module; + sender->m_handler(a_event, sender->m_context); } } diff --git a/eventrouter/internal/module.h b/eventrouter/internal/module.h index 6ff7668..cf2e3e3 100644 --- a/eventrouter/internal/module.h +++ b/eventrouter/internal/module.h @@ -20,6 +20,7 @@ extern "C" typedef struct { ErEventHandler_t m_handler; /// Where events are delivered/returned. + void *m_context; /// Passed to `m_handler` when an event is delivered. // Implementation details. size_t m_task_idx; @@ -30,10 +31,10 @@ extern "C" /// Used to initialize `ErModule_t` definitions while avoiding /// missing-field-initializers warnings. -#define ER_CREATE_MODULE(a_handler) \ - { \ - .m_handler = a_handler, .m_task_idx = 0, .m_module_idx = 0, \ - .m_subscriptions = {0}, \ +#define ER_CREATE_MODULE(a_handler, a_context) \ + { \ + .m_handler = a_handler, .m_context = a_context, .m_task_idx = 0, \ + .m_module_idx = 0, .m_subscriptions = {0}, \ } #ifdef __cplusplus diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index 06ec471..7d7368a 100644 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -17,25 +17,35 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS true) project(cmake-extras) include(cmake/repository_root.cmake) -include(cmake/freertos.cmake) include(cmake/googletest.cmake) #=============================================================================== -# Build implementation-specific eventrouter libs; used by examples and tests. -# =============================================================================== +# Select implementation. +#=============================================================================== -add_library(baremetal_eventrouter STATIC ${REPOSITORY_ROOT}/eventrouter.c) -target_include_directories(baremetal_eventrouter PUBLIC ${REPOSITORY_ROOT} .) -target_compile_definitions(baremetal_eventrouter PUBLIC -DER_BAREMETAL) +set(IMPLEMENTATION "baremetal" CACHE STRING "Select the implementation to build") +set(ALLOWED_IMPLEMENTATIONS "baremetal;freertos;posix") +set_property(CACHE IMPLEMENTATION PROPERTY STRINGS ${ALLOWED_IMPLEMENTATIONS}) +if(NOT IMPLEMENTATION IN_LIST ALLOWED_IMPLEMENTATIONS) + message(FATAL_ERROR "IMPLEMENTATION must be one of: ${ALLOWED_IMPLEMENTATIONS}") +endif() -add_library(freertos_eventrouter STATIC ${REPOSITORY_ROOT}/eventrouter.c) -target_include_directories(freertos_eventrouter PUBLIC ${REPOSITORY_ROOT} .) -target_link_libraries(freertos_eventrouter PUBLIC freertos_kernel) -target_compile_definitions(freertos_eventrouter PUBLIC -DER_FREERTOS) +#=============================================================================== +# Build the eventrouter library; used by examples and tests. +# =============================================================================== -add_library(posix_eventrouter STATIC ${REPOSITORY_ROOT}/eventrouter.c) -target_include_directories(posix_eventrouter PUBLIC ${REPOSITORY_ROOT} .) -target_compile_definitions(posix_eventrouter PUBLIC -DER_POSIX) +add_library(eventrouter STATIC ${REPOSITORY_ROOT}/eventrouter.c) +target_include_directories(eventrouter PUBLIC ${REPOSITORY_ROOT} .) + +if(IMPLEMENTATION STREQUAL "baremetal") + target_compile_definitions(eventrouter PUBLIC -DER_BAREMETAL) +elseif(IMPLEMENTATION STREQUAL "freertos") + include(cmake/freertos.cmake) + target_link_libraries(eventrouter PUBLIC freertos_kernel) + target_compile_definitions(eventrouter PUBLIC -DER_FREERTOS) +elseif(IMPLEMENTATION STREQUAL "posix") + target_compile_definitions(eventrouter PUBLIC -DER_POSIX) +endif() #=============================================================================== # Build tests and example applications diff --git a/extra/cmake/freertos.cmake b/extra/cmake/freertos.cmake index 2b254a9..6e16047 100644 --- a/extra/cmake/freertos.cmake +++ b/extra/cmake/freertos.cmake @@ -11,6 +11,7 @@ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/FreeRTOSConfig.h #ifndef FREERTOSCONFIG_H #define FREERTOSCONFIG_H +#include #include /// These values come from starting with an empty file and silencing compiler @@ -18,7 +19,7 @@ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/FreeRTOSConfig.h /// necessary for the Event Router to function. #define configMAX_PRIORITIES 10 -#define configMINIMAL_STACK_SIZE 1024 +#define configMINIMAL_STACK_SIZE PTHREAD_STACK_MIN #define configTICK_RATE_HZ 1000 #define configTOTAL_HEAP_SIZE (1024 * 1024) #define configUSE_16_BIT_TICKS false @@ -42,3 +43,6 @@ FetchContent_MakeAvailable(freertos) target_link_options(freertos_kernel PUBLIC $<$:-pthread> ) +target_include_directories(freertos_kernel PUBLIC + ${CMAKE_CURRENT_BINARY_DIR} +) \ No newline at end of file diff --git a/extra/example/CMakeLists.txt b/extra/example/CMakeLists.txt index 7931eb7..b580277 100644 --- a/extra/example/CMakeLists.txt +++ b/extra/example/CMakeLists.txt @@ -1,53 +1,11 @@ -#=============================================================================== -# Common Modules -# -# These modules are common to all example applications to demonstrate the -# reusability of event-based code across eventrouter implementations. -# =============================================================================== - -set(COMMON_MODULE_SOURCES # Related header files are directory-local. +add_executable(eventrouter_example module_data_logger.c module_data_uploader.c module_sensor_data_publisher.c + ${IMPLEMENTATION}_eventrouter_example.c + $<$:task_generic.c> ) - -#=============================================================================== -# Baremetal: The simplest example application. -# =============================================================================== - -add_executable(baremetal_eventrouter_example - ${COMMON_MODULE_SOURCES} - baremetal_eventrouter_example.c -) -target_link_libraries(baremetal_eventrouter_example - PRIVATE - baremetal_eventrouter -) - -#=============================================================================== -# FreeRTOS -# =============================================================================== - -add_executable(freertos_eventrouter_example - ${COMMON_MODULE_SOURCES} - freertos_eventrouter_example.c - task_generic.c -) -target_link_libraries(freertos_eventrouter_example - PRIVATE - freertos_eventrouter -) - -#=============================================================================== -# POSIX -# =============================================================================== - -add_executable(posix_eventrouter_example - ${COMMON_MODULE_SOURCES} - posix_eventrouter_example.c - task_generic.c -) -target_link_libraries(posix_eventrouter_example +target_link_libraries(eventrouter_example PRIVATE - posix_eventrouter + eventrouter ) diff --git a/extra/example/module_data_logger.c b/extra/example/module_data_logger.c index 4b8f947..c87e5e7 100644 --- a/extra/example/module_data_logger.c +++ b/extra/example/module_data_logger.c @@ -5,17 +5,20 @@ #include "eventrouter.h" #include "module_sensor_data_publisher.h" -static ErEventHandlerRet_t DataLogger_EventHandler(ErEvent_t* a_event); +static ErEventHandlerRet_t DataLogger_EventHandler(ErEvent_t* a_event, + void* a_context); -ErModule_t g_data_logger_module = ER_CREATE_MODULE(DataLogger_EventHandler); +ErModule_t g_data_logger_module = + ER_CREATE_MODULE(DataLogger_EventHandler, NULL); void DataLogger_Init(void) { ErSubscribe(&g_data_logger_module, ER_EVENT_TYPE__SENSOR_DATA); } -ErEventHandlerRet_t DataLogger_EventHandler(ErEvent_t *a_event) +ErEventHandlerRet_t DataLogger_EventHandler(ErEvent_t* a_event, void* a_context) { + ER_UNUSED(a_context); ErEventHandlerRet_t result = ER_EVENT_HANDLER_RET__HANDLED; switch (a_event->m_type) diff --git a/extra/example/module_data_uploader.c b/extra/example/module_data_uploader.c index 0aa480c..fea529e 100644 --- a/extra/example/module_data_uploader.c +++ b/extra/example/module_data_uploader.c @@ -5,17 +5,21 @@ #include "eventrouter.h" #include "module_sensor_data_publisher.h" -static ErEventHandlerRet_t DataUploader_EventHandler(ErEvent_t* a_event); +static ErEventHandlerRet_t DataUploader_EventHandler(ErEvent_t* a_event, + void* a_context); -ErModule_t g_data_uploader_module = ER_CREATE_MODULE(DataUploader_EventHandler); +ErModule_t g_data_uploader_module = + ER_CREATE_MODULE(DataUploader_EventHandler, NULL); void DataUploader_Init(void) { ErSubscribe(&g_data_uploader_module, ER_EVENT_TYPE__SENSOR_DATA); } -ErEventHandlerRet_t DataUploader_EventHandler(ErEvent_t* a_event) +ErEventHandlerRet_t DataUploader_EventHandler(ErEvent_t* a_event, + void* a_context) { + ER_UNUSED(a_context); ErEventHandlerRet_t result = ER_EVENT_HANDLER_RET__HANDLED; switch (a_event->m_type) diff --git a/extra/example/module_sensor_data_publisher.c b/extra/example/module_sensor_data_publisher.c index 9e06473..efde69e 100644 --- a/extra/example/module_sensor_data_publisher.c +++ b/extra/example/module_sensor_data_publisher.c @@ -6,10 +6,11 @@ #include "eventrouter.h" -static ErEventHandlerRet_t SensorDataPublisher_EventHandler(ErEvent_t *a_event); +static ErEventHandlerRet_t SensorDataPublisher_EventHandler(ErEvent_t *a_event, + void *a_context); ErModule_t g_sensor_data_publisher_module = - ER_CREATE_MODULE(SensorDataPublisher_EventHandler); + ER_CREATE_MODULE(SensorDataPublisher_EventHandler, NULL); static SensorDataEvent_t s_event = { INIT_ER_EVENT(ER_EVENT_TYPE__SENSOR_DATA, &g_sensor_data_publisher_module), @@ -32,8 +33,10 @@ void SensorDataPublisher_Init(void) srand(time(NULL)); } -ErEventHandlerRet_t SensorDataPublisher_EventHandler(ErEvent_t *a_event) +ErEventHandlerRet_t SensorDataPublisher_EventHandler(ErEvent_t *a_event, + void *a_context) { + ER_UNUSED(a_context); ErEventHandlerRet_t result = ER_EVENT_HANDLER_RET__HANDLED; switch (a_event->m_type) diff --git a/extra/test/CMakeLists.txt b/extra/test/CMakeLists.txt index 0e0387a..353acc2 100644 --- a/extra/test/CMakeLists.txt +++ b/extra/test/CMakeLists.txt @@ -1,43 +1,11 @@ -#=============================================================================== -# Baremetal -# =============================================================================== - -add_executable(baremetal_eventrouter_test - baremetal_eventrouter_test.cc +add_executable(eventrouter_test common_eventrouter_test.cc + $<$:baremetal_eventrouter_test.cc> + $<$:os_eventrouter_test.cc> + $<$:mock_os.cc> ) -target_link_libraries(baremetal_eventrouter_test PRIVATE - baremetal_eventrouter +target_link_libraries(eventrouter_test PUBLIC + eventrouter gtest_main ) -gtest_discover_tests(baremetal_eventrouter_test) - -#=============================================================================== -# FreeRTOS -# =============================================================================== - -add_executable(freertos_eventrouter_test - common_eventrouter_test.cc - os_eventrouter_test.cc - mock_os.cc -) -target_link_libraries(freertos_eventrouter_test PRIVATE - freertos_eventrouter - gtest_main -) -gtest_discover_tests(freertos_eventrouter_test) - -#=============================================================================== -# POSIX -# =============================================================================== - -add_executable(posix_eventrouter_test - common_eventrouter_test.cc - os_eventrouter_test.cc - mock_os.cc -) -target_link_libraries(posix_eventrouter_test PRIVATE - posix_eventrouter - gtest_main -) -gtest_discover_tests(posix_eventrouter_test) +gtest_discover_tests(eventrouter_test) \ No newline at end of file diff --git a/extra/test/mock_module.h b/extra/test/mock_module.h index 7b28eed..aa30faf 100644 --- a/extra/test/mock_module.h +++ b/extra/test/mock_module.h @@ -39,8 +39,9 @@ struct MockModule m_module.m_handler = EventHandler; } - static ErEventHandlerRet_t EventHandler(ErEvent_t *a_event) + static ErEventHandlerRet_t EventHandler(ErEvent_t *a_event, void *a_context) { + ER_UNUSED(a_context); m_last_event_handled = a_event; return m_event_handler_ret; } @@ -56,7 +57,7 @@ ErEvent_t *MockModule::m_last_event_handled = nullptr; template ErModule_t MockModule::m_module = - ER_CREATE_MODULE(MockModule::EventHandler); + ER_CREATE_MODULE(MockModule::EventHandler, NULL); template ErEventHandlerRet_t MockModule::m_event_handler_ret =