Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connections as trigger and a comprehensive restructuring #30

Merged
merged 25 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ BasedOnStyle: LLVM
Language: Cpp
IndentWidth: 2
ColumnLimit: 120
AllowShortIfStatementsOnASingleLine: false
SortIncludes: false
AllowShortIfStatementsOnASingleLine: false
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
**/build/
**/src-gen/
**/bin/
.vscode/
cmake-build-debug
cmake-build-release
.idea
include/reactor-uc/config.h

10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ set(TEST_COVERAGE OFF CACHE BOOL "Compute test coverage")
set(ASAN OFF CACHE BOOL "Compile with AddressSanitizer")
set(PLATFORM "POSIX" CACHE STRING "Platform to target")

set(EVENT_QUEUE_SIZE 10 CACHE STRING "Static size of the event queue")
set(REACTION_QUEUE_SIZE 10 CACHE STRING "Static size of the reaction queue")

# Code coverage setup
if(TEST_COVERAGE)
set(CMAKE_BUILD_TYPE "Debug")
Expand All @@ -22,6 +25,7 @@ endif()

# Clang-tidy for static code analysis
if(BUILD_TESTS)
set(CMAKE_BUILD_TYPE "Debug")
find_program(CLANG_TIDY clang-tidy)
if (CLANG_TIDY)
set(CMAKE_C_CLANG_TIDY clang-tidy; --header-filter=include/\(.*\)\\.h; --warnings-as-errors)
Expand All @@ -30,6 +34,7 @@ if(BUILD_TESTS)
endif()
endif()

# TODO: Explicitly name all the sources.
file(GLOB SOURCES "src/*.c")
message(${SOURCES})

Expand All @@ -49,6 +54,11 @@ endif ()
add_compile_options (-fdiagnostics-color=always)
target_include_directories(reactor-uc PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)

configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/reactor-uc/config.h.in
${CMAKE_CURRENT_SOURCE_DIR}/include/reactor-uc/config.h
)

if(BUILD_TESTS)
target_compile_options(reactor-uc PRIVATE -Wall -Wextra -pedantic -Werror)
set(UNITY_DIR ${CMAKE_CURRENT_LIST_DIR}/external/Unity)
Expand Down
2 changes: 1 addition & 1 deletion examples/posix/timer_ex.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void MyReactor_ctor(struct MyReactor *self, Environment *env) {
Reactor_ctor(&self->super, "MyReactor", env, NULL, NULL, 0, self->_reactions, 1, self->_triggers, 1);
MyReaction_ctor(&self->my_reaction, &self->super);
Timer_ctor(&self->timer.super, &self->super, MSEC(0), MSEC(100), self->timer.effects, 1);
self->timer.super.super.register_effect(&self->timer.super.super, &self->my_reaction.super);
TIMER_REGISTER_EFFECT(self->timer, self->my_reaction);
}

int main() {
Expand Down
2 changes: 1 addition & 1 deletion examples/zephyr/src/timer_ex.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void MyReactor_ctor(struct MyReactor *self, Environment *env) {
Reactor_ctor(&self->super, "MyReactor", env, NULL, NULL, 0, self->_reactions, 1, self->_triggers, 1);
MyReaction_ctor(&self->my_reaction, &self->super);
Timer_ctor(&self->timer.super, &self->super, MSEC(0), MSEC(100), self->timer.effects, 1);
self->timer.super.super.register_effect(&self->timer.super.super, &self->my_reaction.super);
TIMER_REGISTER_EFFECT(self->timer, self->my_reaction);
}

struct MyReactor my_reactor;
Expand Down
18 changes: 7 additions & 11 deletions include/reactor-uc/action.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

#include "reactor-uc/reaction.h"
#include "reactor-uc/trigger.h"
#include "reactor-uc/util.h"

typedef struct Action Action;
typedef struct LogicalAction LogicalAction;
Expand All @@ -13,19 +12,16 @@ struct Action {
Trigger super;
interval_t min_offset;
interval_t min_spacing;
tag_t previous_event;
TriggerValue trigger_value;
/**
* @brief This function schedules an event for the logical action. The value
* associated with the event must already have been placed in
*
*/
tag_t previous_event; // Used to enforce min_spacing
TriggerEffects effects;
TriggerSources sources;
TriggerValue trigger_value; // This is where data associated with schedueled events are stored
void (*schedule)(Action *self, interval_t offset, const void *value);
};

void Action_ctor(Action *self, interval_t min_offset, interval_t min_spacing, Reactor *parent, Reaction **sources,
size_t sources_size, Reaction **effects, size_t effects_size, void *value_buf, size_t value_size,
size_t value_capacity, void (*schedule)(Action *, interval_t, const void *));
void Action_ctor(Action *self, TriggerType type, interval_t min_offset, interval_t min_spacing, Reactor *parent,
Reaction **sources, size_t sources_size, Reaction **effects, size_t effects_size, void *value_buf,
size_t value_size, size_t value_capacity, void (*schedule)(Action *, interval_t, const void *));

struct LogicalAction {
Action super;
Expand Down
4 changes: 4 additions & 0 deletions include/reactor-uc/builtin_triggers.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ typedef struct Shutdown Shutdown;

struct Startup {
Trigger super;
TriggerEffects effects;
Startup *next; // Used to trigger all startup-reactions with only a single startup-event on the event queue.
};

void Startup_ctor(Startup *self, Reactor *parent, Reaction **effects, size_t effects_size);

struct Shutdown {
Trigger super;
TriggerEffects effects;
Shutdown *next;
};

void Shutdown_ctor(Shutdown *self, Reactor *parent, Reaction **effects, size_t effects_size);
Expand Down
4 changes: 4 additions & 0 deletions include/reactor-uc/config.h.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// This file is code-generated based on command line variables passed to
// CMake during compilation. Do not edit this file.
#ifndef REACTOR_UC_CONFIG_H
#define REACTOR_UC_CONFIG_H

#define EVENT_QUEUE_SIZE @EVENT_QUEUE_SIZE@
#define REACTION_QUEUE_SIZE @REACTION_QUEUE_SIZE@

#endif
38 changes: 27 additions & 11 deletions include/reactor-uc/connection.h
Original file line number Diff line number Diff line change
@@ -1,47 +1,63 @@
#ifndef REACTOR_UC_CONNECTION_H
#define REACTOR_UC_CONNECTION_H

#include "reactor-uc/action.h"
#include "reactor-uc/port.h"
#include "reactor-uc/reaction.h"
#include "reactor-uc/reactor.h"
#include "reactor-uc/trigger.h"
#include "reactor-uc/action.h"

typedef struct Connection Connection;
typedef struct LogicalConnection LogicalConnection;
typedef struct PhysicalConnection PhysicalConnection;
typedef struct DelayedConnection DelayedConnection;
typedef struct Port Port;
typedef struct Output Output;

typedef enum { CONN_LOGICAL, CONN_PHYSICAL, CONN_DELAYED } ConnectionType;

struct Connection {
Reactor *parent;
ConnectionType type;
Port *upstream;
Port **downstreams;
Trigger super;
Port *upstream; // Single upstream port
Port **downstreams; // Pointer to array of pointers of downstream ports
size_t downstreams_size;
size_t downstreams_registered;
// FIXME: Remove and do it inline in macro.h instead
void (*register_downstream)(Connection *, Port *);
Output *(*get_final_upstream)(Connection *);
/**
* @brief Recursive function that traverses down the connection until it
* reaches the final Inputs or Connections that schedule events.
*/
void (*trigger_downstreams)(Connection *, const void *value_ptr, size_t value_size);
};

void Connection_ctor(Connection *self, TriggerType type, Reactor *parent, Port *upstream, Port **downstreams,
size_t num_downstreams, TriggerValue *trigger_value, void (*prepare)(Trigger *),
void (*cleanup)(Trigger *), void (*trigger_downstreams)(Connection *, const void *, size_t));

struct LogicalConnection {
Connection super;
};

void Connection_ctor(Connection *self, ConnectionType type, Reactor *parent, Port *upstream, Port **downstreams,
size_t num_downstreams);
void LogicalConnection_ctor(LogicalConnection *self, Reactor *parent, Port *upstream, Port **downstreams,
size_t num_downstreams);

struct DelayedConnection {
Connection super;
interval_t delay;
TriggerValue trigger_value; // FIFO for storing outstanding events
};

void DelayedConnection_ctor(DelayedConnection *self, Reactor *parent, Port *upstream, Port **downstreams,
size_t num_downstreams, interval_t delayl);
size_t num_downstreams, interval_t delay, void *value_buf, size_t value_size,
size_t value_capacity);

struct PhysicalConnection {
Connection super;
interval_t delay;
TriggerValue trigger_value;
};

void PhysicalConnection_ctor(PhysicalConnection *self, Reactor *parent, Port *upstream, Port **downstreams,
size_t num_downstreams, interval_t delayl);
size_t num_downstreams, interval_t delay, void *value_buf, size_t value_size,
size_t value_capacity);
#endif
76 changes: 74 additions & 2 deletions include/reactor-uc/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,88 @@
#define lf_set(port, val) \
do { \
typeof(val) __val = (val); \
(port)->super.super.copy_value_and_trigger_downstreams(&(port)->super.super, (const void *)&__val, sizeof(__val)); \
Connection *__conn = (port)->super.super.conn_out; \
if (__conn) { \
__conn->trigger_downstreams(__conn, (const void *)&__val, sizeof(__val)); \
} \
} while (0)

#define lf_get(trigger) (trigger)->buffer[((Trigger *)trigger)->trigger_value->read_idx]
/**
* @brief Retreive the value of a trigger and cast it to the expected type
* FIXME: Handle errors
*
*/
#define lf_get(trigger) (*(typeof((trigger)->buffer[0]) *)(((Trigger *)(trigger))->get((Trigger *)(trigger))))

/**
* @brief Retrieve the is_present field of the trigger
*/
#define lf_is_present(trigger) (((Trigger *)(trigger))->is_present)

// TODO: We need to handle the case when action already has been scheduled.
// then we need a runtime error and NOT overwrite the scheduled value.
/**
* @brief Schedule an event on an action
*
*/
#define lf_schedule(action, val, offset) \
do { \
typeof(val) __val = (val); \
(action)->super.super.schedule(&(action)->super.super, (offset), (const void *)&__val); \
} while (0)

/**
* @brief Convenience macro for registering a reaction as an effect of a trigger.
* The input must be a pointer to a derived Trigger type with an effects field.
* E.g. Action, Timer, Startup, Shutdown, Input
*
*/
#define TRIGGER_REGISTER_EFFECT(trigger, effect) \
do { \
assert((trigger)->effects.num_registered < (trigger)->effects.size); \
(trigger)->effects.reactions[(trigger)->effects.num_registered++] = (effect); \
} while (0)

// The following macros casts the inputs into the correct types before calling TRIGGER_REGISTER_EFFECTs
#define ACTION_REGISTER_EFFECT(action, effect) TRIGGER_REGISTER_EFFECT((Action *)&(action), (Reaction *)&(effect))
#define TIMER_REGISTER_EFFECT(timer, effect) TRIGGER_REGISTER_EFFECT((Timer *)(&(timer)), (Reaction *)(&(effect)))
#define STARTUP_REGISTER_EFFECT(startup, effect) TRIGGER_REGISTER_EFFECT((Startup *)&(startup), (Reaction *)&(effect))
#define SHUTDOWN_REGISTER_EFFECT(shutdown, effect) \
TRIGGER_REGISTER_EFFECT((Shutdown *)&(shutdown), (Reaction *)&(effect))
#define INPUT_REGISTER_EFFECT(input, effect) TRIGGER_REGISTER_EFFECT((Input *)&(input), (Reaction *)&(effect))

/**
* @brief Convenience macro for registering a trigger as an effect of a reaction.
*
*/
#define REACTION_REGISTER_EFFECT(reaction, effect) \
do { \
Reaction *__reaction = (Reaction *)&(reaction); \
assert((__reaction)->effects_registered < (__reaction)->effects_size); \
(__reaction)->effects[(__reaction)->effects_registered++] = (Trigger *)&(effect); \
} while (0)

// Register a reaction as a source of a trigger. `trigger` must be a pointer to
// a derived Trigger type.
#define TRIGGER_REGISTER_SOURCE(trigger, source) \
do { \
assert((trigger)->sources.num_registered < (trigger)->sources.size); \
(trigger)->sources.reactions[(trigger)->sources.num_registered++] = (source); \
} while (0)

// Convenient translation from a user trigger to a pointer to the derived Trigger type.
#define ACTION_REGISTER_SOURCE(action, source) TRIGGER_REGISTER_SOURCE((Action *)&(action), (Reaction *)&(source))
#define OUTPUT_REGISTER_SOURCE(output, source) TRIGGER_REGISTER_SOURCE((Output *)&(output), (Reaction *)&(source))

// Convenience macro to register a downstream port on a connection.
// TODO: Replace the entire function with an inline macro to save memory
#define CONN_REGISTER_DOWNSTREAM(conn, down) \
do { \
((Connection *)&(conn))->register_downstream((Connection *)&(conn), (Port *)&(down)); \
} while (0)

// TODO: The following macro is defined to avoid compiler warnings. Ideally we would
// not have to specify any alignment on any structs. It is a TODO to understand exactly why
// the compiler complains and what we can do about it.
#define MEM_ALIGNMENT 32
#endif
5 changes: 5 additions & 0 deletions include/reactor-uc/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "reactor-uc/tag.h"
typedef struct Platform Platform;

// Return type for wait_until and wait_until_interruptable
typedef enum {
SLEEP_INTERRUPTED = 0,
SLEEP_COMPLETED = 1,
Expand All @@ -21,7 +22,11 @@ struct Platform {
void (*new_async_event)(Platform *self);
};

// Return a pointer to a Platform object. Must be implemented for each
// target platform.
Platform *Platform_new(void);

// Construct a Platform object. Must be implemented for each target platform.
void Platform_ctor(Platform *self);

#endif
23 changes: 14 additions & 9 deletions include/reactor-uc/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,33 @@ typedef struct Output Output;
typedef struct Connection Connection;
typedef struct Port Port;

// Abstract Port type, inherits from Trigger
struct Port {
Trigger super;
TriggerValue trigger_value;
Connection *conn_in;
Connection *conn_out;
void (*copy_value_and_trigger_downstreams)(Port *self, const void *value, size_t value_size);
Connection *conn_in; // Connection coming into the port.
Connection *conn_out; // Connection going out of the port.
};

// Input port. In the user-defined derived struct there must be a `buffer` field for storing the values.
struct Input {
Port super;
void (*trigger_effects)(Input *);
TriggerEffects effects;
void *value_ptr; // Pointer to the `buffer` field in the user Input port struct.
size_t value_size; // Size of the data stored in this Input Port.
};

// Output ports do not have any buffers.
struct Output {
Port super;
TriggerSources sources;
};

void Input_ctor(Input *self, Reactor *parent, Reaction **effects, size_t effects_size, size_t value_size,
void *value_buf, size_t value_capacity);
void Input_ctor(Input *self, Reactor *parent, Reaction **effects, size_t effects_size, void *value_ptr,
size_t value_size);

void Output_ctor(Output *self, Reactor *parent, Reaction **sources, size_t sources_size);
void Port_ctor(Port *self, TriggerType type, Reactor *parent, Reaction **effects, size_t effects_size,
Reaction **sources, size_t sources_size, size_t value_size, void *value_buf, size_t value_capacity);

void Port_ctor(Port *self, TriggerType type, Reactor *parent, void (*prepare)(Trigger *), void (*cleanup)(Trigger *),
const void *(*get)(Trigger *));

#endif
10 changes: 1 addition & 9 deletions include/reactor-uc/queues.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
#ifndef REACTOR_UC_QUEUES_H
#define REACTOR_UC_QUEUES_H

// FIXME: These defines should be someplace else
#ifndef EVENT_QUEUE_SIZE
#define EVENT_QUEUE_SIZE 10
#endif

#ifndef REACTION_QUEUE_SIZE
#define REACTION_QUEUE_SIZE 10
#endif

#include "reactor-uc/config.h"
#include "reactor-uc/event.h"
#include "reactor-uc/reaction.h"
#include "reactor-uc/tag.h"
Expand Down
Loading
Loading