diff --git a/core/reactor.c b/core/reactor.c index 6e2fd24a1..71e58377f 100644 --- a/core/reactor.c +++ b/core/reactor.c @@ -328,8 +328,9 @@ int lf_reactor_c_main(int argc, const char* argv[]) { LF_PRINT_DEBUG("Initializing."); initialize_global(); - // Set start time - start_time = lf_time_physical(); + // Set the start time of the program (if it is not set from the command line). Possibly wait for + // it to arrive also. + _lf_set_and_wait_for_start_time(); #ifndef FEDERATED lf_tracing_set_start_time(start_time); #endif diff --git a/core/reactor_common.c b/core/reactor_common.c index 358480eb8..ab0c07dd4 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -42,6 +42,9 @@ // Global variable defined in tag.c: extern instant_t start_time; +// Whether the start time of the program is decided by the user via a command line argument. +bool start_time_specified = false; + #if !defined NDEBUG // Global variable defined in lf_token.c: extern int _lf_count_payload_allocations; @@ -882,6 +885,12 @@ void usage(int argc, const char* argv[]) { printf(" Executed in threads if possible (optional feature).\n\n"); printf(" -i, --id \n"); printf(" The ID of the federation that this reactor will join.\n\n"); + printf(" -s, --start-time \n"); + printf(" The logical start time of the program, expressed as an absolute time point\n"); + printf(" which is the duration since the epoch of the underlying system clock.\n"); + printf(" The units are nsec, usec, msec, sec, minute, hour, day, week or the plurals of those.\n"); + printf(" On linux, to compute a start time of 2 minutes into the future, expressed in seconds, do:\n"); + printf(" `date -d \"2 minutes\" +%%s`.\n\n"); #ifdef FEDERATED printf(" -r, --rti \n"); printf(" The address of the RTI, which can be in the form of user@host:port or ip:port.\n\n"); @@ -901,6 +910,43 @@ void usage(int argc, const char* argv[]) { int default_argc = 0; const char** default_argv = NULL; +interval_t duration_string_to_interval(const char* time_spec, const char* units) { + interval_t duration = 0LL; + +#if defined(PLATFORM_ARDUINO) + duration = atol(time_spec); +#else + duration = atoll(time_spec); +#endif + + // A parse error returns 0LL, so check to see whether that is what is meant. + if (duration == 0LL && strncmp(time_spec, "0", 1) != 0) { + // Parse error. + return -1; + } + if (strncmp(units, "sec", 3) == 0) { + duration = SEC(duration); + } else if (strncmp(units, "msec", 4) == 0) { + duration = MSEC(duration); + } else if (strncmp(units, "usec", 4) == 0) { + duration = USEC(duration); + } else if (strncmp(units, "nsec", 4) == 0) { + duration = NSEC(duration); + } else if (strncmp(units, "min", 3) == 0) { + duration = MINUTE(duration); + } else if (strncmp(units, "hour", 4) == 0) { + duration = HOUR(duration); + } else if (strncmp(units, "day", 3) == 0) { + duration = DAY(duration); + } else if (strncmp(units, "week", 4) == 0) { + duration = WEEK(duration); + } else { + // Invalid units. + return -1; + } + return duration; +} + /** * Process the command-line arguments. If the command line arguments are not * understood, then print a usage message and return 0. Otherwise, return 1. @@ -934,42 +980,30 @@ int process_args(int argc, const char* argv[]) { } const char* time_spec = argv[i++]; const char* units = argv[i++]; - -#if defined(PLATFORM_ARDUINO) - duration = atol(time_spec); -#else - duration = atoll(time_spec); -#endif - - // A parse error returns 0LL, so check to see whether that is what is meant. - if (duration == 0LL && strncmp(time_spec, "0", 1) != 0) { - // Parse error. - lf_print_error("Invalid time value: %s", time_spec); + duration = duration_string_to_interval(time_spec, units); + if (duration < 0) { + lf_print_error("Invalid time units for --timeout."); usage(argc, argv); return 0; } - if (strncmp(units, "sec", 3) == 0) { - duration = SEC(duration); - } else if (strncmp(units, "msec", 4) == 0) { - duration = MSEC(duration); - } else if (strncmp(units, "usec", 4) == 0) { - duration = USEC(duration); - } else if (strncmp(units, "nsec", 4) == 0) { - duration = NSEC(duration); - } else if (strncmp(units, "min", 3) == 0) { - duration = MINUTE(duration); - } else if (strncmp(units, "hour", 4) == 0) { - duration = HOUR(duration); - } else if (strncmp(units, "day", 3) == 0) { - duration = DAY(duration); - } else if (strncmp(units, "week", 4) == 0) { - duration = WEEK(duration); - } else { - // Invalid units. - lf_print_error("Invalid time units: %s", units); + } else if (strcmp(arg, "-s") == 0 || strcmp(arg, "--start-time") == 0) { + if (argc < i + 2) { + lf_print_error("--start-time needs time and units."); + usage(argc, argv); + return 0; + } + + const char* time_spec = argv[i++]; + const char* units = argv[i++]; + start_time = duration_string_to_interval(time_spec, units); + if (start_time < 0) { + lf_print_error("Invalid time units for --start-time."); usage(argc, argv); return 0; + } else { + start_time_specified = true; } + } else if (strcmp(arg, "-k") == 0 || strcmp(arg, "--keepalive") == 0) { if (argc < i + 1) { lf_print_error("--keepalive needs a boolean."); @@ -1227,3 +1261,15 @@ index_t lf_combine_deadline_and_level(interval_t deadline, int level) { else return (deadline << 16) | level; } + +void _lf_set_and_wait_for_start_time() { + if (!start_time_specified) { + start_time = lf_time_physical(); + } else { + instant_t now = lf_time_physical(); + if (!fast && now < start_time) { + lf_print("Sleeping " PRINTF_TIME " ns until specified start time", start_time - now); + lf_sleep(start_time - now); + } + } +} \ No newline at end of file diff --git a/core/threaded/reactor_threaded.c b/core/threaded/reactor_threaded.c index 82dd2b648..d61973549 100644 --- a/core/threaded/reactor_threaded.c +++ b/core/threaded/reactor_threaded.c @@ -1017,7 +1017,9 @@ int lf_reactor_c_main(int argc, const char* argv[]) { // Initialize the clock through the platform API. No reading of physical time before this. _lf_initialize_clock(); - start_time = lf_time_physical(); + // Set the start time of the program (if it is not set from the command line). Possibly wait for + // it to arrive also. + _lf_set_and_wait_for_start_time(); #ifndef FEDERATED lf_tracing_set_start_time(start_time); #endif diff --git a/include/core/reactor_common.h b/include/core/reactor_common.h index 8086ed7db..049672c22 100644 --- a/include/core/reactor_common.h +++ b/include/core/reactor_common.h @@ -53,6 +53,7 @@ extern unsigned int _lf_number_of_workers; extern int default_argc; extern const char** default_argv; extern instant_t duration; +extern bool start_time_specified; extern bool fast; extern bool keepalive_specified; @@ -65,6 +66,11 @@ extern struct allocation_record_t* _lf_reactors_to_free; ////////////////////// Functions ////////////////////// +/** + * @brief Set the start time of the program and possibly wait for it to arrive. + */ +void _lf_set_and_wait_for_start_time(); + /** * @brief Combine a deadline and a level into a single index for sorting in the reaction queue. *