diff --git a/.github/workflows/check-diff.yml b/.github/workflows/check-diff.yml deleted file mode 100644 index ecb22827d..000000000 --- a/.github/workflows/check-diff.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Check diff from main - -on: - workflow_call: - outputs: - run_arduino: - description: 'Return true if Arduino tests should be run' - value: ${{ jobs.check.outputs.run_arduino == 'true' }} - run_macos: - description: 'Return true if macOS tests should be run' - value: ${{ jobs.check.outputs.run_macos == 'true' }} - run_python: - description: 'Return true if Python tests should be run' - value: ${{ jobs.check.outputs.run_python == 'true' }} - run_windows: - description: 'Return true if Windows tests should be run' - value: ${{ jobs.check.outputs.run_windows == 'true' }} - run_zephyr: - description: 'Return true if Zephyr tests should be run' - value: ${{ jobs.check.outputs.run_zephyr == 'true' }} - -jobs: - check: - runs-on: ubuntu-latest - outputs: - run_arduino: ${{ steps.do.outputs.changed_arduino == 1 || github.ref == 'refs/heads/main' }} - run_macos: ${{ steps.do.outputs.changed_macos == 1 || github.ref == 'refs/heads/main' }} - run_python: ${{ steps.do.outputs.changed_python == 1 || github.ref == 'refs/heads/main' }} - run_windows: ${{ steps.do.outputs.changed_windows == 1 || github.ref == 'refs/heads/main' }} - run_zephyr: ${{ steps.do.outputs.changed_zephyr == 1 || github.ref == 'refs/heads/main' }} - - steps: - - name: Check out reactor-c repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - id: do - name: Check which targets have changes - run: | - ./check-diff.sh "arduino" arduino - ./check-diff.sh "macos|unix" macos - ./check-diff.sh "python" python - ./check-diff.sh "windows" windows - ./check-diff.sh "zephyr" zephyr - shell: bash - working-directory: .github/scripts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26795c446..fa8a5f752 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,11 @@ on: pull_request: # Also allow running this workflow manually from the Actions tab. workflow_dispatch: + merge_group: + +concurrency: + group: ci-${{ github.ref }}-${{ github.event_path }} + cancel-in-progress: ${{ github.ref != 'refs/heads/master' }} jobs: unit-tests-single: @@ -25,60 +30,57 @@ jobs: build-trace-tools: uses: ./.github/workflows/build-trace-tools.yml - check-diff: - uses: ./.github/workflows/check-diff.yml - fetch-lf: uses: lf-lang/lingua-franca/.github/workflows/extract-ref.yml@master with: file: 'lingua-franca-ref.txt' lf-default-arduino: - needs: [fetch-lf, check-diff] + needs: [fetch-lf] uses: lf-lang/lingua-franca/.github/workflows/c-arduino-tests.yml@master with: runtime-ref: ${{ github.ref }} compiler-ref: ${{ needs.fetch-lf.outputs.ref }} - if: ${{ needs.check-diff.outputs.run_arduino == 'true' }} + if: ${{ github.event_name == 'merge_group' || github.ref == 'refs/heads/main' || contains( github.event.pull_request.labels.*.name, 'arduino') }} lf-default-zephyr: - needs: [fetch-lf, check-diff] + needs: [fetch-lf] uses: lf-lang/lingua-franca/.github/workflows/c-zephyr-tests.yml@master with: runtime-ref: ${{ github.ref }} compiler-ref: ${{ needs.fetch-lf.outputs.ref }} - if: ${{ needs.check-diff.outputs.run_zephyr == 'true' }} + if: ${{ github.event_name == 'merge_group' || github.ref == 'refs/heads/main' ||contains( github.event.pull_request.labels.*.name, 'zephyr') }} lf-default: - needs: [fetch-lf, check-diff] + needs: [fetch-lf] uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master with: runtime-ref: ${{ github.ref }} compiler-ref: ${{ needs.fetch-lf.outputs.ref }} - all-platforms: ${{ needs.check-diff.outputs.run_macos == 'true' || needs.check-diff.outputs.run_windows == 'true' }} + all-platforms: ${{ github.event_name == 'merge_group' || github.ref == 'refs/heads/main' || contains( github.event.pull_request.labels.*.name, 'mac') || contains( github.event.pull_request.labels.*.name, 'windows') }} lf-python: - needs: [fetch-lf, check-diff] + needs: [fetch-lf] uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@master with: reactor-c-ref: ${{ github.ref }} compiler-ref: ${{ needs.fetch-lf.outputs.ref }} - if: ${{ needs.check-diff.outputs.run_python == 'true' }} + if: ${{ github.event_name == 'merge_group' || github.ref == 'refs/heads/main' ||contains( github.event.pull_request.labels.*.name, 'python') }} lf-gedf-np: - needs: [fetch-lf, check-diff] + needs: [fetch-lf] uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master with: runtime-ref: ${{ github.ref }} compiler-ref: ${{ needs.fetch-lf.outputs.ref }} scheduler: GEDF_NP - all-platforms: ${{ needs.check-diff.outputs.run_macos == 'true' || needs.check-diff.outputs.run_windows == 'true' }} + all-platforms: ${{ github.event_name == 'merge_group' || github.ref == 'refs/heads/main' || contains( github.event.pull_request.labels.*.name, 'mac') || contains( github.event.pull_request.labels.*.name, 'windows') }} lf-adaptive: - needs: [fetch-lf, check-diff] + needs: [fetch-lf] uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master with: runtime-ref: ${{ github.ref }} compiler-ref: ${{ needs.fetch-lf.outputs.ref }} scheduler: ADAPTIVE - all-platforms: ${{ needs.check-diff.outputs.run_macos == 'true' || needs.check-diff.outputs.run_windows == 'true' }} + all-platforms: ${{ github.event_name == 'merge_group' || github.ref == 'refs/heads/main' || contains( github.event.pull_request.labels.*.name, 'mac') || contains( github.event.pull_request.labels.*.name, 'windows') }} diff --git a/core/federated/RTI/rti_common.h b/core/federated/RTI/rti_common.h index a97c211b2..b55273e6e 100644 --- a/core/federated/RTI/rti_common.h +++ b/core/federated/RTI/rti_common.h @@ -182,9 +182,10 @@ void notify_advance_grant_if_safe(scheduling_node_t* e); void notify_provisional_tag_advance_grant(scheduling_node_t* e, tag_t tag); /** - * Determine whether the specified scheduling node is eligible for a tag advance grant, - * (TAG) and, if so, return the details. This is called upon receiving a LTC, NET - * or resign from an upstream node. + * @brief Determine whether the specified scheduling node is eligible for a tag advance grant, + * (TAG) and, if so, return the details. + * + * This is called upon receiving a LTC, NET or resign from an upstream node. * * This function calculates the minimum M over * all upstream scheduling nodes of the "after" delay plus the most recently diff --git a/core/federated/federate.c b/core/federated/federate.c index 17544aeb4..a5424c51f 100644 --- a/core/federated/federate.c +++ b/core/federated/federate.c @@ -2730,8 +2730,8 @@ int lf_send_tagged_message(environment_t* env, if (message_type == MSG_TYPE_P2P_TAGGED_MESSAGE) { lf_print_warning("Failed to send message to %s. Dropping the message.", next_destination_str); } else { - lf_print_error_system_failure("Failed to send message to %s. Connection lost to the RTI.", - next_destination_str); + lf_print_error_system_failure("Failed to send message to %s with error code %d (%s). Connection lost to the RTI.", + next_destination_str, errno, strerror(errno)); } } LF_MUTEX_UNLOCK(&lf_outbound_socket_mutex); diff --git a/core/reactor_common.c b/core/reactor_common.c index 8492ef48a..bd9cc1210 100644 --- a/core/reactor_common.c +++ b/core/reactor_common.c @@ -1325,3 +1325,8 @@ void termination(void) { } lf_tracing_global_shutdown(); } + +index_t lf_combine_deadline_and_level(interval_t deadline, int level) { + if (deadline > ULLONG_MAX >> 16) return ((ULLONG_MAX >> 16) << 16) | level; + else return (deadline << 16) | level; +} diff --git a/core/tag.c b/core/tag.c index 55937259c..3dbffec16 100644 --- a/core/tag.c +++ b/core/tag.c @@ -128,6 +128,10 @@ instant_t lf_time_start(void) { } size_t lf_readable_time(char* buffer, instant_t time) { + if (time <= (instant_t)0) { + snprintf(buffer, 2, "0"); + return 1; + } char* original_buffer = buffer; bool lead = false; // Set to true when first clause has been printed. if (time > WEEKS(1)) { @@ -147,8 +151,8 @@ size_t lf_readable_time(char* buffer, instant_t time) { size_t printed = lf_comma_separated_time(buffer, time / DAYS(1)); time = time % DAYS(1); buffer += printed; - snprintf(buffer, 6, " days"); - buffer += 5; + snprintf(buffer, 3, " d"); + buffer += 2; } if (time > HOURS(1)) { if (lead == true) { @@ -159,8 +163,8 @@ size_t lf_readable_time(char* buffer, instant_t time) { size_t printed = lf_comma_separated_time(buffer, time / HOURS(1)); time = time % HOURS(1); buffer += printed; - snprintf(buffer, 7, " hours"); - buffer += 6; + snprintf(buffer, 3, " h"); + buffer += 2; } if (time > MINUTES(1)) { if (lead == true) { @@ -171,8 +175,8 @@ size_t lf_readable_time(char* buffer, instant_t time) { size_t printed = lf_comma_separated_time(buffer, time / MINUTES(1)); time = time % MINUTES(1); buffer += printed; - snprintf(buffer, 9, " minutes"); - buffer += 8; + snprintf(buffer, 5, " min"); + buffer += 4; } if (time > SECONDS(1)) { if (lead == true) { @@ -183,28 +187,26 @@ size_t lf_readable_time(char* buffer, instant_t time) { size_t printed = lf_comma_separated_time(buffer, time / SECONDS(1)); time = time % SECONDS(1); buffer += printed; - snprintf(buffer, 9, " seconds"); - buffer += 8; + snprintf(buffer, 3, " s"); + buffer += 2; } if (time > (instant_t)0) { if (lead == true) { snprintf(buffer, 3, ", "); buffer += 2; } - const char* units = "nanoseconds"; + const char* units = "ns"; if (time % MSEC(1) == (instant_t) 0) { - units = "milliseconds"; + units = "ms"; time = time / MSEC(1); } else if (time % USEC(1) == (instant_t) 0) { - units = "microseconds"; + units = "us"; time = time / USEC(1); } size_t printed = lf_comma_separated_time(buffer, time); buffer += printed; - snprintf(buffer, 14, " %s", units); + snprintf(buffer, 3, " %s", units); buffer += strlen(units) + 1; - } else { - snprintf(buffer, 2, "0"); } return (buffer - original_buffer); } diff --git a/core/threaded/reactor_threaded.c b/core/threaded/reactor_threaded.c index 19ffee43c..f85baef33 100644 --- a/core/threaded/reactor_threaded.c +++ b/core/threaded/reactor_threaded.c @@ -632,6 +632,8 @@ void _lf_initialize_start_tag(environment_t *env) { LF_PRINT_LOG("Waiting for start time " PRINTF_TIME " plus STA " PRINTF_TIME ".", start_time, lf_fed_STA_offset); #else + // For other than federated decentralized execution, there is no lf_fed_STA_offset variable defined. + // To use uniform code below, we define it here as a local variable. instant_t lf_fed_STA_offset = 0; LF_PRINT_LOG("Waiting for start time " PRINTF_TIME ".", start_time); diff --git a/include/core/reactor_common.h b/include/core/reactor_common.h index 97ec976b8..92630be1e 100644 --- a/include/core/reactor_common.h +++ b/include/core/reactor_common.h @@ -65,6 +65,16 @@ extern struct allocation_record_t* _lf_reactors_to_free; ////////////////////// Functions ////////////////////// +/** + * @brief Combine a deadline and a level into a single index for sorting in the reaction queue. + * + * This shifts the deadline right by 16 bits and inserts the level in the low-order 16 bits. + * If the deadline is larger than `ULLONG_MAX >> 16`, then it is treated as the largest possible deadline. + * @oaran deadline THe deadline. + * @param level The level in the reaction graph. +*/ +index_t lf_combine_deadline_and_level(interval_t deadline, int level); + /** * @brief Create and initialize the required number of environments for the program. * @note This function will be code generated by the compiler. diff --git a/logging/api/logging_macros.h b/logging/api/logging_macros.h index 6ecc7e7ce..0864cdea7 100644 --- a/logging/api/logging_macros.h +++ b/logging/api/logging_macros.h @@ -78,19 +78,22 @@ #define LF_ASSERT(condition, format, ...) \ do { \ if (!(condition)) { \ - lf_print_error_and_exit(format, ##__VA_ARGS__); \ + lf_print_error_and_exit("`" format "`. Failed assertion in %s:%d(%s):(" #condition \ + ") != true`", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \ } \ } while(0) #define LF_ASSERTN(condition, format, ...) \ do { \ if (condition) { \ - lf_print_error_and_exit(format, ##__VA_ARGS__); \ + lf_print_error_and_exit("`" format "`. Failed assertion in %s:%d(%s):(" #condition \ + ") != false`", ##__VA_ARGS__, __FILE__, __LINE__, __func__); \ } \ } while(0) #define LF_ASSERT_NON_NULL(pointer) \ do { \ if (!(pointer)) { \ - lf_print_error_and_exit("Assertion failed: pointer is NULL Out of memory?."); \ + lf_print_error_and_exit("`Out of memory?` Assertion failed in %s:%d(%s):`" #pointer " == NULL`",\ + __FILE__, __LINE__, __func__); \ } \ } while(0) #endif // NDEBUG diff --git a/tag/api/tag.h b/tag/api/tag.h index 55d83c35d..4f2dbd031 100644 --- a/tag/api/tag.h +++ b/tag/api/tag.h @@ -199,21 +199,22 @@ instant_t lf_time_start(void); * Maximum number of nanoseconds is 999,999,999 * Maximum number of microsteps is 4,294,967,295 * Total number of characters for the above is 24. - * Text descriptions and spaces add an additional 55, - * for a total of 79. One more allows for a null terminator. + * Text descriptions and spaces add an additional 30, + * for a total of 54. One more allows for a null terminator. + * Round up to a power of two. */ -#define LF_TIME_BUFFER_LENGTH 80 +#define LF_TIME_BUFFER_LENGTH 64 /** * Store into the specified buffer a string giving a human-readable * rendition of the specified time. The buffer must have length at least * equal to LF_TIME_BUFFER_LENGTH. The format is: * ``` - * x weeks, x days, x hours, x minutes, x seconds, x unit + * x weeks, x d, x hr, x min, x s, x unit * ``` * where each `x` is a string of numbers with commas inserted if needed - * every three numbers and `unit` is nanoseconds, microseconds, or - * milliseconds. + * every three numbers and `unit` is ns, us, or + * ms. * @param buffer The buffer into which to write the string. * @param time The time to write. * @return The number of characters written (not counting the null terminator). diff --git a/util/tracing/visualization/fedsd.py b/util/tracing/visualization/fedsd.py index 253218684..0d7f57246 100644 --- a/util/tracing/visualization/fedsd.py +++ b/util/tracing/visualization/fedsd.py @@ -113,7 +113,7 @@ # Events matching at the sender and receiver ends depend on whether they are tagged # (the elapsed logical time and microstep have to be the same) or not. # Set of tagged events (messages) -non_tagged_messages = {'FED_ID', 'ACK', 'RESIGN', 'FAILED', 'REJECT', 'ADR_RQ', 'ADR_AD', 'MSG', 'P2P_MSG', 'STOP'} +non_tagged_messages = {'FED_ID', 'ACK', 'RESIGN', 'FAILED', 'REJECT', 'ADR_QR', 'ADR_AD', 'MSG', 'P2P_MSG', 'STOP'} ################################################################################ @@ -680,7 +680,7 @@ def get_and_convert_lft_files(rti_lft_file, federates_lft_files, start_time, end # FIXME: Using microseconds is hardwired here. physical_time = f'{int(row["physical_time"]/1000):,}' - if (row['event'] in {'FED_ID', 'ACK', 'FAILED', 'REJECT', 'ADR_RQ', 'ADR_AD', 'MSG', 'P2P_MSG', 'STOP'}): + if (row['event'] in {'FED_ID', 'ACK', 'FAILED', 'REJECT', 'ADR_QR', 'ADR_AD', 'MSG', 'P2P_MSG', 'STOP'}): label = row['event'] else: label = row['event'] + '(' + f'{int(row["logical_time"]):,}' + ', ' + str(row['microstep']) + ')'