diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index 2f3fd275f..31409c377 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -105,5 +105,6 @@ define(NUMBER_OF_FEDERATES)
define(NUMBER_OF_WORKERS)
define(SCHEDULER)
define(LF_SOURCE_DIRECTORY)
+define(LF_PACKAGE_DIRECTORY)
define(LF_FILE_SEPARATOR)
define(WORKERS_NEEDED_FOR_FEDERATE)
diff --git a/core/reactor.c b/core/reactor.c
index 1172d2c60..62824e480 100644
--- a/core/reactor.c
+++ b/core/reactor.c
@@ -368,8 +368,6 @@ int lf_reactor_c_main(int argc, const char* argv[]) {
_lf_trigger_startup_reactions();
_lf_initialize_timers();
- _lf_initialize_watchdog_mutexes();
-
// If the stop_tag is (0,0), also insert the shutdown
// reactions. This can only happen if the timeout time
// was set to 0.
diff --git a/core/reactor_common.c b/core/reactor_common.c
index 6caafe63a..7b52548c2 100644
--- a/core/reactor_common.c
+++ b/core/reactor_common.c
@@ -56,6 +56,14 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "hashset/hashset.h"
#include "hashset/hashset_itr.h"
+#ifdef LF_THREADED
+#include "watchdog.h"
+
+// Code generated global variables.
+extern int _lf_watchdog_number;
+extern watchdog_t* _lf_watchdogs;
+#endif
+
// Global variable defined in tag.c:
extern tag_t current_tag;
extern instant_t start_time;
@@ -1291,50 +1299,6 @@ trigger_handle_t _lf_schedule_int(lf_action_base_t* action, interval_t extra_del
return _lf_schedule_value(action, extra_delay, container, 1);
}
-#ifdef LF_THREADED
-
-void* run_watchdog(void* arg) {
- watchdog_t* watchdog = (watchdog_t*)arg;
-
- self_base_t* base = watchdog->base;
- lf_mutex_lock(&(base->watchdog_mutex));
-
- while (lf_time_physical() < watchdog->expiration) {
- interval_t T = watchdog->expiration - lf_time_physical();
- lf_mutex_unlock(&(base->watchdog_mutex));
- lf_sleep(T);
- lf_mutex_lock(&(base->watchdog_mutex));
- }
-
- if (watchdog->expiration != NEVER) {
- watchdog_function_t watchdog_func = watchdog->watchdog_function;
- (*watchdog_func)(base);
- }
- watchdog->thread_active = false;
-
- lf_mutex_unlock(&(base->watchdog_mutex));
- watchdog->thread_active = false;
- return NULL;
-}
-
-void _lf_watchdog_start(watchdog_t* watchdog, interval_t additional_timeout) {
- // Assumes reaction mutex is already held.
-
- self_base_t* base = watchdog->base;
-
- watchdog->expiration = lf_time_logical() + watchdog->min_expiration + additional_timeout;
-
- if (!watchdog->thread_active) {
- lf_thread_create(&(watchdog->thread_id), run_watchdog, watchdog);
- watchdog->thread_active = true;
- }
-}
-
-void _lf_watchdog_stop(watchdog_t* watchdog) {
- watchdog->expiration = NEVER;
-}
-#endif
-
/**
* Invoke the given reaction
@@ -1345,8 +1309,8 @@ void _lf_watchdog_stop(watchdog_t* watchdog) {
void _lf_invoke_reaction(reaction_t* reaction, int worker) {
#ifdef LF_THREADED
- if (((self_base_t*) reaction->self)->has_watchdog == true) {
- lf_mutex_lock(&(((self_base_t*) reaction->self)->watchdog_mutex));
+ if (((self_base_t*) reaction->self)->reactor_mutex != NULL) {
+ lf_mutex_lock((lf_mutex_t*)((self_base_t*)reaction->self)->reactor_mutex);
}
#endif
@@ -1358,8 +1322,8 @@ void _lf_invoke_reaction(reaction_t* reaction, int worker) {
#ifdef LF_THREADED
- if (((self_base_t*) reaction->self)->has_watchdog == true) {
- lf_mutex_unlock(&(((self_base_t*) reaction->self)->watchdog_mutex));
+ if (((self_base_t*) reaction->self)->reactor_mutex != NULL) {
+ lf_mutex_unlock((lf_mutex_t*)((self_base_t*)reaction->self)->reactor_mutex);
}
#endif
}
@@ -1802,6 +1766,13 @@ void termination(void) {
lf_print_warning("Memory allocated for tokens has not been freed!");
lf_print_warning("Number of unfreed tokens: %d.", _lf_count_token_allocations);
}
+#ifdef LF_THREADED
+ for (int i = 0; i < _lf_watchdog_number; i++) {
+ if (_lf_watchdogs[i].base->reactor_mutex != NULL) {
+ free(_lf_watchdogs[i].base->reactor_mutex);
+ }
+ }
+#endif
_lf_free_all_reactors();
free(_lf_is_present_fields);
free(_lf_is_present_fields_abbreviated);
diff --git a/core/threaded/CMakeLists.txt b/core/threaded/CMakeLists.txt
index 95989b8ad..334c8bfa2 100644
--- a/core/threaded/CMakeLists.txt
+++ b/core/threaded/CMakeLists.txt
@@ -7,6 +7,7 @@ set(
scheduler_NP.c
scheduler_PEDF_NP.c
scheduler_sync_tag_advance.c
+ watchdog.c
)
list(APPEND INFO_SOURCES ${THREADED_SOURCES})
diff --git a/core/threaded/watchdog.c b/core/threaded/watchdog.c
new file mode 100644
index 000000000..f93245fdc
--- /dev/null
+++ b/core/threaded/watchdog.c
@@ -0,0 +1,83 @@
+/**
+ * @file
+ * @author Benjamin Asch
+ * @author Edward A. Lee
+ * @copyright (c) 2023, The University of California at Berkeley.
+ * License: BSD 2-clause
+ * @brief Definitions for watchdogs.
+ */
+
+#include
+#include "watchdog.h"
+
+extern int _lf_watchdog_number;
+extern watchdog_t* _lf_watchdogs;
+
+/**
+ * @brief Initialize watchdog mutexes.
+ * For any reactor with one or more watchdogs, the self struct should have a non-NULL
+ * `reactor_mutex` field which points to an instance of `lf_mutex_t`.
+ * This function initializes those mutexes.
+ */
+void _lf_initialize_watchdog_mutexes() {
+ for (int i = 0; i < _lf_watchdog_number; i++) {
+ self_base_t* current_base = _lf_watchdogs[i].base;
+ if (current_base->reactor_mutex != NULL) {
+ lf_mutex_init((lf_mutex_t*)(current_base->reactor_mutex));
+ }
+ }
+}
+
+/**
+ * @brief Thread function for watchdog.
+ * This function sleeps until physical time exceeds the expiration time of
+ * the watchdog and then invokes the watchdog expiration handler function.
+ * In normal usage, the expiration time is incremented while the thread is
+ * sleeping, so the watchdog never expires and the handler function is never
+ * invoked.
+ * This function acquires the reaction mutex and releases it while sleeping.
+ *
+ * @param arg A pointer to the watchdog struct
+ * @return NULL
+ */
+void* _lf_run_watchdog(void* arg) {
+ watchdog_t* watchdog = (watchdog_t*)arg;
+
+ self_base_t* base = watchdog->base;
+ assert(base->reactor_mutex != NULL);
+ lf_mutex_lock((lf_mutex_t*)(base->reactor_mutex));
+ instant_t physical_time = lf_time_physical();
+ while (physical_time < watchdog->expiration) {
+ interval_t T = watchdog->expiration - physical_time;
+ lf_mutex_unlock((lf_mutex_t*)base->reactor_mutex);
+ lf_sleep(T);
+ lf_mutex_lock((lf_mutex_t*)(base->reactor_mutex));
+ physical_time = lf_time_physical();
+ }
+
+ if (watchdog->expiration != NEVER) {
+ watchdog_function_t watchdog_func = watchdog->watchdog_function;
+ (*watchdog_func)(base);
+ }
+ watchdog->thread_active = false;
+
+ lf_mutex_unlock((lf_mutex_t*)(base->reactor_mutex));
+ return NULL;
+}
+
+void lf_watchdog_start(watchdog_t* watchdog, interval_t additional_timeout) {
+ // Assumes reaction mutex is already held.
+
+ self_base_t* base = watchdog->base;
+
+ watchdog->expiration = lf_time_logical() + watchdog->min_expiration + additional_timeout;
+
+ if (!watchdog->thread_active) {
+ lf_thread_create(&(watchdog->thread_id), _lf_run_watchdog, watchdog);
+ watchdog->thread_active = true;
+ }
+}
+
+void lf_watchdog_stop(watchdog_t* watchdog) {
+ watchdog->expiration = NEVER;
+}
diff --git a/include/api/api.h b/include/api/api.h
index a17000050..505e6aed9 100644
--- a/include/api/api.h
+++ b/include/api/api.h
@@ -170,19 +170,6 @@ trigger_handle_t lf_schedule_value(void* action, interval_t extra_delay, void* v
*/
bool lf_check_deadline(void* self, bool invoke_deadline_handler);
-#ifdef LF_THREADED
-/**
- * Begin the watchdog.
- *
- * @param watchdog The watchdog to be started.
- * @param additional_timeout The timeout to be added to the minimum
- * experiation of the watchdog.
- **/
-void lf_watchdog_start(watchdog_t* watchdog, interval_t additional_timeout);
-
-void lf_watchdog_stop(watchdog_t* watchdog);
-#endif
-
/**
* Compare two tags. Return -1 if the first is less than
* the second, 0 if they are equal, and +1 if the first is
diff --git a/include/core/lf_types.h b/include/core/lf_types.h
index d66039e53..d71dbaf7a 100644
--- a/include/core/lf_types.h
+++ b/include/core/lf_types.h
@@ -140,13 +140,6 @@ typedef pqueue_pri_t index_t;
*/
typedef void(*reaction_function_t)(void*);
-/**
- * Watchdog function type. The argument passed to one of
- * these watchdog functions is a pointer to the self struct
- * for the reactor.
- */
-typedef void(*watchdog_function_t)(void*);
-
/** Trigger struct representing an output, timer, action, or input. See below. */
typedef struct trigger_t trigger_t;
@@ -216,23 +209,6 @@ struct event_t {
event_t* next; // Pointer to the next event lined up in superdense time.
};
-/** Typdef for watchdog_t struct, used to call watchdog handler. */
-typedef struct watchdog_t watchdog_t;
-
-#ifdef LF_THREADED
-/** Watchdog struct for handler. */
-struct watchdog_t {
- struct self_base_t* base; // The reactor that contains the watchdog.
- trigger_t* trigger; // The trigger associated with this watchdog.
- instant_t expiration; // The expiration instant for the watchdog. (Initialized to NEVER)
- interval_t min_expiration; // The minimum expiration interval for the watchdog.
- lf_thread_t thread_id; // The thread that the watchdog is meant to run on.
- bool thread_active; // Boolean indicating whether or not thread is active.
- watchdog_function_t watchdog_function; // The function/handler for the watchdog.
-};
-#endif
-
-
/**
* Trigger struct representing an output, timer, action, or input.
*/
@@ -303,9 +279,8 @@ typedef struct self_base_t {
struct allocation_record_t *allocations;
struct reaction_t *executing_reaction; // The currently executing reaction of the reactor.
#ifdef LF_THREADED
- lf_mutex_t watchdog_mutex; // The mutex for this reactor to be acquired before reaction
- // invocation.
- bool has_watchdog; // Boolean signifying initialization of watchdog_mutex
+ void* reactor_mutex; // If not null, this is expected to point to an lf_mutex_t.
+ // It is not declared as such to avoid a dependence on platform.h.
#endif
#ifdef MODAL_REACTORS
reactor_mode_state_t _lf__mode_state; // The current mode (for modal models).
diff --git a/include/core/platform.h b/include/core/platform.h
index 30fa251ce..2a632d5fc 100644
--- a/include/core/platform.h
+++ b/include/core/platform.h
@@ -36,6 +36,10 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef PLATFORM_H
#define PLATFORM_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "lf_types.h"
#if defined(LF_THREADED) && defined(LF_UNTHREADED)
@@ -313,4 +317,8 @@ extern int lf_sleep_until_locked(instant_t wakeup_time);
*/
DEPRECATED(extern int lf_nanosleep(interval_t sleep_duration));
+#ifdef __cplusplus
+}
+#endif
+
#endif // PLATFORM_H
diff --git a/include/core/reactor.h b/include/core/reactor.h
index d09a60ec0..a2d527688 100644
--- a/include/core/reactor.h
+++ b/include/core/reactor.h
@@ -535,20 +535,6 @@ trigger_handle_t _lf_schedule_copy(lf_action_base_t* action, interval_t offset,
*/
void _lf_fd_send_stop_request_to_rti(void);
-
-#ifdef LF_THREADED
-/**
- * Function to start the watchdog.
- *
- * @param watchdog The watchdog to be started
- * @param additional_timeout Additional timeout to be added to the watchdog's
- * minimum expiration.
- **/
-void _lf_watchdog_start(watchdog_t* watchdog, interval_t additional_timeout);
-
-void _lf_watchdog_stop(watchdog_t* watchdog);
-#endif
-
/**
* These functions must be implemented by both threaded and unthreaded
* runtime. Should be routed to appropriate API calls in platform.h
diff --git a/include/core/reactor_common.h b/include/core/reactor_common.h
index 0efe4b110..7e0620346 100644
--- a/include/core/reactor_common.h
+++ b/include/core/reactor_common.h
@@ -82,11 +82,6 @@ trigger_handle_t _lf_insert_reactions_for_trigger(trigger_t* trigger, lf_token_t
void _lf_advance_logical_time(instant_t next_time);
trigger_handle_t _lf_schedule_int(lf_action_base_t* action, interval_t extra_delay, int value);
-#ifdef LF_THREADED
-void* run_watchdog(void* arg);
-void _lf_watchdog_start(watchdog_t* watchdog, interval_t additional_timeout);
-void _lf_watchdog_stop(watchdog_t* watchdog);
-#endif
void _lf_invoke_reaction(reaction_t* reaction, int worker);
void schedule_output_reactions(reaction_t* reaction, int worker);
int process_args(int argc, const char* argv[]);
diff --git a/include/core/threaded/watchdog.h b/include/core/threaded/watchdog.h
new file mode 100644
index 000000000..d14c7adad
--- /dev/null
+++ b/include/core/threaded/watchdog.h
@@ -0,0 +1,67 @@
+/**
+ * @file
+ * @author Benjamin Asch
+ * @author Edward A. Lee
+ * @copyright (c) 2023, The University of California at Berkeley.
+ * License: BSD 2-clause
+ * @brief Declarations for watchdogs.
+ */
+
+#ifndef WATCHDOG_H
+#define WATCHDOG_H 1
+
+#include "platform.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Watchdog function type. The argument passed to one of
+ * these watchdog functions is a pointer to the self struct
+ * for the reactor.
+ */
+typedef void(*watchdog_function_t)(void*);
+
+/** Typdef for watchdog_t struct, used to call watchdog handler. */
+typedef struct watchdog_t watchdog_t;
+
+/** Watchdog struct for handler. */
+struct watchdog_t {
+ struct self_base_t* base; // The reactor that contains the watchdog.
+ trigger_t* trigger; // The trigger associated with this watchdog.
+ instant_t expiration; // The expiration instant for the watchdog. (Initialized to NEVER)
+ interval_t min_expiration; // The minimum expiration interval for the watchdog.
+ lf_thread_t thread_id; // The thread that the watchdog is meant to run on.
+ bool thread_active; // Boolean indicating whether or not thread is active.
+ watchdog_function_t watchdog_function; // The function/handler for the watchdog.
+};
+
+/**
+ * @brief Start or restart the watchdog timer.
+ * This function sets the expiration time of the watchdog to the current logical time
+ * plus the minimum timeout of the watchdog plus the specified `additional_timeout`.
+ * If a watchdog timer thread is not already running, then this function will start one.
+ * This function assumes the reactor mutex is held when it is called; this assumption
+ * is satisfied whenever this function is called from within a reaction that declares
+ * the watchdog as an effect.
+ *
+ * @param watchdog The watchdog to be started
+ * @param additional_timeout Additional timeout to be added to the watchdog's
+ * minimum expiration.
+ */
+void lf_watchdog_start(watchdog_t* watchdog, interval_t additional_timeout);
+
+/**
+ * @brief Stop the specified watchdog without invoking the expiration handler.
+ * This function sets the expiration time of the watchdog to `NEVER`.
+ *
+ * @param watchdog The watchdog.
+ */
+void lf_watchdog_stop(watchdog_t* watchdog);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/schedule.c b/lib/schedule.c
index 11e955dc4..30367cb7f 100644
--- a/lib/schedule.c
+++ b/lib/schedule.c
@@ -199,12 +199,3 @@ bool lf_check_deadline(void* self, bool invoke_deadline_handler) {
}
return false;
}
-
-#ifdef LF_THREADED
-void lf_watchdog_start(watchdog_t* watchdog, interval_t additional_timeout) {
- return _lf_watchdog_start(watchdog, additional_timeout);
-}
-void lf_watchdog_stop(watchdog_t* watchdog) {
- return _lf_watchdog_stop(watchdog);
-}
-#endif
\ No newline at end of file
diff --git a/lingua-franca-ref.txt b/lingua-franca-ref.txt
index 021098cc4..5d1996fa8 100644
--- a/lingua-franca-ref.txt
+++ b/lingua-franca-ref.txt
@@ -1 +1 @@
-watchdogs
\ No newline at end of file
+watchdogs-eal2
diff --git a/util/README.md b/util/README.md
index c05656fe3..8f1bf9226 100644
--- a/util/README.md
+++ b/util/README.md
@@ -4,7 +4,7 @@ This directory contains source files for use by Lingua Franca programs using the
C target. To use a file in this directory, specify a target property as follows:
```
target C {
- files: ["/lib/C/util/filename", ...],
+ files: ["/lib/c/reactor-c/util/filename", ...],
...
};
```
@@ -14,4 +14,4 @@ directory. Hence, you can include the file in your C code as follows:
preamble {=
#include "filename"
=}
-```
\ No newline at end of file
+```