├── lingua-franca-ref.txt ├── util ├── tracing │ ├── visualization │ │ ├── .gitignore │ │ └── README.md │ ├── codegen │ │ ├── CMakeLists.txt │ │ └── src │ │ │ └── tracepoint_to_rs.c │ ├── README.md │ └── Makefile ├── deque.cmake ├── wave_file_reader.cmake ├── sensor_simulator.cmake ├── README.md ├── audio_loop.cmake ├── type_converter.h ├── wave_file_reader.h ├── deque.h └── generics.h ├── docs ├── requirements.txt ├── logo.png ├── favicon.ico ├── favicon.png ├── index.rst ├── make.bat ├── markdown │ ├── 2_introduction.md │ ├── 4_license.md │ └── 1_landing_page.md ├── assets │ ├── doxygen-awesome-sidebar-only-darkmode-toggle.css │ ├── doxygen-awesome-paragraph-link.js │ ├── doxygen-awesome-interactive-toc.js │ ├── doxygen-awesome-sidebar-only.css │ └── doxygen-awesome-tabs.js └── doxygen.h ├── core ├── federated │ ├── RTI │ │ ├── .gitignore │ │ ├── local_rti.cmake │ │ ├── rti.Dockerfile │ │ └── README.md │ ├── CMakeLists.txt │ └── network │ │ └── CMakeLists.txt ├── utils │ ├── vector.cmake │ ├── hashset │ │ ├── CMakeLists.txt │ │ ├── Makefile │ │ └── hashset_itr.c │ ├── CMakeLists.txt │ ├── pqueue.c │ └── lf_semaphore.c ├── modal_models │ └── CMakeLists.txt ├── threaded │ ├── CMakeLists.txt │ ├── scheduler_instance.c │ └── scheduler_sync_tag_advance.c ├── lf_utils.cmake ├── mixed_radix.c ├── clock.c └── port.c ├── trace ├── impl │ ├── build.sh │ ├── CMakeLists.txt │ └── include │ │ └── trace_impl.h └── api │ ├── types │ ├── CMakeLists.txt │ └── trace_types.h │ └── CMakeLists.txt ├── tag └── api │ └── CMakeLists.txt ├── logging └── api │ └── CMakeLists.txt ├── version └── api │ ├── CMakeLists.txt │ └── lf_core_version.h ├── platform ├── api │ ├── CMakeLists.txt │ └── platform.h └── impl │ ├── platform.c │ └── CMakeLists.txt ├── .github ├── scripts │ ├── build-rti.sh │ └── check-diff.sh └── workflows │ ├── build-trace-tools.yml │ ├── clang-format.yml │ ├── api-docs.yml │ ├── unit-tests.yml │ ├── build-rti.yml │ └── ci.yml ├── test ├── general │ ├── tag_test.c │ └── utils │ │ ├── hashmap_test.c │ │ ├── vector_test.c │ │ └── pqueue_test.c ├── rand_utils.h ├── rand_utils.c ├── src_gen_stub.c ├── Tests.cmake └── scheduling │ └── scheduling_api_test.c ├── lib └── CMakeLists.txt ├── .clang-format ├── low_level_platform ├── README.md ├── api │ ├── platform │ │ ├── lf_macos_support.h │ │ ├── lf_POSIX_threads_support.h │ │ ├── lf_platform_util.h │ │ ├── arduino_mbed │ │ │ ├── MutexWrapper.h │ │ │ ├── ThreadWrapper.h │ │ │ └── ConditionWrapper.h │ │ ├── lf_patmos_support.h │ │ ├── lf_linux_support.h │ │ ├── lf_nrf52_support.h │ │ ├── lf_unix_clock_support.h │ │ ├── lf_zephyr_support.h │ │ ├── lf_rp2040_support.h │ │ ├── lf_windows_support.h │ │ ├── lf_tag_64_32.h │ │ ├── lf_zephyr_board_support.h │ │ ├── lf_flexpret_support.h │ │ ├── lf_arduino_support.h │ │ └── lf_atomic.h │ └── CMakeLists.txt └── impl │ └── src │ ├── arduino_mbed │ ├── MutexWrapper.cpp │ ├── ThreadWrapper.cpp │ └── ConditionWrapper.cpp │ ├── lf_platform_util.c │ ├── lf_os_single_threaded_support.c │ ├── lf_unix_clock_support.c │ ├── lf_atomic_gcc_clang.c │ ├── lf_atomic_windows.c │ ├── lf_macos_support.c │ ├── lf_atomic_irq.c │ ├── lf_POSIX_threads_support.c │ ├── lf_patmos_support.c │ ├── lf_zephyr_clock_kernel.c │ └── lf_linux_support.c ├── .gitignore ├── python ├── include │ ├── python_time.h │ ├── python_tag.h │ ├── modal_models │ │ └── definitions.h │ ├── python_action.h │ ├── python_port.h │ └── python_capsule_extension.h └── lib │ └── python_time.c ├── CONTRIBUTING.md ├── include ├── api │ └── reaction_macros_undef.h └── core │ ├── utils │ ├── impl │ │ └── pointer_hashmap.h │ ├── pqueue.h │ ├── lf_semaphore.h │ └── hashset │ │ └── hashset_itr.h │ ├── threaded │ ├── scheduler_sync_tag_advance.h │ └── scheduler.h │ └── clock.h ├── DOCUMENTATION.md ├── Makefile ├── README.md ├── LICENSE.md └── CMakeLists.txt /lingua-franca-ref.txt: -------------------------------------------------------------------------------- 1 | master 2 | -------------------------------------------------------------------------------- /util/tracing/visualization/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | breathe 3 | exhale 4 | sphinx-rtd-theme -------------------------------------------------------------------------------- /util/deque.cmake: -------------------------------------------------------------------------------- 1 | target_sources(${LF_MAIN_TARGET} PRIVATE deque.c) 2 | -------------------------------------------------------------------------------- /core/federated/RTI/.gitignore: -------------------------------------------------------------------------------- 1 | CMakeFiles 2 | Makefile 3 | cmake_install.cmake -------------------------------------------------------------------------------- /core/utils/vector.cmake: -------------------------------------------------------------------------------- 1 | target_sources(${LF_MAIN_TARGET} PRIVATE vector.c) 2 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lf-lang/reactor-c/HEAD/docs/logo.png -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lf-lang/reactor-c/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lf-lang/reactor-c/HEAD/docs/favicon.png -------------------------------------------------------------------------------- /util/wave_file_reader.cmake: -------------------------------------------------------------------------------- 1 | target_sources(${LF_MAIN_TARGET} PRIVATE wave_file_reader.c) 2 | -------------------------------------------------------------------------------- /trace/impl/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cmake -S . -B build -DLOG_LEVEL=4 4 | cmake --build build 5 | -------------------------------------------------------------------------------- /core/modal_models/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MODAL_SOURCES modes.c) 2 | 3 | list(TRANSFORM MODAL_SOURCES PREPEND modal_models/) 4 | list(APPEND REACTORC_SOURCES ${MODAL_SOURCES}) 5 | -------------------------------------------------------------------------------- /tag/api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lf-tag-api INTERFACE) 2 | target_include_directories(lf-tag-api INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 3 | add_library(lf::tag-api ALIAS lf-tag-api) 4 | -------------------------------------------------------------------------------- /core/federated/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(FEDERATED_SOURCES clock-sync.c federate.c) 2 | 3 | list(TRANSFORM FEDERATED_SOURCES PREPEND federated/) 4 | list(APPEND REACTORC_SOURCES ${FEDERATED_SOURCES}) 5 | -------------------------------------------------------------------------------- /core/utils/hashset/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HASHSET_SOURCES hashset.c hashset_itr.c) 2 | 3 | 4 | list(TRANSFORM HASHSET_SOURCES PREPEND utils/hashset/) 5 | list(APPEND REACTORC_SOURCES ${HASHSET_SOURCES}) 6 | -------------------------------------------------------------------------------- /logging/api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lf-logging-api INTERFACE) 2 | target_include_directories(lf-logging-api INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 3 | add_library(lf::logging-api ALIAS lf-logging-api) 4 | -------------------------------------------------------------------------------- /version/api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lf-version-api INTERFACE) 2 | target_include_directories(lf-version-api INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 3 | add_library(lf::version-api ALIAS lf-version-api) 4 | -------------------------------------------------------------------------------- /core/federated/network/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LF_NETWORK_FILES net_util.c socket_common.c) 2 | 3 | list(TRANSFORM LF_NETWORK_FILES PREPEND federated/network/) 4 | list(APPEND REACTORC_SOURCES ${LF_NETWORK_FILES}) 5 | -------------------------------------------------------------------------------- /platform/api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lf-platform-api INTERFACE) 2 | add_library(lf::platform-api ALIAS lf-platform-api) 3 | target_include_directories(lf-platform-api INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 4 | -------------------------------------------------------------------------------- /trace/api/types/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lf-trace-api-types INTERFACE) 2 | add_library(lf::trace-api-types ALIAS lf-trace-api-types) 3 | target_include_directories(lf-trace-api-types INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 4 | -------------------------------------------------------------------------------- /.github/scripts/build-rti.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pushd core/federated/RTI 3 | mkdir build 4 | cd build 5 | if [[ "$OSTYPE" == "darwin"* ]]; then 6 | export OPENSSL_ROOT_DIR="/usr/local/opt/openssl" 7 | fi 8 | cmake "$@" ../ 9 | make 10 | popd 11 | -------------------------------------------------------------------------------- /test/general/tag_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "lf_types.h" 4 | 5 | int main() { 6 | char* buf = malloc(sizeof(char) * 128); 7 | lf_readable_time(buf, 0); 8 | printf("%s", buf); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. C Reactor Docs documentation master file, created by 2 | sphinx-quickstart on Sun Oct 3 09:09:57 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | .. toctree:: 7 | 8 | api/library_root 9 | api -------------------------------------------------------------------------------- /.github/scripts/check-diff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | changes() { 4 | git diff --name-only HEAD $(git merge-base HEAD origin/main) 5 | } 6 | 7 | if changes | grep "$1" | grep -q -v "^.*md\|txt$"; then 8 | echo "changed_$2=1" >> $GITHUB_OUTPUT 9 | else 10 | echo "changed_$2=0" >> $GITHUB_OUTPUT 11 | fi 12 | -------------------------------------------------------------------------------- /trace/api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lf-trace-api INTERFACE) 2 | add_library(lf::trace-api ALIAS lf-trace-api) 3 | include(${CMAKE_CURRENT_LIST_DIR}/types/CMakeLists.txt) 4 | target_link_libraries(lf-trace-api INTERFACE lf::trace-api-types) 5 | target_include_directories(lf-trace-api INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 6 | -------------------------------------------------------------------------------- /util/sensor_simulator.cmake: -------------------------------------------------------------------------------- 1 | target_sources(${LF_MAIN_TARGET} PRIVATE sensor_simulator.c) 2 | find_package(Curses REQUIRED) # Finds the lncurses library 3 | include_directories(${CURSES_INCLUDE_DIR}) # "The include directories needed to use Curses" 4 | target_link_libraries(${LF_MAIN_TARGET} PRIVATE ${CURSES_LIBRARIES}) # Links the Curses library 5 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LF_ROOT ${CMAKE_CURRENT_LIST_DIR}/..) 2 | include(${LF_ROOT}/core/lf_utils.cmake) 3 | 4 | add_library(lib schedule.c) 5 | target_link_libraries(lib PRIVATE lf::low-level-platform-api) 6 | target_link_libraries(lib PRIVATE lf::logging-api) 7 | target_link_libraries(lib PUBLIC lf::trace-api-types) 8 | 9 | lf_enable_compiler_warnings(lib) -------------------------------------------------------------------------------- /core/threaded/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set( 2 | THREADED_SOURCES 3 | reactor_threaded.c 4 | scheduler_adaptive.c 5 | scheduler_GEDF_NP.c 6 | scheduler_NP.c 7 | scheduler_sync_tag_advance.c 8 | scheduler_instance.c 9 | watchdog.c 10 | ) 11 | 12 | list(TRANSFORM THREADED_SOURCES PREPEND threaded/) 13 | list(APPEND REACTORC_SOURCES ${THREADED_SOURCES}) 14 | 15 | -------------------------------------------------------------------------------- /core/federated/RTI/local_rti.cmake: -------------------------------------------------------------------------------- 1 | # This adds the local RTI sources required for scheduling enclaves 2 | # to the build. 3 | set( 4 | LOCAL_RTI_SOURCES 5 | rti_common.c 6 | rti_local.c 7 | ) 8 | 9 | list(APPEND INFO_SOURCES ${LOCAL_RTI_SOURCES}) 10 | 11 | list(TRANSFORM LOCAL_RTI_SOURCES PREPEND federated/RTI/) 12 | list(APPEND REACTORC_SOURCES ${LOCAL_RTI_SOURCES}) 13 | 14 | -------------------------------------------------------------------------------- /core/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(UTIL_SOURCES vector.c pqueue_base.c pqueue_tag.c pqueue.c util.c) 2 | 3 | if(NOT DEFINED LF_SINGLE_THREADED) 4 | list(APPEND UTIL_SOURCES lf_semaphore.c) 5 | endif() 6 | 7 | list(TRANSFORM UTIL_SOURCES PREPEND utils/) 8 | list(APPEND REACTORC_SOURCES ${UTIL_SOURCES}) 9 | 10 | # Include sources from subdirectories 11 | include(utils/hashset/CMakeLists.txt) 12 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | Language: C 4 | IndentWidth: 2 5 | BreakConstructorInitializersBeforeComma: 'true' 6 | PointerAlignment: Left 7 | IncludeBlocks: Preserve 8 | SortIncludes: false 9 | ColumnLimit: 120 10 | --- 11 | BasedOnStyle: LLVM 12 | Language: Cpp 13 | IndentWidth: 2 14 | BreakConstructorInitializersBeforeComma: 'true' 15 | PointerAlignment: Left 16 | IncludeBlocks: Preserve 17 | SortIncludes: false 18 | ColumnLimit: 120 19 | -------------------------------------------------------------------------------- /low_level_platform/README.md: -------------------------------------------------------------------------------- 1 | This sub-project defines the platform abstraction used by reactor-c. 2 | 3 | It exposes an interface that does include compile-time constructs such as 4 | typedefs and preprocessor definitions. Use the `platform` subproject if the 5 | simplified interface that appears there is sufficient. 6 | 7 | Strongly prefer to depend on the `platform` subproject if the module you are 8 | building needs to be compiled using a separate toolchain. 9 | -------------------------------------------------------------------------------- /test/rand_utils.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** 4 | * @brief Ensures that the expectation of each entry of `out` is equal 5 | * to the corresponding entry of `src`. Assumes that a random seed has 6 | * already been set using `srand`. 7 | * 8 | * @param src An array of integers of size `size`. 9 | * @param size The size of both `src` and `out`. 10 | * @param out An array of integers of size `size`. 11 | */ 12 | void perturb(int* src, size_t size, int* out); 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /docs/_build 3 | /docs/api 4 | **/.vscode/ 5 | **/build/ 6 | **/.DS_Store 7 | /core/federated/RTI/build/ 8 | /cmake-build-debug/ 9 | 10 | # Generated trace files 11 | *.lft 12 | util/tracing/trace_to_chrome 13 | util/tracing/trace_to_chrome.o 14 | util/tracing/trace_to_csv 15 | util/tracing/trace_to_csv.o 16 | util/tracing/trace_to_influxdb 17 | util/tracing/trace_to_influxdb.o 18 | util/tracing/trace_util.o 19 | 20 | # Generated trace lib 21 | trace/**/*.a 22 | -------------------------------------------------------------------------------- /.github/workflows/build-trace-tools.yml: -------------------------------------------------------------------------------- 1 | name: Build the tracing tools 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | run: 8 | strategy: 9 | matrix: 10 | platform: [ubuntu-24.04, macos-latest, windows-latest] 11 | runs-on: ${{ matrix.platform }} 12 | 13 | steps: 14 | - name: Check out reactor-c repository 15 | uses: actions/checkout@v3 16 | - name: Building tracing utils 17 | working-directory: ./util/tracing 18 | run: make 19 | shell: bash 20 | -------------------------------------------------------------------------------- /util/README.md: -------------------------------------------------------------------------------- 1 | ## /lib/C/util/ 2 | 3 | This directory contains source files for use by Lingua Franca programs using the 4 | C target. To use a file in this directory, specify a target property as follows: 5 | ``` 6 | target C { 7 | files: ["/lib/c/reactor-c/util/filename", ...], 8 | ... 9 | }; 10 | ``` 11 | This causes the `lfc` or LF IDE to copy the file into your project's `src-gen` 12 | directory. Hence, you can include the file in your C code as follows: 13 | ``` 14 | preamble {= 15 | #include "filename" 16 | =} 17 | ``` 18 | -------------------------------------------------------------------------------- /util/tracing/codegen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(TracepointToRs LANGUAGES C) 3 | add_executable(tracepoint-to-rs ${CMAKE_CURRENT_LIST_DIR}/src/tracepoint_to_rs.c) 4 | set(LF_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../..) 5 | include(${LF_ROOT}/trace/api/CMakeLists.txt) 6 | include(${LF_ROOT}/version/api/CMakeLists.txt) 7 | target_link_libraries(tracepoint-to-rs PUBLIC lf::trace-api) 8 | target_link_libraries(tracepoint-to-rs PUBLIC lf::version-api) 9 | target_link_libraries(tracepoint-to-rs PUBLIC lf::trace-api-types) 10 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_macos_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * @author Soroush Bateni 5 | * 6 | * @brief MacOS API support for the C target of Lingua Franca. 7 | */ 8 | 9 | #ifndef LF_MACOS_SUPPORT_H 10 | #define LF_MACOS_SUPPORT_H 11 | 12 | #include // For fixed-width integral types 13 | 14 | // Use 64-bit times and 32-bit unsigned microsteps 15 | #include "lf_tag_64_32.h" 16 | 17 | #if !defined LF_SINGLE_THREADED 18 | #include "lf_POSIX_threads_support.h" 19 | #endif 20 | 21 | #endif // LF_MACOS_SUPPORT_H 22 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_POSIX_threads_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_POSIX_threads_support.h 3 | * @brief POSIX API support for the C target of Lingua Franca. 4 | * @author{Soroush Bateni } 5 | * 6 | * All functions return 0 on success. 7 | */ 8 | 9 | #ifndef LF_POSIX_THREADS_SUPPORT_H 10 | #define LF_POSIX_THREADS_SUPPORT_H 11 | 12 | #include 13 | 14 | typedef pthread_mutex_t lf_mutex_t; 15 | typedef struct { 16 | lf_mutex_t* mutex; 17 | pthread_cond_t condition; 18 | } lf_cond_t; 19 | typedef pthread_t lf_thread_t; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_platform_util.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_platform_util.h 3 | * @brief Utility functions for platform-specific operations in the Lingua Franca C runtime. 4 | * 5 | * This header file provides utility functions that are used across different 6 | * platform implementations to handle common platform-specific operations. 7 | */ 8 | 9 | #ifndef LF_PLATFORM_UTIL_H 10 | #define LF_PLATFORM_UTIL_H 11 | 12 | /** 13 | * @brief Maps a priority into a destination priority range. 14 | */ 15 | int map_priorities(int priority, int dest_min, int dest_max); 16 | 17 | #endif -------------------------------------------------------------------------------- /.github/workflows/clang-format.yml: -------------------------------------------------------------------------------- 1 | name: clang-format-review 2 | 3 | # You can be more specific, but it currently only works on pull requests 4 | on: [pull_request] 5 | 6 | jobs: 7 | clang-format: 8 | runs-on: ubuntu-24.04 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Install clang-tidy and clang-format 12 | run: | 13 | sudo apt-get update 14 | sudo apt-get install -y clang-tidy 15 | sudo apt-get install -y pipx 16 | pipx install clang-format 17 | clang-format --version 18 | - name: Analyze 19 | run: make format-check 20 | -------------------------------------------------------------------------------- /core/lf_utils.cmake: -------------------------------------------------------------------------------- 1 | function(lf_enable_compiler_warnings target) 2 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 3 | target_compile_options(${target} PRIVATE -Wall -Wextra -Wpedantic -Werror) 4 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") 5 | if(${CMAKE_C_COMPILER} MATCHES "gcc\\.exe") 6 | target_compile_options(${target} PRIVATE -Wall) 7 | else() 8 | target_compile_options(${target} PRIVATE /W4) 9 | endif() 10 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") 11 | target_compile_options(${target} PRIVATE -Wall) 12 | else() 13 | target_compile_options(${target} PRIVATE -Wall) 14 | endif() 15 | endfunction() -------------------------------------------------------------------------------- /python/include/python_time.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * @author Soroush Bateni 5 | * @author Hou Seng Wong 6 | * 7 | * @brief Implementation of functions defined in @see pythontarget.h 8 | */ 9 | PyObject* py_lf_time_logical(PyObject* self, PyObject* args); 10 | PyObject* py_lf_time_logical_elapsed(PyObject* self, PyObject* args); 11 | PyObject* py_lf_time_physical(PyObject* self, PyObject* args); 12 | PyObject* py_lf_time_physical_elapsed(PyObject* self, PyObject* args); 13 | PyObject* py_lf_time_start(PyObject* self, PyObject* args); 14 | 15 | extern PyTypeObject PyTimeType; 16 | 17 | extern PyMethodDef PyTimeTypeMethods[]; 18 | extern PyTypeObject PyTimeType; 19 | -------------------------------------------------------------------------------- /core/federated/RTI/rti.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASEIMAGE=alpine:latest 2 | FROM ${BASEIMAGE} AS builder 3 | COPY . /lingua-franca 4 | WORKDIR /lingua-franca/core/federated/RTI 5 | RUN set -ex && apk add --no-cache gcc musl-dev cmake make git && \ 6 | mkdir container && \ 7 | cd container && \ 8 | cmake ../ && \ 9 | make && \ 10 | make install 11 | 12 | WORKDIR /lingua-franca 13 | 14 | # application stage 15 | FROM ${BASEIMAGE} AS app 16 | LABEL maintainer="lf-lang" 17 | LABEL source="https://github.com/lf-lang/reactor-c/tree/main/core/federated/RTI" 18 | COPY --from=builder /usr/local/bin/RTI /usr/local/bin/RTI 19 | 20 | WORKDIR /lingua-franca 21 | 22 | ENTRYPOINT ["/usr/local/bin/RTI"] 23 | -------------------------------------------------------------------------------- /.github/workflows/api-docs.yml: -------------------------------------------------------------------------------- 1 | name: API docs 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | jobs: 8 | build: 9 | runs-on: macos-latest 10 | steps: 11 | - name: Requirements 12 | run: | 13 | brew install doxygen 14 | - name: Checkout repo 15 | uses: actions/checkout@v4 16 | - name: Build docs 17 | run: make docs 18 | && cd docs/_build/html 19 | && touch .nojekyll 20 | - name: Deploy 21 | uses: JamesIves/github-pages-deploy-action@releases/v3 22 | with: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | BRANCH: gh-pages # The branch the action should deploy to. 25 | FOLDER: docs/_build/html # The folder the action should deploy. 26 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | > [!IMPORTANT] 4 | > This document contains guidelines specific to this repository. Please also read the general [contributing guidelines](https://github.com/lf-lang/.github/blob/main/CONTRIBUTING.md) of the lf-lang organization. 5 | 6 | This repository hosts the source code for the C runtime system for Lingua Franca, which is the runtime system you get when you specify C or CCpp as the target. 7 | It is also used by the Python target. 8 | The C runtime system is tightly integrated with the Lingua Franca code generator, which is in the [lingua-franca](https://github.com/lf-lang/lingua-franca) repository. 9 | 10 | For more details, see [contributing suggestions](https://lf-lang.org/reactor-c/contributing.html). 11 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/arduino_mbed/MutexWrapper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file MutexWrapper.h 3 | * @brief Mutex support for RTOS-enabled Arduino boards using MBED. 4 | * 5 | * @author Anirudh Rengarajan 6 | * 7 | * This header file provides a C wrapper around the MBED RTOS mutex API 8 | * for use with Arduino boards that support MBED. It provides mutex creation, 9 | * locking, and unlocking functionality. 10 | */ 11 | 12 | #ifndef MUTEXWRAPPER_H 13 | #define MUTEXWRAPPER_H 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | void* mutex_new(); 20 | void mutex_delete(); 21 | void mutex_lock(); 22 | bool mutex_trylock(); 23 | void mutex_unlock(); 24 | void* mutex_get_owner(); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | #endif -------------------------------------------------------------------------------- /test/rand_utils.c: -------------------------------------------------------------------------------- 1 | #include "rand_utils.h" 2 | #include 3 | 4 | /** 5 | * @brief Ensures that the expectation of each entry of `out` is equal 6 | * to the corresponding entry of `src`. Assumes that a random seed has 7 | * already been set using `srand`. 8 | * 9 | * @param src An array of integers of size `size`. 10 | * @param size The size of both `src` and `out`. 11 | * @param out An array of integers of size `size`. 12 | */ 13 | void perturb(int* src, size_t size, int* out) { 14 | out[size - 1] = src[size - 1]; 15 | for (size_t a = 0; a < size - 1; a += 2) { 16 | int min = src[a] < src[a + 1] ? src[a] : src[a + 1]; 17 | int diff = rand() % (min * 2) - min; 18 | out[a] = src[a] + diff; 19 | out[a + 1] = src[a + 1] - diff; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_patmos_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Patmos API support for the C target of Lingua Franca. 4 | * 5 | * This is based on lf_nrf_support.h in icyphy/lf-buckler. 6 | * 7 | * @author Ehsan Khodadad 8 | * @author Luca Pezzarossa 9 | * @author Martin Schoeberl 10 | */ 11 | 12 | #ifndef LF_PATMOS_SUPPORT_H 13 | #define LF_PATMOS_SUPPORT_H 14 | 15 | // This embedded platform has no TTY suport 16 | #define NO_TTY 17 | 18 | #include // For fixed-width integral types 19 | #include 20 | 21 | #include // Needed to define PRId64 and PRIu32 22 | #define PRINTF_TIME "%" PRId64 23 | #define PRINTF_MICROSTEP "%" PRIu32 24 | #define PRINTF_TAG "(%" PRId64 ", %" PRIu32 ")" 25 | 26 | #endif // LF_PATMOS_SUPPORT_H 27 | -------------------------------------------------------------------------------- /util/audio_loop.cmake: -------------------------------------------------------------------------------- 1 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 2 | target_sources(${LF_MAIN_TARGET} PRIVATE audio_loop_linux.c) 3 | find_package(ALSA REQUIRED) 4 | if (ALSA_FOUND) 5 | include_directories(${ALSA_INCLUDE_DIRS}) 6 | target_link_libraries(${LF_MAIN_TARGET} PRIVATE ${ALSA_LIBRARIES}) 7 | endif(ALSA_FOUND) 8 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") 9 | target_sources(${LF_MAIN_TARGET} PRIVATE audio_loop_mac.c) 10 | target_link_libraries(${LF_MAIN_TARGET} PRIVATE "-framework AudioToolbox") 11 | target_link_libraries(${LF_MAIN_TARGET} PRIVATE "-framework CoreFoundation") 12 | else() 13 | message(FATAL_ERROR "Your platform is not supported!" 14 | " Sound is only currently supported on Linux and MacOS.") 15 | endif() 16 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_linux_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * @author Soroush Bateni 5 | * 6 | * @brief Linux API support for the C target of Lingua Franca. 7 | */ 8 | 9 | #ifndef LF_LINUX_SUPPORT_H 10 | #define LF_LINUX_SUPPORT_H 11 | 12 | #include // For fixed-width integral types 13 | #include // For CLOCK_MONOTONIC 14 | #include // _POSIX_TIMERS _POSIX_CLOCK_MONOTONIC 15 | 16 | // Use 64-bit times and 32-bit unsigned microsteps 17 | #include "lf_tag_64_32.h" 18 | 19 | #if !defined LF_SINGLE_THREADED 20 | #include "lf_POSIX_threads_support.h" 21 | #endif 22 | 23 | #if !defined(_POSIX_TIMERS) || _POSIX_TIMERS <= 0 24 | #error Linux platform misses clock support 25 | #endif 26 | 27 | #endif // LF_LINUX_SUPPORT_H 28 | -------------------------------------------------------------------------------- /python/include/python_tag.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Hou Seng Wong 4 | * 5 | * @brief Functions for tag operations in Python. 6 | */ 7 | 8 | #ifndef PYTHON_TAG_H 9 | #define PYTHON_TAG_H 10 | #include 11 | #include 12 | #include "tag.h" 13 | 14 | extern PyTypeObject PyTagType; 15 | 16 | /** 17 | * Python wrapper for the tag_t struct in the C target. 18 | **/ 19 | typedef struct { 20 | PyObject_HEAD tag_t tag; 21 | } py_tag_t; 22 | 23 | /** 24 | * @brief Convert C tag to `py_tag_t` 25 | * 26 | * @param c_tag The tag in C. 27 | * @return py_tag_t* The tag in Python. 28 | */ 29 | py_tag_t* convert_C_tag_to_py(tag_t c_tag); 30 | 31 | PyObject* py_lf_tag(PyObject* self, PyObject* args); 32 | PyObject* py_tag_compare(PyObject* self, PyObject* args); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /include/api/reaction_macros_undef.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Hou Seng Wong 4 | * 5 | * @brief Undefine macros defined in api/reaction_macros.h. 6 | * 7 | * This file is included at the end of each reaction body to undefine the macros used in reaction bodies. 8 | */ 9 | 10 | // Prevent inclusion if reaction_macros.h has not been included. 11 | #ifdef REACTION_MACROS_H 12 | // Allow subsequent inclusion of reaction_macros.h. 13 | #undef REACTION_MACROS_H 14 | 15 | #undef lf_set 16 | #undef lf_set_token 17 | #undef lf_set_destructor 18 | #undef lf_set_copy_constructor 19 | 20 | #ifdef MODAL_REACTORS 21 | #undef lf_set_mode 22 | #endif 23 | 24 | #undef lf_tag 25 | #undef lf_time_logical 26 | #undef lf_time_logical_elapsed 27 | 28 | #undef lf_reactor_name 29 | #undef lf_reactor_full_name 30 | 31 | #endif // REACTION_MACROS_H 32 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/arduino_mbed/ThreadWrapper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ThreadWrapper.h 3 | * @brief Threading support for RTOS-enabled Arduino boards using MBED. 4 | * 5 | * @author Anirudh Rengarajan 6 | * 7 | * This header file provides a C wrapper around the MBED RTOS threading API 8 | * for use with Arduino boards that support MBED. It provides thread creation, 9 | * management, and synchronization primitives. 10 | */ 11 | 12 | #ifndef THREADWRAPPER_H 13 | #define THREADWRAPPER_H 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | void* thread_new(); 20 | void thread_delete(void* thread); 21 | int thread_start(void* thread, void* (*function)(void*), void* arguments); 22 | int thread_join(void* thread, int* thread_return); 23 | int thread_terminate(void* thread); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | #endif -------------------------------------------------------------------------------- /test/src_gen_stub.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tag.h" 3 | #include "environment.h" 4 | 5 | /** 6 | * This file enables unit tests to run without there having been an actual code generation 7 | * from a Lingua Franca program. It defines (mostly empty) functions that would normally be 8 | * code generated. Of course, this strategy will only work for tests that do not actually 9 | * need functional versions of these functions. 10 | */ 11 | 12 | environment_t _env; 13 | 14 | void _lf_initialize_trigger_objects(void) {} 15 | void lf_terminate_execution(void) {} 16 | void lf_set_default_command_line_options(void) {} 17 | void _lf_initialize_watchdogs(environment_t** envs) { (void)envs; } 18 | void logical_tag_complete(tag_t tag_to_send) { (void)tag_to_send; } 19 | int _lf_get_environments(environment_t** envs) { 20 | *envs = &_env; 21 | return 1; 22 | } 23 | -------------------------------------------------------------------------------- /version/api/lf_core_version.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_core_version.h 3 | * @brief Structs for the API for runtime plugins to use to sanity-check compatibility with the core. 4 | * 5 | * @author Peter Donovan 6 | * 7 | * Plugins APIs can include a function to get information about the 8 | * version of the plugin, and the core can use that information to determine if 9 | * the plugin is compatible with the core. 10 | */ 11 | #ifndef VERSION_H 12 | #define VERSION_H 13 | 14 | typedef enum { 15 | TRIBOOL_FALSE = 0, 16 | TRIBOOL_TRUE = 1, 17 | TRIBOOL_DOES_NOT_MATTER = 2, 18 | } tribool_t; 19 | 20 | typedef struct { 21 | tribool_t single_threaded; 22 | tribool_t build_type_is_debug; 23 | int log_level; 24 | } build_config_t; 25 | 26 | typedef struct { 27 | const build_config_t build_config; 28 | const char* core_version_name; 29 | } version_t; 30 | 31 | #endif // VERSION_H 32 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/arduino_mbed/ConditionWrapper.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ConditionWrapper.h 3 | * @brief Condition variable support for RTOS-enabled Arduino boards using MBED. 4 | * 5 | * @author Anirudh Rengarajan 6 | * 7 | * This header file provides a C wrapper around the MBED RTOS condition variable API 8 | * for use with Arduino boards that support MBED. It provides condition variable 9 | * creation, waiting, and notification functionality. 10 | */ 11 | #ifndef CONDITIONWRAPPER_H 12 | #define CONDITIONWRAPPER_H 13 | 14 | struct condition; 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | void* condition_new(void*); 21 | void condition_delete(void*); 22 | int condition_wait_for(void*, uint64_t); 23 | int condition_wait(void*); 24 | void condition_notify_one(void*); 25 | void condition_notify_all(void*); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | #endif -------------------------------------------------------------------------------- /platform/impl/platform.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file platform.c 3 | * @author Peter Donovan 4 | * 5 | * @brief A variant of the platform abstraction whose ABI is platform-independent. 6 | */ 7 | #include 8 | 9 | #include "low_level_platform.h" 10 | #include "platform.h" 11 | 12 | // MUTEXES ********************************************************************* 13 | 14 | lf_platform_mutex_ptr_t lf_platform_mutex_new() { 15 | lf_platform_mutex_ptr_t mutex = (lf_platform_mutex_ptr_t)malloc(sizeof(lf_mutex_t)); 16 | if (mutex) 17 | lf_mutex_init(mutex); 18 | return mutex; 19 | } 20 | 21 | void lf_platform_mutex_free(lf_platform_mutex_ptr_t mutex) { free((void*)mutex); } 22 | int lf_platform_mutex_lock(lf_platform_mutex_ptr_t mutex) { return lf_mutex_lock((lf_mutex_t*)mutex); } 23 | int lf_platform_mutex_unlock(lf_platform_mutex_ptr_t mutex) { return lf_mutex_unlock((lf_mutex_t*)mutex); } 24 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_nrf52_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Soroush Bateni 4 | * @author Abhi Gundrala 5 | * @author Erling Rennemo Jellum 6 | * 7 | * @brief nRF52 API support for the C target of Lingua Franca. 8 | */ 9 | 10 | #ifndef LF_NRF52_SUPPORT_H 11 | #define LF_NRF52_SUPPORT_H 12 | 13 | // This embedded platform has no command line interface 14 | #define NO_CLI 15 | #define MINIMAL_STDLIB 16 | 17 | #include // For fixed-width integral types 18 | #include 19 | 20 | #include // Needed to define PRId64 and PRIu32 21 | #define PRINTF_TIME "%" PRId64 22 | #define PRINTF_MICROSTEP "%" PRIu32 23 | #define PRINTF_TAG "(%" PRId64 ", %" PRIu32 ")" 24 | 25 | /** 26 | * No mutex or condition variable needed for single threaded NRF platforms 27 | */ 28 | typedef void* lf_mutex_t; 29 | typedef void _lf_cond_var_t; 30 | 31 | #endif // LF_nRF52832_SUPPORT_H 32 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.https://www.sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_unix_clock_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_unix_clock_support.h 3 | * @brief Unix clock support functions for the Lingua Franca C runtime. 4 | * 5 | * This header file provides utility functions for converting between Unix 6 | * timespec structures and Lingua Franca time representations. These functions 7 | * are used by platform implementations that rely on Unix clock mechanisms. 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | /** 14 | * @brief Convert a _lf_time_spec_t ('tp') to an instant_t representation in 15 | * nanoseconds. 16 | * 17 | * @return nanoseconds (long long). 18 | */ 19 | instant_t convert_timespec_to_ns(struct timespec tp); 20 | 21 | /** 22 | * @brief Convert an instant_t ('t') representation in nanoseconds to a 23 | * _lf_time_spec_t. 24 | * 25 | * @return _lf_time_spec_t representation of 't'. 26 | */ 27 | struct timespec convert_ns_to_timespec(instant_t t); 28 | -------------------------------------------------------------------------------- /core/utils/hashset/Makefile: -------------------------------------------------------------------------------- 1 | INCLUDES = -I "../../../include/core/utils/" 2 | CFLAGS=-pedantic -Wall -Wextra -Werror -ggdb3 -O0 3 | 4 | test: test.o hashset.o hashset_itr.o 5 | $(CC) $(CFLAGS) test.o hashset.o hashset_itr.o -o $@ 6 | 7 | test.o: test.c 8 | $(CC) $(CFLAGS) $(INCLUDES) -c test.c -o $@ 9 | 10 | hashset.o: hashset.c 11 | $(CC) $(CFLAGS) $(INCLUDES) -c hashset.c -o $@ 12 | 13 | hashset_itr.o: hashset_itr.c 14 | $(CC) $(CFLAGS) $(INCLUDES) -c hashset_itr.c -o $@ 15 | 16 | clean: 17 | rm -f *.o test 18 | 19 | reformat: 20 | astyle --mode=c \ 21 | --quiet \ 22 | --style=1tbs \ 23 | --indent=spaces=4 \ 24 | --indent-col1-comments \ 25 | --max-instatement-indent=78 \ 26 | --pad-oper \ 27 | --pad-header \ 28 | --add-brackets \ 29 | --align-pointer=name \ 30 | *.[ch] 31 | 32 | .PHONY: clean reformat 33 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit tests 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | cmake-args: 7 | description: Arguments to pass to cmake 8 | type: string 9 | required: false 10 | jobs: 11 | run: 12 | strategy: 13 | matrix: 14 | platform: [ubuntu-24.04, macos-latest] 15 | runs-on: ${{ matrix.platform }} 16 | 17 | steps: 18 | - name: Check out reactor-c repository 19 | uses: actions/checkout@v2 20 | 21 | - name: Build and run unit tests ${{ inputs.cmake-args }} 22 | run: | 23 | mkdir build 24 | cd build 25 | cmake .. ${{ inputs.cmake-args }} 26 | cmake --build . 27 | sudo make test 28 | - name: Run RTI unit tests 29 | run: | 30 | cd core/federated/RTI 31 | mkdir build 32 | cd build 33 | cmake .. 34 | cmake --build . 35 | ctest 36 | 37 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/arduino_mbed/MutexWrapper.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Mutex support in RTOS-enabled Arduino Boards (MBED) 4 | * 5 | * @author Anirudh Rengarajan 6 | */ 7 | 8 | #if !defined(LF_SINGLE_THREADED) 9 | #include "mbed.h" 10 | #include "rtos.h" 11 | using namespace rtos; 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | void *mutex_new(){ 18 | return new Mutex(); 19 | } 20 | 21 | void mutex_delete(void* mutex){ 22 | Mutex *m = (Mutex *)mutex; 23 | delete m; 24 | } 25 | 26 | void mutex_lock(void* mutex){ 27 | Mutex *m = (Mutex *)mutex; 28 | m->lock(); 29 | } 30 | 31 | bool mutex_trylock(void* mutex){ 32 | Mutex *m = (Mutex *)mutex; 33 | return m->trylock(); 34 | } 35 | 36 | void mutex_unlock(void* mutex){ 37 | Mutex *m = (Mutex *)mutex; 38 | m->unlock(); 39 | } 40 | 41 | void *mutex_get_owner(void* mutex){ 42 | Mutex *m = (Mutex *)mutex; 43 | return m->get_owner(); 44 | } 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | #endif -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_platform_util.c: -------------------------------------------------------------------------------- 1 | #include "low_level_platform.h" 2 | #include "platform/lf_platform_util.h" 3 | 4 | int map_priorities(int priority, int dest_min, int dest_max) { 5 | // Check if priority is within the legal range 6 | if (priority < LF_SCHED_MIN_PRIORITY || priority > LF_SCHED_MAX_PRIORITY) { 7 | return -1; 8 | } 9 | 10 | // Perform the linear mapping. Since we are working with integers, it is 11 | // important to multiply before we divide 12 | return dest_min + (((priority - LF_SCHED_MIN_PRIORITY) * (dest_max - dest_min)) / 13 | (LF_SCHED_MAX_PRIORITY - LF_SCHED_MIN_PRIORITY)); 14 | } 15 | 16 | #ifndef PLATFORM_ZEPHYR // on Zephyr, this is handled separately 17 | #ifndef LF_SINGLE_THREADED 18 | static int _lf_worker_thread_count = 0; 19 | 20 | static thread_local int lf_thread_id_var = -1; 21 | 22 | int lf_thread_id() { return lf_thread_id_var; } 23 | 24 | void initialize_lf_thread_id() { lf_thread_id_var = lf_atomic_fetch_add(&_lf_worker_thread_count, 1); } 25 | #endif 26 | #endif 27 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_os_single_threaded_support.c: -------------------------------------------------------------------------------- 1 | #if defined LF_SINGLE_THREADED && !defined(PLATFORM_ARDUINO) 2 | /** 3 | * @file lf_os_single_threaded_support.c 4 | * @author Marten Lohstroh 5 | * 6 | * @brief Implementation of platform functions to ensure safe concurrent 7 | * access to a critical section, which are unnecessary in an OS-supported 8 | * single-threaded runtime and therefore are left blank. 9 | * 10 | * These implementataions do nothing, with the functions just returning 0. 11 | * 12 | * @note This file is only to be used in conjuction with an OS-supported 13 | * single-threaded runtime. If threads are enabled, this file will fail 14 | * to compile. If threads are needed, use a multi-threaded runtime instead. 15 | */ 16 | 17 | #if defined(_THREADS_H) || defined(_PTHREAD_H) 18 | #error Usage of threads in the single-threaded runtime is not safe. 19 | #endif 20 | 21 | int lf_disable_interrupts_nested() { return 0; } 22 | 23 | int lf_enable_interrupts_nested() { return 0; } 24 | 25 | int _lf_single_threaded_notify_of_event() { return 0; } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/arduino_mbed/ThreadWrapper.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Threading support in RTOS-enabled Arduino Boards (MBED) 4 | * 5 | * @author Anirudh Rengarajan 6 | */ 7 | 8 | #if !defined(LF_SINGLE_THREADED) 9 | #include "mbed.h" 10 | #include "rtos.h" 11 | 12 | using namespace mbed; 13 | using namespace rtos; 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | void *thread_new(){ 20 | return new Thread(); 21 | } 22 | 23 | void thread_delete(void* thread){ 24 | Thread *t = (Thread*)thread; 25 | delete t; 26 | } 27 | 28 | long int thread_start(void* thread, void (*function) (void *), void* arguments){ 29 | Thread *t = (Thread*)thread; 30 | osStatus s = t->start(callback(function, arguments)); 31 | return s; 32 | } 33 | 34 | long int thread_join(void* thread, void** thread_return){ 35 | Thread *t = (Thread*)thread; 36 | osStatus s = t->join(); 37 | return s; 38 | } 39 | 40 | int thread_terminate(void* thread){ 41 | Thread *t = (Thread*)thread; 42 | return t->terminate(); 43 | } 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | #endif -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_zephyr_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_zephyr_support.h 3 | * @brief Platform-specific support for Zephyr RTOS in the Lingua Franca C runtime. 4 | * 5 | * @author Erling Jellum 6 | * 7 | * This header file provides platform-specific definitions and implementations 8 | * for running Lingua Franca programs on Zephyr RTOS. It includes type definitions 9 | * and platform-specific constants needed for the runtime. 10 | */ 11 | 12 | #ifndef LF_ZEPHYR_SUPPORT_H 13 | #define LF_ZEPHYR_SUPPORT_H 14 | 15 | #include "lf_tag_64_32.h" 16 | 17 | #include // For fixed-width integral types 18 | #include 19 | #include //malloc, calloc, free, realloc 20 | 21 | #include 22 | 23 | #define NO_CLI 24 | #define MINIMAL_STDLIB 25 | #if !defined(LF_SINGLE_THREADED) 26 | 27 | typedef struct k_mutex lf_mutex_t; 28 | typedef struct { 29 | lf_mutex_t* mutex; 30 | struct k_condvar condition; 31 | } lf_cond_t; 32 | typedef struct k_thread* lf_thread_t; 33 | 34 | void _lf_initialize_clock_zephyr_common(); 35 | 36 | #endif // !LF_SINGLE_THREADED 37 | 38 | #endif // LF_ZEPHYR_SUPPORT_H 39 | -------------------------------------------------------------------------------- /DOCUMENTATION.md: -------------------------------------------------------------------------------- 1 | [![CI](https://github.com/lf-lang/reactor-c/actions/workflows/ci.yml/badge.svg)](https://github.com/lf-lang/reactor-c/actions/workflows/ci.yml) 2 | [![API docs](https://github.com/lf-lang/reactor-c/actions/workflows/api-docs.yml/badge.svg)](https://github.com/lf-lang/reactor-c/actions/workflows/api-docs.yml) 3 | 4 | # Reactor-C Documentation 5 | 6 | ## Automatically Generated Doc Files 7 | 8 | The code in reactor-c is documented with Javadoc-style comments that are automatically processed and deployed when you push updates to the repo. The latest docs can be found here: 9 | 10 | - [reactor-c docs](https://www.lf-lang.org/reactor-c/) 11 | 12 | ## Building Doc Files Locally 13 | 14 | To clone the repo and build the doc files locally, simply do this: 15 | 16 | ### Prerequisites 17 | 18 | - Install [doxygen](https://www.doxygen.nl) 19 | 20 | ### Build Documentation Files 21 | 22 | - Check out this repo and build the docs: 23 | - `git clone git@github.com:lf-lang/reactor-c.git` 24 | - `cd reactor-c` 25 | - `make docs` 26 | 27 | ### View Documentation Files 28 | 29 | - Point your browser to the generated HTML page: 30 | - `firefox docs/_build/html/index.html` 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This file lets you format the code-base with a single command. 2 | FILES := $(shell find . -name '*.c' -o -name '*.h') 3 | 4 | .PHONY: help 5 | help: 6 | @echo "Available commands:" 7 | @echo " make format - Format all C source files using clang-format" 8 | @echo " make format-check - Check whether C source files are properly formatted" 9 | @echo " make docs - Generate documentation using Doxygen" 10 | @echo " make clean - Clean up build and documentation" 11 | @echo " make unit-tests - Compile and run unit tests (without scheduling tests)" 12 | 13 | .PHONY: format 14 | format: 15 | clang-format -i -style=file $(FILES) 16 | 17 | .PHONY: format-check 18 | format-check: 19 | clang-format --dry-run --Werror -style=file $(FILES) 20 | 21 | .PHONY: docs 22 | docs: 23 | cd docs && doxygen Doxyfile.in 24 | 25 | .PHONY: clean 26 | clean: 27 | rm -rf build docs/_build 28 | 29 | .PHONY: unit-tests 30 | unit-tests: clean 31 | # In case NUMBER_OF_WORKERS has been set, unset it. 32 | cmake -B build -UNUMBER_OF_WORKERS 33 | cmake --build build 34 | cd build && make test 35 | 36 | # Set help as the default target 37 | .DEFAULT_GOAL := help 38 | -------------------------------------------------------------------------------- /.github/workflows/build-rti.yml: -------------------------------------------------------------------------------- 1 | name: Build the RTI 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | native-build: 8 | strategy: 9 | matrix: 10 | platform: [ubuntu-24.04, macos-latest, windows-latest] 11 | runs-on: ${{ matrix.platform }} 12 | 13 | steps: 14 | - name: Check out reactor-c repository 15 | uses: actions/checkout@v4 16 | - name: Build the RTI with AUTH=OFF 17 | run: .github/scripts/build-rti.sh -DAUTH=OFF 18 | - name: Build the RTI with AUTH=ON 19 | run: .github/scripts/build-rti.sh -DAUTH=ON 20 | 21 | docker-build: 22 | runs-on: ubuntu-24.04 23 | steps: 24 | - name: Check out reactor-c repository 25 | uses: actions/checkout@v4 26 | - name: Set up QEMU 27 | uses: docker/setup-qemu-action@v3 28 | with: 29 | platforms: arm, arm64, riscv64 30 | - name: Set up Docker Buildx 31 | uses: docker/setup-buildx-action@v3 32 | - name: Build Docker image 33 | uses: docker/build-push-action@v6 34 | with: 35 | file: ./core/federated/RTI/rti.Dockerfile 36 | context: . 37 | platforms: linux/amd64, linux/arm64, linux/arm/v7, linux/riscv64 38 | push: false 39 | tags: lflang/rti:latest 40 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_unix_clock_support.c: -------------------------------------------------------------------------------- 1 | #if defined(PLATFORM_Linux) || defined(PLATFORM_Darwin) 2 | #include 3 | #include 4 | 5 | #include "low_level_platform.h" 6 | #include "logging.h" 7 | #include "platform/lf_unix_clock_support.h" 8 | 9 | instant_t convert_timespec_to_ns(struct timespec tp) { return ((instant_t)tp.tv_sec) * BILLION + tp.tv_nsec; } 10 | 11 | struct timespec convert_ns_to_timespec(instant_t t) { 12 | struct timespec tp; 13 | tp.tv_sec = t / BILLION; 14 | tp.tv_nsec = (t % BILLION); 15 | return tp; 16 | } 17 | 18 | void _lf_initialize_clock() { 19 | struct timespec res; 20 | int return_value = clock_getres(CLOCK_REALTIME, (struct timespec*)&res); 21 | if (return_value < 0) { 22 | lf_print_error_and_exit("Could not obtain resolution for CLOCK_REALTIME"); 23 | } 24 | 25 | lf_print("---- System clock resolution: %ld nsec", res.tv_nsec); 26 | } 27 | 28 | /** 29 | * Fetch the value of CLOCK_REALTIME and store it in t. 30 | * @return 0 for success, or -1 for failure. 31 | */ 32 | int _lf_clock_gettime(instant_t* t) { 33 | if (t == NULL) 34 | return -1; 35 | struct timespec tp; 36 | if (clock_gettime(CLOCK_REALTIME, (struct timespec*)&tp) != 0) { 37 | return -1; 38 | } 39 | *t = convert_timespec_to_ns(tp); 40 | return 0; 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /low_level_platform/api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lf-low-level-platform-api INTERFACE) 2 | target_include_directories(lf-low-level-platform-api INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 3 | add_library(lf::low-level-platform-api ALIAS lf-low-level-platform-api) 4 | target_link_libraries(lf-low-level-platform-api INTERFACE lf::tag-api) 5 | 6 | if(${CMAKE_SYSTEM_NAME} STREQUAL "nRF52") 7 | target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_NRF52) 8 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") 9 | target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_ZEPHYR) 10 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Patmos") 11 | target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_PATMOS) 12 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Rp2040") 13 | target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_RP2040) 14 | target_link_libraries(lf-low-level-platform-api INTERFACE pico_stdlib) 15 | target_link_libraries(lf-low-level-platform-api INTERFACE pico_multicore) 16 | target_link_libraries(lf-low-level-platform-api INTERFACE pico_sync) 17 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FlexPRET") 18 | target_compile_definitions(lf-low-level-platform-api INTERFACE PLATFORM_FLEXPRET) 19 | target_link_libraries(lf-low-level-platform-api INTERFACE fp-sdk) 20 | endif() 21 | -------------------------------------------------------------------------------- /core/utils/pqueue.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pqueue.c 3 | * @author Marten Lohstroh 4 | * @author Edward A. Lee 5 | * @author Byeonggil Jun 6 | * 7 | * @brief Priority queue definitions for queues where the priority is a number that can be compared with ordinary 8 | * numerical comparisons. 9 | * 10 | * This is used for the reaction queue. The event queue uses a `tag_t` struct for its priority, so it cannot use this. 11 | */ 12 | 13 | #include "low_level_platform.h" 14 | #include "pqueue.h" 15 | #include "util.h" 16 | #include "lf_types.h" 17 | 18 | int in_reverse_order(pqueue_pri_t thiz, pqueue_pri_t that) { return (thiz > that) ? 1 : (thiz < that) ? -1 : 0; } 19 | 20 | int in_no_particular_order(pqueue_pri_t thiz, pqueue_pri_t that) { 21 | (void)thiz; 22 | (void)that; 23 | return 0; 24 | } 25 | 26 | int reaction_matches(void* a, void* b) { return (a == b); } 27 | 28 | pqueue_pri_t get_reaction_index(void* reaction) { return ((reaction_t*)reaction)->index; } 29 | 30 | size_t get_reaction_position(void* reaction) { return ((reaction_t*)reaction)->pos; } 31 | 32 | void set_reaction_position(void* reaction, size_t pos) { ((reaction_t*)reaction)->pos = pos; } 33 | 34 | void print_reaction(void* reaction) { 35 | reaction_t* r = (reaction_t*)reaction; 36 | LF_PRINT_DEBUG("%s: index: %llx, reaction: %p", r->name, r->index, reaction); 37 | } 38 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/arduino_mbed/ConditionWrapper.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Condition variable support in RTOS-enabled Arduino Boards (MBED) 4 | * 5 | * @author Anirudh Rengarajan 6 | */ 7 | 8 | #if !defined(LF_SINGLE_THREADED) 9 | #include "mbed.h" 10 | #include "MutexWrapper.h" 11 | 12 | using namespace rtos; 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | void* condition_new(void* mutex){ 19 | return new ConditionVariable(*((Mutex*)mutex)); 20 | } 21 | 22 | void condition_delete(void* condition){ 23 | ConditionVariable* cv = (ConditionVariable*) condition; 24 | delete cv; 25 | } 26 | 27 | bool condition_wait_for(void* condition, int64_t wakeup_time){ 28 | ConditionVariable* cv = (ConditionVariable*) condition; 29 | return cv->wait_for(wakeup_time / 1000000LL); 30 | } 31 | 32 | int condition_wait(void* condition){ 33 | ConditionVariable* cv = (ConditionVariable*) condition; 34 | cv->wait(); 35 | return 0; 36 | } 37 | 38 | void condition_notify_one(void* condition) { 39 | ConditionVariable* cv = (ConditionVariable*) condition; 40 | cv->notify_one(); 41 | } 42 | 43 | void condition_notify_all(void* condition) { 44 | ConditionVariable* cv = (ConditionVariable*) condition; 45 | cv->notify_all(); 46 | } 47 | 48 | #ifdef __cplusplus 49 | } 50 | #endif 51 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI](https://github.com/lf-lang/reactor-c/actions/workflows/ci.yml/badge.svg)](https://github.com/lf-lang/reactor-c/actions/workflows/ci.yml) 2 | [![API docs](https://github.com/lf-lang/reactor-c/actions/workflows/api-docs.yml/badge.svg)](https://github.com/lf-lang/reactor-c/actions/workflows/api-docs.yml) 3 | 4 | # README 5 | 6 | The reactor-c runtime system for Lingua Franca is the system used when you specify as your target `C` or `CCpp`. 7 | The [documentation here](https://lf-lang.org/reactor-c) is generated automatically from the C source code using [Doxygen](https://doxygen.nl). 8 | Higher-level user documentation and getting-started information can be found on the 9 | [lf-lang.org website](https://lf-lang.org/docs/next?target-languages=c), 10 | with details about the C runtime on the 11 | [target language details](https://lf-lang.org/docs/next/reference/target-language-details?target-languages=c) 12 | page. 13 | 14 | When you specify 15 | 16 | ``` 17 | target C 18 | ``` 19 | then a C compiler (such as `gcc`) will be used. 20 | If you specify 21 | 22 | ``` 23 | target CCpp 24 | ``` 25 | Then a C++ compiler (such as `g++`) will be used. 26 | In this case, your reaction bodies can written in C++ and you can include and link to C++ code. 27 | Unlike the Cpp target, you will still access the LF API through the C API documented here. 28 | 29 | 30 | -------------------------------------------------------------------------------- /util/tracing/README.md: -------------------------------------------------------------------------------- 1 | ## util/tracing 2 | 3 | This directory contains the source code for utilities that are standalone executables 4 | for post-processing tracing data created by the tracing function in Lingua Franca. 5 | 6 | Utilities for visualizing the data are contained in the [visualization](visualization/README.md) 7 | directory. 8 | 9 | * trace\_to\_csv: Creates a comma-separated values text file from a binary trace file. 10 | The resulting file is suitable for analyzing in spreadsheet programs such as Excel. 11 | 12 | * trace\_to\_chrome: Creates a JSON file suitable for importing into Chrome's trace 13 | visualizer. Point Chrome to chrome://tracing/ and load the resulting file. 14 | 15 | * trace\_to\_influxdb: A preliminary implementation that takes a binary trace file 16 | and uploads its data into [InfluxDB](https://en.wikipedia.org/wiki/InfluxDB). 17 | 18 | * fedsd: A utility that converts trace files from a federate into sequence diagrams 19 | showing the interactions between federates and the RTI. 20 | 21 | ## Installing 22 | 23 | ``` 24 | sudo make install 25 | ``` 26 | Will install the tracing executables to the `bin` directory under `/usr/local/`. To install them to a different location, use the `INSTALL_PREFIX` flag, e.g. 27 | 28 | ``` 29 | make install INSTALL_PREFIX=~/.local/ 30 | ``` 31 | which will install the executables under `~/.local/bin/`. 32 | -------------------------------------------------------------------------------- /include/core/utils/impl/pointer_hashmap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pointer_hashmap.h 3 | * @author Peter Donovan (peterdonovan@berkeley.edu) 4 | * @brief Defines a hashmap type that maps void pointers to integers. 5 | * @ingroup Utilities 6 | * 7 | * To use this: 8 | * ```c 9 | * #include "core/utils/impl/pointer_hashmap.h" 10 | * ``` 11 | * To create a new hashmap: 12 | * ```c 13 | * hashmap_object2int_t my_map = hashmap_object2int_new(CAPACITY, NOTHING); 14 | * ``` 15 | * where CAPACITY is the maximum capacity of the hashmap and NOTHING is 16 | * a pointer key that is guaranteed to be never used (e.g., NULL). 17 | * To put an entry into the hashmap: 18 | * ```c 19 | * hashmap_object2int_put(POINTER, VALUE); 20 | * ``` 21 | * where POINTER is a pointer not equal to NOTHING and VALUE is an integer. 22 | * This will segfault if the hashmap is already at capacity. 23 | * 24 | * To retrieve a value from the hashmap: 25 | * ```c 26 | * int value = hashmap_object2int_get(POINTER); 27 | * ``` 28 | * This will segfault if the entry is not in the hashmap. 29 | * 30 | * See @ref hashmap.h for documentation on how to declare other hashmap types. 31 | */ 32 | 33 | #define HASHMAP(token) hashmap_object2int##_##token 34 | #define K void* 35 | #define V int 36 | #define HASH_OF(key) (size_t)key 37 | #include "hashmap.h" 38 | #undef HASHMAP 39 | #undef K 40 | #undef V 41 | #undef HASH_OF 42 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_rp2040_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_rp2040_support.h 3 | * @brief Platform-specific support for Raspberry Pi Pico (RP2040) in the Lingua Franca C runtime. 4 | * 5 | * @author Edward A. Lee 6 | * 7 | * This header file provides platform-specific definitions and implementations 8 | * for running Lingua Franca programs on the Raspberry Pi Pico (RP2040) microcontroller. 9 | * It includes type definitions, platform-specific constants, and synchronization 10 | * primitives needed for the runtime. 11 | */ 12 | 13 | /** 14 | * @brief pico support for reactor-c 15 | */ 16 | 17 | #ifndef LF_RP2040_SUPPORT_H 18 | #define LF_RP2040_SUPPORT_H 19 | 20 | #include 21 | #include 22 | 23 | #define NO_CLI 24 | #define MINIMAL_STDLIB 25 | 26 | // Defines for formatting time in printf for pico 27 | #define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" 28 | #define PRINTF_TIME "%lld" 29 | #define PRINTF_MICROSTEP "%d" 30 | 31 | #define LF_TIME_BUFFER_LENGTH 80 32 | #define _LF_TIMEOUT 1 33 | 34 | #ifndef LF_SINGLE_THREADED 35 | #warning "Threaded support on rp2040 is still experimental" 36 | 37 | typedef recursive_mutex_t lf_mutex_t; 38 | typedef struct { 39 | semaphore_t notifs[NUM_CORES]; 40 | lf_mutex_t* mutex; 41 | } lf_cond_t; 42 | typedef int lf_thread_t; 43 | 44 | #endif // LF_SINGLE_THREADED 45 | 46 | #endif // LF_PICO_SUPPORT_H 47 | -------------------------------------------------------------------------------- /util/type_converter.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file type_converter.h 3 | * @author Muhammad Khubaib Umer 4 | * 5 | * @brief This file provides macro `DO_CONVERT(fromType, toType, value)`. 6 | * @ingroup Utilities 7 | * 8 | * Sometimes the generic Reactor can work as a connector between two reactors. 9 | * We provide this macro to enable user to provide their own converter libraries 10 | * as long as they follow the convention for naming their conversion functions this macro will work. 11 | * 12 | * Convention: `toType convert__fromType_to__toType(fromType x)`. 13 | */ 14 | 15 | #ifndef TYPE_CONVERTER_H_ 16 | #define TYPE_CONVERTER_H_ 17 | 18 | #define PASTE(x, y) x##y 19 | 20 | #define RESOLVE(i, o, in) PASTE(convert__##i, _to__##o)(in) 21 | 22 | /** 23 | * @brief Convert the specified value from one type to another. 24 | * @ingroup Utilities 25 | * 26 | * This macro enables user to provide their own converter libraries as long as they 27 | * follow the convention for naming their conversion functions. 28 | * @note Converter library functions must follow this convention: `toType convert__fromType_to__toType(fromType x)`. 29 | * 30 | * @param fromType Typename of `value` field 31 | * @param toType Typename of desired type 32 | * @param value Actual value of type `fromType` 33 | */ 34 | #define DO_CONVERT(fromType, toType, value) RESOLVE(fromType, toType, value) 35 | 36 | #endif // TYPE_CONVERTER_H_ 37 | -------------------------------------------------------------------------------- /platform/impl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LF_PLATFORM_FILES ${CMAKE_CURRENT_LIST_DIR}/platform.c) 2 | 3 | 4 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Zephyr") 5 | zephyr_library_named(lf-platform-impl) 6 | zephyr_library_sources(${LF_PLATFORM_FILES}) 7 | zephyr_library_link_libraries(kernel) 8 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FlexPRET") 9 | add_library(lf-platform-impl STATIC) 10 | target_sources(lf-platform-impl PUBLIC ${LF_PLATFORM_FILES}) 11 | target_link_libraries(lf-platform-impl PRIVATE fp-sdk) 12 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Patmos") 13 | add_library(lf-platform-impl STATIC) 14 | target_sources(lf-platform-impl PUBLIC ${LF_PLATFORM_FILES}) 15 | else() 16 | add_library(lf-platform-impl STATIC) 17 | target_sources(lf-platform-impl PUBLIC ${LF_PLATFORM_FILES}) 18 | endif() 19 | 20 | add_library(lf::platform-impl ALIAS lf-platform-impl) 21 | target_link_libraries(lf-platform-impl PRIVATE lf::low-level-platform-api) 22 | target_link_libraries(lf-platform-impl PRIVATE lf::platform-api) 23 | message(STATUS "Applying preprocessor definitions to platform...") 24 | macro(platform_define X) 25 | if(DEFINED ${X}) 26 | message(STATUS ${X}=${${X}}) 27 | target_compile_definitions(lf-platform-impl PUBLIC ${X}=${${X}}) 28 | endif(DEFINED ${X}) 29 | endmacro() 30 | platform_define(LF_SINGLE_THREADED) 31 | platform_define(LOG_LEVEL) 32 | platform_define(MODAL_REACTORS) 33 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_atomic_gcc_clang.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Soroush Bateni 4 | * @author Erling Rennemo Jellum 5 | * 6 | * @brief Atomic operations using GCC/Clang APIs. 7 | */ 8 | #if defined(PLATFORM_Linux) || defined(PLATFORM_Darwin) 9 | #if defined(__GNUC__) || defined(__clang__) 10 | 11 | #include "platform/lf_atomic.h" 12 | #include "low_level_platform.h" 13 | 14 | int lf_atomic_fetch_add(int* ptr, int value) { return __sync_fetch_and_add(ptr, value); } 15 | int64_t lf_atomic_fetch_add64(int64_t* ptr, int64_t value) { return __sync_fetch_and_add(ptr, value); } 16 | int lf_atomic_add_fetch(int* ptr, int value) { return __sync_add_and_fetch(ptr, value); } 17 | int64_t lf_atomic_add_fetch64(int64_t* ptr, int64_t value) { return __sync_add_and_fetch(ptr, value); } 18 | bool lf_atomic_bool_compare_and_swap(int* ptr, int oldval, int newval) { 19 | return __sync_bool_compare_and_swap(ptr, oldval, newval); 20 | } 21 | bool lf_atomic_bool_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval) { 22 | return __sync_bool_compare_and_swap(ptr, oldval, newval); 23 | } 24 | int lf_atomic_val_compare_and_swap(int* ptr, int oldval, int newval) { 25 | return __sync_val_compare_and_swap(ptr, oldval, newval); 26 | } 27 | int64_t lf_atomic_val_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval) { 28 | return __sync_val_compare_and_swap(ptr, oldval, newval); 29 | } 30 | 31 | #endif 32 | #endif 33 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_windows_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * @author Soroush Bateni 5 | * @author Erling Jellum 6 | * 7 | * @brief Windows API support for the C target of Lingua Franca. 8 | * 9 | * The API is implemented in the header files. This is also the case for Linux 10 | * and macos. 11 | * 12 | * All functions return 0 on success. 13 | * 14 | * @see https://gist.github.com/Soroosh129/127d1893fa4c1da6d3e1db33381bb273 15 | */ 16 | 17 | #ifndef LF_WINDOWS_SUPPORT_H 18 | #define LF_WINDOWS_SUPPORT_H 19 | 20 | #include // For fixed-width integral types 21 | #include 22 | 23 | #if !defined LF_SINGLE_THREADED 24 | /** 25 | * On Windows, one could use both a mutex or 26 | * a critical section for the same purpose. However, 27 | * critical sections are lighter and limited to one process 28 | * and thus fit the requirements of Lingua Franca. 29 | */ 30 | typedef CRITICAL_SECTION lf_mutex_t; 31 | /** 32 | * For compatibility with other platform APIs, we assume 33 | * that mutex is analogous to critical section. 34 | */ 35 | typedef lf_mutex_t _lf_critical_section_t; 36 | typedef struct { 37 | _lf_critical_section_t* critical_section; 38 | CONDITION_VARIABLE condition; 39 | } lf_cond_t; 40 | typedef HANDLE lf_thread_t; 41 | #endif 42 | 43 | // Use 64-bit times and 32-bit unsigned microsteps 44 | #include "lf_tag_64_32.h" 45 | 46 | #endif // LF_WINDOWS_SUPPORT_H 47 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_atomic_windows.c: -------------------------------------------------------------------------------- 1 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) 2 | /** 3 | * @file 4 | * @author Soroush Bateni 5 | * @author Erling Rennemo Jellum 6 | * @brief Implements the atomic API for Windows machines. 7 | */ 8 | 9 | #include "platform/lf_atomic.h" 10 | #include 11 | 12 | int lf_atomic_fetch_add(int* ptr, int value) { return InterlockedExchangeAdd((LONG*)ptr, (LONG)value); } 13 | int64_t lf_atomic_fetch_add64(int64_t* ptr, int64_t value) { return InterlockedExchangeAdd64(ptr, value); } 14 | int lf_atomic_add_fetch(int* ptr, int value) { return InterlockedAdd((LONG*)ptr, (LONG)value); } 15 | int64_t lf_atomic_add_fetch64(int64_t* ptr, int64_t value) { return InterlockedAdd64(ptr, value); } 16 | bool lf_atomic_bool_compare_and_swap(int* ptr, int oldval, int newval) { 17 | return (InterlockedCompareExchange((LONG*)ptr, (LONG)newval, (LONG)oldval) == oldval); 18 | } 19 | bool lf_atomic_bool_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval) { 20 | return (InterlockedCompareExchange64(ptr, newval, oldval) == oldval); 21 | } 22 | int lf_atomic_val_compare_and_swap(int* ptr, int oldval, int newval) { 23 | return InterlockedCompareExchange((LONG*)ptr, (LONG)newval, (LONG)oldval); 24 | } 25 | int64_t lf_atomic_val_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval) { 26 | return InterlockedCompareExchange64(ptr, newval, oldval); 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All files in this repository are copyrighted by their respective authors 2 | and/or institutions and bear a BSD 2-Clause License: 3 | 4 | Copyright (c) 2021-2025, by the Authors 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_tag_64_32.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_tag_64_32.h 3 | * @brief Format strings for printf for 64-bit times and 32-bit unsigned microsteps. 4 | * 5 | * Define PRINTF_TIME and PRINTF_MICROSTEP, which are the printf 6 | * codes (like the d in %d to print an int) for time and microsteps. 7 | * To use these, specify the printf as follows: 8 | * printf("Time: " PRINTF_TIME "\n", time_value); 9 | * On most platforms, time is an signed 64-bit number (int64_t) and 10 | * the microstep is an unsigned 32-bit number (uint32_t). 11 | * Sadly, in C, there is no portable to print such numbers using 12 | * printf without getting a warning on some platforms. 13 | * On each platform, the code for printf if given by the macros 14 | * PRId64 and PRIu32 defined in inttypes.h. Hence, here, we import 15 | * inttypes.h, then define PRINTF_TIME and PRINTF_MICROSTEP. 16 | * If you are targeting a platform that uses some other type 17 | * for time and microsteps, you can simply define 18 | * PRINTF_TIME and PRINTF_MICROSTEP directly in the same file that 19 | * defines the types instant_t, interval_t, and microstep_t. 20 | */ 21 | 22 | #include 23 | #define PRINTF_TIME "%" PRId64 24 | #define PRINTF_MICROSTEP "%" PRIu32 25 | 26 | // For convenience, the following string can be inserted in a printf 27 | // format for printing both time and microstep as follows: 28 | // printf("Tag is " PRINTF_TAG "\n", time_value, microstep); 29 | #define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" 30 | -------------------------------------------------------------------------------- /docs/markdown/2_introduction.md: -------------------------------------------------------------------------------- 1 | \page intro Introduction 2 | 3 | \note These docs assumes familiarity with the Lingua Franca coordination langauge. Refer to 4 | the [Lingua Franca Handbook](https://www.lf-lang.org/docs/) for an in-depth guide to 5 | reactor-oriented programming. 6 | 7 | ## Getting started 8 | 9 | First, [install Lingua Franca](https://www.lf-lang.org/docs/installation). 10 | If you install this by cloning the [lingua-franca repository](https://github.com/lf-lang/lingua-franca), then the `reactor-c` repository will be installed as a git submodule at location 11 | `core/src/main/resources/lib/c/reactor-c` within the `lingua-franca` clone. 12 | 13 | The simplest possible C-target LF program looks like this: 14 | 15 | ```lf 16 | target C 17 | main reactor { 18 | reaction(startup) {= 19 | printf("Hello World!\n"); 20 | =} 21 | } 22 | ``` 23 | 24 | Put this in a file `HelloWorld.lf` in a directory called `src`. 25 | Assuming that upon installing Lingua Franca, you arranged it so that `lfc` is in your `PATH`, then you can compile this program and run it as follows: 26 | 27 | ```shell 28 | lfc src/HelloWorld.lf 29 | bin/HelloWorld 30 | ``` 31 | 32 | When executed, the program should print `Hello World!` and terminate. 33 | The `bin` directory is created automatically within the same directory that houses your `src` directory. 34 | 35 | The `lfc` compiler will also create a directory `src-gen` that includes a standalone C program together with all required compiler infrastructure (cmake files, etc.). 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /docs/markdown/4_license.md: -------------------------------------------------------------------------------- 1 | \page license License 2 | 3 | All files in this repository are copyrighted by their respective authors 4 | and/or institutions and bear a BSD 2-Clause License: 5 | 6 | Copyright (c) 2021-2025, by the Authors 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright notice, this 12 | list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /python/include/modal_models/definitions.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * @author Soroush Bateni 5 | * 6 | * @brief Definitions needed for the modal models in the Python target. 7 | */ 8 | 9 | #ifndef PYTHON_MODAL_MODELS_DEFS_H 10 | #define PYTHON_MODAL_MODELS_DEFS_H 11 | 12 | #ifdef MODAL_REACTORS 13 | #include 14 | #include 15 | #include "tag.h" 16 | #include "../include/api/schedule.h" 17 | 18 | /** 19 | * The struct used to represent modes in Python. 20 | * An instance of this struct is created when entering a reaction that 21 | * has declared a mode as an effect. This struct represents everything 22 | * needed to take a transition to that mode, including a pointer to 23 | * that mode and the type of transition (reset or history). 24 | */ 25 | typedef struct { 26 | PyObject_HEAD PyObject* mode; 27 | PyObject* lf_self; 28 | lf_mode_change_type_t change_type; 29 | } mode_capsule_struct_t; 30 | 31 | /** 32 | * Set a new mode for a modal model. 33 | */ 34 | static PyObject* py_mode_set(PyObject* self, PyObject* args); 35 | 36 | /** 37 | * Convert a `reactor_mode_t` to a `mode_capsule_t`. 38 | */ 39 | PyObject* convert_C_mode_to_py(reactor_mode_t* mode, self_base_t* lf_self, lf_mode_change_type_t change_type); 40 | 41 | /** 42 | * @brief Initialize `mode_capsule_t` in the `current_module`. 43 | * 44 | */ 45 | void initialize_mode_capsule_t(PyObject* current_module); 46 | 47 | #else 48 | #define initialize_mode_capsule_t(...) 49 | #endif // MODAL_REACTORS 50 | 51 | #endif // PYTHON_MODAL_MODELS_DEFS_H 52 | -------------------------------------------------------------------------------- /trace/impl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LF_ROOT ${CMAKE_CURRENT_LIST_DIR}/../..) 2 | include(${LF_ROOT}/core/lf_utils.cmake) 3 | 4 | add_library(lf-trace-impl STATIC) 5 | add_library(lf::trace-impl ALIAS lf-trace-impl) 6 | target_link_libraries(lf-trace-impl PRIVATE lf::trace-api) 7 | target_link_libraries(lf-trace-impl PRIVATE lf::platform-api) 8 | target_link_libraries(lf-trace-impl PRIVATE lf::logging-api) 9 | target_link_libraries(lf-trace-impl PRIVATE lf::version-api) 10 | lf_enable_compiler_warnings(lf-trace-impl) 11 | 12 | target_sources(lf-trace-impl PUBLIC ${CMAKE_CURRENT_LIST_DIR}/src/trace_impl.c) 13 | 14 | target_include_directories(lf-trace-impl PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) 15 | 16 | # handle compile-time parameters 17 | if(NOT DEFINED LOG_LEVEL) 18 | message(FATAL_ERROR "You must set LOG_LEVEL cmake argument") 19 | endif() 20 | target_compile_definitions(lf-trace-impl PRIVATE LOG_LEVEL=${LOG_LEVEL}) 21 | # build type parameter (release, debug, etc) is implicitly handled by CMake 22 | 23 | # make name platform-independent 24 | set_target_properties(lf-trace-impl PROPERTIES PREFIX "") 25 | set_target_properties(lf-trace-impl PROPERTIES OUTPUT_NAME "lf-trace-impl") 26 | set_target_properties(lf-trace-impl PROPERTIES SUFFIX ".a") 27 | set_target_properties(lf-trace-impl PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/lib") 28 | set_target_properties(lf-trace-impl PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_LIST_DIR}/lib") 29 | set_target_properties(lf-trace-impl PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_LIST_DIR}/lib") 30 | -------------------------------------------------------------------------------- /core/threaded/scheduler_instance.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Soroush Bateni 4 | * @author Edward A. Lee 5 | * 6 | * @brief Common scheduler functions. 7 | * 8 | * This file defines functions that are common across multiple schedulers. 9 | */ 10 | 11 | #include 12 | #include "scheduler_instance.h" 13 | #include "environment.h" 14 | #include "reactor.h" 15 | #include "lf_types.h" 16 | #include "util.h" 17 | 18 | bool init_sched_instance(environment_t* env, lf_scheduler_t** instance, size_t number_of_workers, 19 | sched_params_t* params) { 20 | 21 | assert(env != GLOBAL_ENVIRONMENT); 22 | LF_ASSERT(env, "`init_sched_instance` called without env pointer being set"); 23 | 24 | // Check if the instance is already initialized 25 | LF_CRITICAL_SECTION_ENTER(env); 26 | if (*instance != NULL) { 27 | // Already initialized 28 | LF_CRITICAL_SECTION_EXIT(env); 29 | return false; 30 | } else { 31 | *instance = (lf_scheduler_t*)calloc(1, sizeof(lf_scheduler_t)); 32 | } 33 | LF_MUTEX_UNLOCK(&env->mutex); 34 | 35 | if (params == NULL || params->num_reactions_per_level_size == 0) { 36 | (*instance)->max_reaction_level = DEFAULT_MAX_REACTION_LEVEL; 37 | } 38 | 39 | if (params != NULL) { 40 | if (params->num_reactions_per_level != NULL) { 41 | (*instance)->max_reaction_level = params->num_reactions_per_level_size - 1; 42 | } 43 | } 44 | 45 | (*instance)->number_of_workers = number_of_workers; 46 | 47 | (*instance)->should_stop = false; 48 | (*instance)->env = env; 49 | 50 | return true; 51 | } 52 | -------------------------------------------------------------------------------- /platform/api/platform.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file platform.h 3 | * @author Peter Donovan 4 | * 5 | * @brief Platform API for runtime plugins to use while sharing implementation 6 | * source code and binaries with the core and with each other. 7 | * @ingroup Platform 8 | */ 9 | 10 | /** 11 | * @brief Pointer to the platform-specific implementation of a mutex. 12 | * @ingroup Platform 13 | */ 14 | typedef void* lf_platform_mutex_ptr_t; 15 | 16 | /** 17 | * @brief Create a new mutex and return (a pointer to) it. 18 | * @ingroup Platform 19 | */ 20 | lf_platform_mutex_ptr_t lf_platform_mutex_new(); 21 | 22 | /** 23 | * @brief Free all resources associated with the provided mutex. 24 | * @ingroup Platform 25 | */ 26 | void lf_platform_mutex_free(lf_platform_mutex_ptr_t mutex); 27 | 28 | /** 29 | * @brief Acquire the given mutex. 30 | * 31 | * @return 0 on success, platform-specific error number otherwise. 32 | * @ingroup Platform 33 | */ 34 | int lf_platform_mutex_lock(lf_platform_mutex_ptr_t mutex); 35 | 36 | /** 37 | * @brief Release the given mutex. 38 | * 39 | * @return 0 on success, platform-specific error number otherwise. 40 | * @ingroup Platform 41 | */ 42 | int lf_platform_mutex_unlock(lf_platform_mutex_ptr_t mutex); 43 | 44 | /// \cond INTERNAL // Doxygen conditional. 45 | // The following is defined in low_level_platform.h, so ask Doxygen to ignore this. 46 | 47 | /** 48 | * @brief The ID of the current thread. 49 | * 50 | * The only guarantee is that these IDs will be a contiguous range of numbers 51 | * starting at 0. 52 | */ 53 | int lf_thread_id(); 54 | 55 | /// \endcond // INTERNAL 56 | -------------------------------------------------------------------------------- /util/tracing/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for utilities that convert Lingua Franca trace files 2 | # into other formats. 3 | # @author: Edward A. Lee 4 | REACTOR_C=../.. 5 | CURRENT_PATH=$(shell pwd) 6 | CC=gcc 7 | CFLAGS= -I$(REACTOR_C)/include/core/ \ 8 | -I$(REACTOR_C)/include/core/modal_models \ 9 | -I$(REACTOR_C)/include/core/utils \ 10 | -I$(REACTOR_C)/include \ 11 | -I$(REACTOR_C)/low_level_platform/api \ 12 | -I$(REACTOR_C)/tag/api \ 13 | -I$(REACTOR_C)/trace/api \ 14 | -I$(REACTOR_C)/trace/api/types \ 15 | -I$(REACTOR_C)/version/api \ 16 | -I$(REACTOR_C)/logging/api \ 17 | -I$(REACTOR_C)/trace/impl/include \ 18 | -DLF_SINGLE_THREADED=1 \ 19 | -Wall 20 | DEPS= 21 | LIBS=-lcurl 22 | 23 | INSTALL_PREFIX ?= /usr/local 24 | BIN_INSTALL_PATH = $(INSTALL_PREFIX)/bin 25 | 26 | %.o: %.c $(DEPS) 27 | $(CC) -c -o $@ $< $(CFLAGS) 28 | 29 | trace_to_csv: trace_to_csv.o trace_util.o 30 | $(CC) -o trace_to_csv trace_to_csv.o trace_util.o 31 | 32 | trace_to_chrome: trace_to_chrome.o trace_util.o 33 | $(CC) -o trace_to_chrome trace_to_chrome.o trace_util.o 34 | 35 | trace_to_influxdb: trace_to_influxdb.o trace_util.o 36 | $(CC) -o trace_to_influxdb trace_to_influxdb.o trace_util.o $(LIBS) 37 | 38 | install: trace_to_csv trace_to_chrome trace_to_influxdb 39 | cp trace_to_csv $(BIN_INSTALL_PATH) 40 | cp trace_to_chrome $(BIN_INSTALL_PATH) 41 | cp trace_to_influxdb $(BIN_INSTALL_PATH) 42 | cp ./visualization/fedsd.py $(BIN_INSTALL_PATH) 43 | ln -f -s $(BIN_INSTALL_PATH)/fedsd.py $(BIN_INSTALL_PATH)/fedsd 44 | chmod +x $(BIN_INSTALL_PATH)/fedsd 45 | 46 | clean: 47 | rm -f *.o trace_to_chrome trace_to_influxdb trace_to_csv 48 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_macos_support.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * @author Soroush Bateni 5 | * 6 | * @brief MacOS API support for the C target of Lingua Franca. 7 | */ 8 | 9 | #ifdef PLATFORM_Darwin 10 | /* MacOS API support for the C target of Lingua Franca. */ 11 | 12 | #include "platform/lf_macos_support.h" 13 | #include "low_level_platform.h" 14 | #include "tag.h" 15 | 16 | #if defined LF_SINGLE_THREADED 17 | #include "lf_os_single_threaded_support.c" 18 | #else 19 | #include "lf_POSIX_threads_support.c" 20 | 21 | /** 22 | * Real-time scheduling API not implemented for macOS. 23 | */ 24 | int lf_thread_set_cpu(lf_thread_t thread, size_t cpu_number) { return -1; } 25 | 26 | int lf_thread_set_priority(lf_thread_t thread, int priority) { return -1; } 27 | 28 | int lf_thread_set_scheduling_policy(lf_thread_t thread, lf_scheduling_policy_t* policy) { return -1; } 29 | #endif 30 | 31 | #include "platform/lf_unix_clock_support.h" 32 | 33 | // See `man 2 clock_nanosleep` for return values 34 | int lf_sleep(interval_t sleep_duration) { 35 | const struct timespec tp = convert_ns_to_timespec(sleep_duration); 36 | struct timespec remaining; 37 | return nanosleep((const struct timespec*)&tp, (struct timespec*)&remaining); 38 | } 39 | 40 | int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time) { 41 | (void)env; 42 | interval_t sleep_duration = wakeup_time - lf_time_physical(); 43 | 44 | if (sleep_duration <= 0) { 45 | return 0; 46 | } else { 47 | return lf_sleep(sleep_duration); 48 | } 49 | } 50 | 51 | int lf_nanosleep(interval_t sleep_duration) { return lf_sleep(sleep_duration); } 52 | #endif 53 | -------------------------------------------------------------------------------- /docs/assets/doxygen-awesome-sidebar-only-darkmode-toggle.css: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | 4 | Doxygen Awesome 5 | https://github.com/jothepro/doxygen-awesome-css 6 | 7 | MIT License 8 | 9 | Copyright (c) 2021 - 2023 jothepro 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | 29 | */ 30 | 31 | @media screen and (min-width: 768px) { 32 | 33 | #MSearchBox { 34 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px); 35 | } 36 | 37 | #MSearchField { 38 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(ReactorC LANGUAGES C) 3 | 4 | # Require C11 5 | set(CMAKE_C_STANDARD 11) 6 | set(CMAKE_C_STANDARD_REQUIRED ON) 7 | 8 | # Require C++17 9 | set(CMAKE_CXX_STANDARD 17) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | 12 | # Currently this build process is used for testing, not production. 13 | set(DEFAULT_BUILD_TYPE Debug) 14 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 15 | set(CMAKE_BUILD_TYPE ${DEFAULT_BUILD_TYPE} CACHE STRING "Choose the type of build." FORCE) 16 | endif() 17 | 18 | # Whether or not we are using THREADED or SINGLE-THREADED runtime is a global 19 | # flag. If it is passed to cmake. Then add it as a global compile def which 20 | # is inherited by all child nodes (add_subdirectory) 21 | if(DEFINED LF_SINGLE_THREADED) 22 | add_compile_definitions(LF_SINGLE_THREADED=1) 23 | endif() 24 | 25 | set(Lib lib) 26 | set(CoreLibPath core) 27 | set(CoreLib reactor-c) 28 | set(PlatformLib platform) 29 | 30 | include_directories(${CMAKE_SOURCE_DIR}/include) 31 | include_directories(${CMAKE_SOURCE_DIR}/include/core) 32 | include_directories(${CMAKE_SOURCE_DIR}/include/core/federated) 33 | include_directories(${CMAKE_SOURCE_DIR}/include/core/federated/network) 34 | include_directories(${CMAKE_SOURCE_DIR}/include/core/modal_models) 35 | include_directories(${CMAKE_SOURCE_DIR}/include/core/platform) 36 | include_directories(${CMAKE_SOURCE_DIR}/include/core/threaded) 37 | include_directories(${CMAKE_SOURCE_DIR}/include/core/utils) 38 | include_directories(${CMAKE_SOURCE_DIR}/include/api) 39 | 40 | enable_testing() 41 | add_subdirectory(${Lib}) 42 | add_subdirectory(${CoreLibPath}) 43 | 44 | include(test/Tests.cmake) 45 | -------------------------------------------------------------------------------- /include/core/threaded/scheduler_sync_tag_advance.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file scheduler_sync_tag_advance.h 3 | * @author Soroush Bateni 4 | * @author Edward A. Lee 5 | * @author Marten Lohstroh 6 | * 7 | * @brief API used to advance tag globally. 8 | * @ingroup Internal 9 | */ 10 | 11 | #ifndef SCHEDULER_SYNC_TAG_ADVANCE_H 12 | #define SCHEDULER_SYNC_TAG_ADVANCE_H 13 | 14 | #include 15 | 16 | #include "tag.h" 17 | #include "scheduler_instance.h" 18 | 19 | /** 20 | * @brief Placeholder for code-generated function that will, in a federated 21 | * execution, be used to coordinate the advancement of tag. 22 | * @ingroup Internal 23 | * 24 | * It will notify the runtime infrastructure (RTI) that all reactions at the 25 | * specified logical tag have completed. 26 | * 27 | * This function should be called only while holding the mutex lock. 28 | * 29 | * @param tag_to_send The tag to send. 30 | */ 31 | void logical_tag_complete(tag_t tag_to_send); 32 | 33 | /** 34 | * @brief Return true if the worker should stop now; false otherwise. 35 | * @ingroup Internal 36 | * This function assumes the caller holds the mutex lock. 37 | * @param sched The scheduler instance to check. 38 | */ 39 | bool should_stop_locked(lf_scheduler_t* sched); 40 | 41 | /** 42 | * @brief Advance the tag to the next tag on the event queue. 43 | * @ingroup Internal 44 | * 45 | * This will also pop events for the newly acquired tag and trigger 46 | * the enabled reactions using the scheduler. 47 | * 48 | * This function assumes the caller holds the environment mutex lock. 49 | * @param sched The scheduler instance to check. 50 | * @return True if the worker thread should exit. False otherwise. 51 | */ 52 | bool _lf_sched_advance_tag_locked(lf_scheduler_t* sched); 53 | 54 | #endif // LF_C11_THREADS_SUPPORT_H 55 | -------------------------------------------------------------------------------- /test/Tests.cmake: -------------------------------------------------------------------------------- 1 | # This adds all tests in the test directory. 2 | include(CTest) 3 | 4 | set(TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test) 5 | set(TEST_SUFFIX test.c) # Files that are tests must have names ending with TEST_SUFFIX. 6 | set(LF_ROOT ${CMAKE_CURRENT_LIST_DIR}/..) 7 | set(TEST_MOCK_SRCS ${TEST_DIR}/src_gen_stub.c ${TEST_DIR}/rand_utils.c) 8 | 9 | include(${LF_ROOT}/core/lf_utils.cmake) 10 | 11 | # Add the test files found in DIR to TEST_FILES. 12 | function(add_test_dir DIR) 13 | file( 14 | GLOB_RECURSE TEST_FILES_FOR_DIR 15 | LIST_DIRECTORIES false 16 | RELATIVE ${TEST_DIR} 17 | ${DIR}/*${TEST_SUFFIX} 18 | ) 19 | list(APPEND TEST_FILES ${TEST_FILES_FOR_DIR}) 20 | set(TEST_FILES ${TEST_FILES} PARENT_SCOPE) 21 | endfunction() 22 | 23 | # Add the appropriate directories for the provided build parameters. 24 | add_test_dir(${TEST_DIR}/general) 25 | if(NUMBER_OF_WORKERS) 26 | if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 27 | add_test_dir(${TEST_DIR}/scheduling) 28 | endif() 29 | endif(NUMBER_OF_WORKERS) 30 | 31 | # Create executables for each test. 32 | foreach(FILE ${TEST_FILES}) 33 | string(REGEX REPLACE "[./]" "_" NAME ${FILE}) 34 | add_executable(${NAME} ${TEST_DIR}/${FILE} ${TEST_MOCK_SRCS}) 35 | add_test(NAME ${NAME} COMMAND ${NAME}) 36 | # This is needed for the tests to use the threading API declared in 37 | # low_level_platform.h. Ideally this would not be needed. 38 | target_link_libraries(${NAME} PRIVATE lf::low-level-platform-impl) 39 | target_link_libraries( 40 | ${NAME} PRIVATE 41 | ${CoreLib} ${Lib} 42 | ) 43 | target_include_directories(${NAME} PRIVATE ${TEST_DIR}) 44 | # Warnings as errors 45 | lf_enable_compiler_warnings(${NAME}) 46 | endforeach(FILE ${TEST_FILES}) 47 | -------------------------------------------------------------------------------- /trace/impl/include/trace_impl.h: -------------------------------------------------------------------------------- 1 | #include "trace.h" 2 | 3 | // FIXME: Target property should specify the capacity of the trace buffer. 4 | #define TRACE_BUFFER_CAPACITY 2048 5 | 6 | /** Size of the table of trace objects. */ 7 | #define TRACE_OBJECT_TABLE_SIZE 1024 8 | 9 | /** Max length of trace file name*/ 10 | #define TRACE_MAX_FILENAME_LENGTH 128 11 | 12 | // TYPE DEFINITIONS ********************************************************** 13 | 14 | /** 15 | * @brief This struct holds all the state associated with tracing in a single environment. 16 | * Each environment which has tracing enabled will have such a struct on its environment struct. 17 | * 18 | */ 19 | typedef struct trace_t { 20 | /** 21 | * Array of buffers into which traces are written. 22 | * When a buffer becomes full, the contents is flushed to the file, 23 | * which will create a significant pause in the calling thread. 24 | */ 25 | trace_record_nodeps_t** _lf_trace_buffer; 26 | size_t* _lf_trace_buffer_size; 27 | 28 | /** The number of trace buffers allocated when tracing starts. */ 29 | size_t _lf_number_of_trace_buffers; 30 | 31 | /** Marker that tracing is stopping or has stopped. */ 32 | int _lf_trace_stop; 33 | 34 | /** The file into which traces are written. */ 35 | FILE* _lf_trace_file; 36 | 37 | /** The file name where the traces are written*/ 38 | char filename[TRACE_MAX_FILENAME_LENGTH]; 39 | 40 | /** Table of pointers to a description of the object. */ 41 | object_description_t _lf_trace_object_descriptions[TRACE_OBJECT_TABLE_SIZE]; 42 | size_t _lf_trace_object_descriptions_size; 43 | 44 | /** Indicator that the trace header information has been written to the file. */ 45 | bool _lf_trace_header_written; 46 | 47 | // /** Pointer back to the environment which we are tracing within*/ 48 | // environment_t* env; 49 | } trace_t; 50 | -------------------------------------------------------------------------------- /core/federated/RTI/README.md: -------------------------------------------------------------------------------- 1 | \file README.md 2 | \ingroup RTI 3 | # RTI Installation Instructions 4 | 5 | This folder contains the source code for the Run-Time Infrastructure (RTI) that 6 | is necessary for federated Lingua Franca programs. 7 | 8 | By default, as of version 0.10.0, when you run `lfc` on a federated Lingua Franca program, a copy of 9 | the RTI code will be included in the generated code, compiled, and invoked by the 10 | launch scripts. This mechanism ensures that you always get the version of the 11 | RTI that matches your version of lfc. 12 | 13 | Here, we provide instructions to manually compile and install the RTI. 14 | For example, in the directory `core/federated/RTI` of the `reactor-c` repository, do: 15 | 16 | ```bash 17 | mkdir build && cd build 18 | cmake ../ 19 | make 20 | sudo make install 21 | ``` 22 | 23 | To run the unit tests 24 | ```bash 25 | make test 26 | ``` 27 | 28 | **Note:** To enable DEBUG messages, use the following build commands instead: 29 | 30 | ```bash 31 | mkdir build && cd build 32 | cmake -DCMAKE_BUILD_TYPE=DEBUG ../ 33 | make 34 | sudo make install 35 | ``` 36 | 37 | If you would like to go back to the non-DEBUG mode, you would have to remove all contents of the `build` folder. 38 | 39 | **Note:** To enable simple HMAC-based authentication of federates, 40 | add `-DAUTH=ON` option to the cmake command as shown below: 41 | 42 | ```bash 43 | mkdir build && cd build 44 | cmake -DAUTH=ON ../ 45 | make 46 | sudo make install 47 | ``` 48 | 49 | If you would like to go back to non-AUTH mode, you would have to remove all contents of the `build` folder. 50 | 51 | To build a docker image for the RTI, do 52 | ```bash 53 | docker build -t lflang/rti:latest -f rti.Dockerfile ../../../ 54 | ``` 55 | 56 | To push it to DockerHub, run: 57 | ```bash 58 | docker push lflang/rti:latest 59 | ``` 60 | 61 | You may need to login first: 62 | ```bash 63 | docker login -u [username] 64 | ``` 65 | 66 | To authenticate, request a PAT on [DockerHub](https://hub.docker.com/settings/security). 67 | 68 | -------------------------------------------------------------------------------- /core/mixed_radix.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * 5 | * @brief Functions for permuted mixed-radix numbers used in Lingua Franca programs. 6 | * 7 | * See @ref mixed_radix.h for docs. 8 | */ 9 | 10 | #include 11 | #include // defines NULL 12 | 13 | #include "mixed_radix.h" 14 | 15 | /** 16 | * Increment the mixed radix number by one according to the permutation matrix. 17 | * @param mixed A pointer to the mixed-radix number. 18 | */ 19 | void mixed_radix_incr(mixed_radix_int_t* mixed) { 20 | int i = 0; 21 | assert(mixed != NULL); 22 | assert(mixed->size > 0); 23 | while (i < mixed->size) { 24 | int digit_to_increment = mixed->permutation[i]; 25 | assert(digit_to_increment >= 0); 26 | mixed->digits[digit_to_increment]++; 27 | if (mixed->digits[digit_to_increment] >= mixed->radixes[digit_to_increment]) { 28 | mixed->digits[digit_to_increment] = 0; 29 | i++; 30 | } else { 31 | return; // All done. 32 | } 33 | } 34 | // If we get here, the number has overflowed. Wrap to zero. 35 | mixed->digits[i - 1] = 0; 36 | } 37 | 38 | /** 39 | * Return the int value of a mixed-radix number after dropping 40 | * the first n digits. If n is larger than or equal to the size 41 | * of the mixed-radix number, then return 0. 42 | * @param mixed A pointer to the mixed-radix number. 43 | * @param n The number of digits to drop, which is assumed to 44 | * be greater than or equal to 0. 45 | */ 46 | int mixed_radix_parent(mixed_radix_int_t* mixed, int n) { 47 | assert(mixed != NULL); 48 | assert(mixed->size > 0); 49 | assert(n >= 0); 50 | int result = 0; 51 | int factor = 1; 52 | for (int i = n; i < mixed->size; i++) { 53 | result += factor * mixed->digits[i]; 54 | factor *= mixed->radixes[i]; 55 | } 56 | return result; 57 | } 58 | 59 | /** 60 | * Return the int value of a mixed-radix number. 61 | * @param mixed A pointer to the mixed-radix number. 62 | */ 63 | int mixed_radix_to_int(mixed_radix_int_t* mixed) { return mixed_radix_parent(mixed, 0); } 64 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_zephyr_board_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_zephyr_board_support.h 3 | * @brief Provide preprocessor flags for the particular board that was chosen 4 | * 5 | * @author Erling Rennemo Jellum 6 | */ 7 | 8 | #ifndef LF_ZEPHYR_BOARD_SUPPORT_H 9 | #define LF_ZEPHYR_BOARD_SUPPORT_H 10 | 11 | // Default options 12 | #define LF_ZEPHYR_THREAD_PRIORITY_DEFAULT 5 13 | #define LF_ZEPHYR_STACK_SIZE_DEFAULT 2048 14 | 15 | #if defined(LF_ZEPHYR_CLOCK_COUNTER) 16 | #if defined(CONFIG_SOC_FAMILY_NRF) 17 | #define LF_TIMER DT_NODELABEL(timer1) 18 | #define LF_WAKEUP_OVERHEAD_US 100 19 | #define LF_MIN_SLEEP_US 10 20 | #define LF_RUNTIME_OVERHEAD_US 19 21 | #elif defined(CONFIG_BOARD_ATSAMD20_XPRO) 22 | #define LF_TIMER DT_NODELABEL(tc4) 23 | #elif defined(CONFIG_SOC_FAMILY_SAM) 24 | #define LF_TIMER DT_NODELABEL(tc0) 25 | #elif defined(CONFIG_COUNTER_MICROCHIP_MCP7940N) 26 | #define LF_TIMER DT_NODELABEL(extrtc0) 27 | #elif defined(CONFIG_COUNTER_RTC0) 28 | #define LF_TIMER DT_NODELABEL(rtc0) 29 | #elif defined(CONFIG_COUNTER_RTC_STM32) 30 | #define LF_TIMER DT_INST(0, st_stm32_rtc) 31 | #elif defined(CONFIG_COUNTER_XLNX_AXI_TIMER) 32 | #define LF_TIMER DT_INST(0, xlnx_xps_timer_1_00_a) 33 | #elif defined(CONFIG_COUNTER_TMR_ESP32) 34 | #define LF_TIMER DT_NODELABEL(timer0) 35 | #elif defined(CONFIG_COUNTER_MCUX_CTIMER) 36 | #define LF_TIMER DT_NODELABEL(ctimer0) 37 | #elif defined(CONFIG_SOC_MIMXRT1176_CM7) 38 | #define LF_TIMER DT_NODELABEL(gpt2) 39 | #else 40 | // This board does not have support for the counter clock. If the user 41 | // explicitly asked for this cock, then throw an error. 42 | #error "LF_ZEPHYR_CLOCK_COUNTER was requested but it is not supported by the board" 43 | #endif 44 | #ifndef LF_WAKEUP_OVERHEAD_US 45 | #define LF_WAKEUP_OVERHEAD_US 0 46 | #endif 47 | 48 | #ifndef LF_MIN_SLEEP_US 49 | #define LF_MIN_SLEEP_US 10 50 | #endif 51 | 52 | #ifndef LF_RUNTIME_OVERHEAD_US 53 | #define LF_RUNTIME_OVERHEAD_US 0 54 | #endif 55 | 56 | #ifndef LF_TIMER_ALARM_CHANNEL 57 | #define LF_TIMER_ALARM_CHANNEL 0 58 | #endif 59 | #endif 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /include/core/clock.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file clock.h 3 | * @brief A higher level API to the clock utilities provided by the platform API. 4 | * @ingroup Platform 5 | * 6 | * @author Erling Rennemo Jellum 7 | * 8 | * This builds on top of the clocking API of the different platforms and ensures: 9 | * 1. Monotonicity 10 | * 2. That clock synchronization offsets are applied and removed when necessary. 11 | */ 12 | 13 | #ifndef CLOCK_H 14 | #define CLOCK_H 15 | 16 | #include "low_level_platform.h" 17 | 18 | /** 19 | * @brief Block the calling thread until wakeup_time is reached or the thread is 20 | * interrupted by an asynchronous scheduling. 21 | * @ingroup Platform 22 | * 23 | * This is used by the single-threaded runtime. Before calling the appropriate 24 | * function in the platform API, the wakeup_time will be translated into the 25 | * correct timescale by removing any clock synchronization offset. 26 | * 27 | * @return 0 on success or -1 if interrupted. 28 | */ 29 | int lf_clock_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time); 30 | 31 | /** 32 | * @brief Retrieve the current physical time from the platform API. 33 | * @ingroup Platform 34 | * 35 | * This adds any clock synchronization offset and guarantees monotonicity. Specifically, 36 | * each returned value will be at least one nanosecond larger than any previously returned time. 37 | * 38 | * @param now A pointer to the location in which to store the result. 39 | * @return 0 on success, -1 on failure to read the platform clock. 40 | */ 41 | int lf_clock_gettime(instant_t* now); 42 | 43 | #if !defined(LF_SINGLE_THREADED) 44 | /** 45 | * @brief Block the calling thread on the condition variable until it is 46 | * signaled or until wakeup_time is reached. 47 | * @ingroup Platform 48 | * 49 | * Before calling the appropriate function in the platform API, the wakeup_time 50 | * will be translated into the correct timescale by removing any clock 51 | * synchronization offset. 52 | * 53 | * @return 0 on success, LF_TIMEOUT on timeout, platform-specific error 54 | * otherwise. 55 | */ 56 | int lf_clock_cond_timedwait(lf_cond_t* cond, instant_t wakeup_time); 57 | #endif 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /docs/markdown/1_landing_page.md: -------------------------------------------------------------------------------- 1 | \mainpage 2 | 3 | \note The reactor-c runtime system for Lingua Franca is the system used when you specify as your target `C`, `CCpp`, or `Python`. While it is theoretically possible to use reactor-c alone by writing applications in C, it is meant to be used via the [Lingua Franca coordination language](https://lf-lang.org) together with its code generator, `lfc`. 4 | 5 | The [documentation here](https://lf-lang.org/reactor-c) is generated automatically from the C source code using [Doxygen](https://doxygen.nl). 6 | Higher-level user documentation and getting-started information can be found on the 7 | [lf-lang.org website](https://lf-lang.org/docs/next?target-languages=c), 8 | with details about the C runtime on the 9 | [target language details](https://lf-lang.org/docs/next/reference/target-language-details?target-languages=c) 10 | page. 11 | 12 | \note The most useful entry point to this documentation is likely the [topics page](topics.html). The [API](group__API.html) subpage describes the API used in writing reactors. The [Utilities](group__Utilities.html) subpage describes various utilities that may help with writing reactors. 13 | 14 | ## Overview 15 | 16 | When you specify 17 | 18 | ``` 19 | target C 20 | ``` 21 | then a C compiler (such as `gcc`) will be used. 22 | If you specify 23 | 24 | ``` 25 | target CCpp 26 | ``` 27 | then a C++ compiler (such as `g++`) will be used. 28 | In this case, your reaction bodies can written in C++ and you can include and link to C++ code. 29 | Unlike the Cpp target, you will still access the LF API through the C API documented here. 30 | 31 | When you specify 32 | 33 | ``` 34 | target Python 35 | ``` 36 | then `lfc` will generate a Python program that uses this reactor-c runtime under the hood. 37 | 38 | ## Supported Platforms 39 | 40 | The C, CCpp, and Python targets are tested nightly on Linux, macOS, and Windows (with WSL). The federated programs are tested on Linux and macOS only. 41 | 42 | For embedded platforms, there is support for C on Arduino and Zephyr, plus bare-metal (unthreaded) execution on the Raspberry Pi RP2040, nRF52, FlexPRET, and Patmos. For bare-metal, Zephyr, and RIoT, consider using the [reactor-uc](https://github.com/lf-lang/reactor-uc) target instead. 43 | 44 | 45 | -------------------------------------------------------------------------------- /core/utils/lf_semaphore.c: -------------------------------------------------------------------------------- 1 | #if !defined(LF_SINGLE_THREADED) 2 | /** 3 | * @brief Semaphore utility for reactor C. 4 | * 5 | * @author Soroush Bateni 6 | */ 7 | 8 | #include "lf_semaphore.h" 9 | #include 10 | #include "util.h" // Defines macros LF_MUTEX_LOCK, etc. 11 | 12 | /** 13 | * @brief Create a new semaphore. 14 | * 15 | * @param count The count to start with. 16 | * @return lf_semaphore_t* Can be NULL on error. 17 | */ 18 | lf_semaphore_t* lf_semaphore_new(size_t count) { 19 | lf_semaphore_t* semaphore = (lf_semaphore_t*)malloc(sizeof(lf_semaphore_t)); 20 | LF_MUTEX_INIT(&semaphore->mutex); 21 | LF_COND_INIT(&semaphore->cond, &semaphore->mutex); 22 | semaphore->count = count; 23 | return semaphore; 24 | } 25 | 26 | /** 27 | * @brief Release the 'semaphore' and add 'i' to its count. 28 | * 29 | * @param semaphore Instance of a semaphore 30 | * @param i The count to add. 31 | */ 32 | void lf_semaphore_release(lf_semaphore_t* semaphore, size_t i) { 33 | assert(semaphore != NULL); 34 | LF_MUTEX_LOCK(&semaphore->mutex); 35 | semaphore->count += i; 36 | lf_cond_broadcast(&semaphore->cond); 37 | LF_MUTEX_UNLOCK(&semaphore->mutex); 38 | } 39 | 40 | /** 41 | * @brief Acquire the 'semaphore'. Will block if count is 0. 42 | * 43 | * @param semaphore Instance of a semaphore. 44 | */ 45 | void lf_semaphore_acquire(lf_semaphore_t* semaphore) { 46 | assert(semaphore != NULL); 47 | LF_MUTEX_LOCK(&semaphore->mutex); 48 | while (semaphore->count == 0) { 49 | lf_cond_wait(&semaphore->cond); 50 | } 51 | semaphore->count--; 52 | LF_MUTEX_UNLOCK(&semaphore->mutex); 53 | } 54 | 55 | /** 56 | * @brief Wait on the 'semaphore' if count is 0. 57 | * 58 | * @param semaphore Instance of a semaphore. 59 | */ 60 | void lf_semaphore_wait(lf_semaphore_t* semaphore) { 61 | assert(semaphore != NULL); 62 | LF_MUTEX_LOCK(&semaphore->mutex); 63 | while (semaphore->count == 0) { 64 | lf_cond_wait(&semaphore->cond); 65 | } 66 | LF_MUTEX_UNLOCK(&semaphore->mutex); 67 | } 68 | 69 | /** 70 | * @brief Destroy the 'semaphore'. 71 | * 72 | * @param semaphore Instance of a semaphore. 73 | */ 74 | void lf_semaphore_destroy(lf_semaphore_t* semaphore) { 75 | assert(semaphore != NULL); 76 | free(semaphore); 77 | } 78 | #endif 79 | -------------------------------------------------------------------------------- /core/threaded/scheduler_sync_tag_advance.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Soroush Bateni 4 | * @author Edward A. Lee 5 | * @author Marten Lohstroh 6 | * 7 | * @brief API used to advance tag globally. 8 | */ 9 | 10 | #if !defined(LF_SINGLE_THREADED) 11 | 12 | #include "scheduler_sync_tag_advance.h" 13 | #include "rti_local.h" 14 | #include "environment.h" 15 | #include "tracepoint.h" 16 | #include "util.h" 17 | 18 | // Forward declaration of function defined in reactor_threaded.h 19 | void _lf_next_locked(struct environment_t* env); 20 | 21 | /** 22 | * @brief Indicator that execution of the specified tag has completed. 23 | */ 24 | static tag_t _latest_tag_completed = NEVER_TAG_INITIALIZER; 25 | 26 | bool should_stop_locked(lf_scheduler_t* sched) { 27 | // If this is not the very first step, check against the stop tag to see whether this is the last step. 28 | // Also, stop only after completing the stop tag. 29 | if (lf_tag_compare(_latest_tag_completed, sched->env->current_tag) == 0) { 30 | // If we are at the stop tag, do not call _lf_next_locked() 31 | // to prevent advancing the logical time. 32 | if (lf_tag_compare(sched->env->current_tag, sched->env->stop_tag) >= 0) { 33 | LF_PRINT_DEBUG("****************** Stopping execution at tag " PRINTF_TAG, 34 | sched->env->current_tag.time - lf_time_start(), sched->env->current_tag.microstep); 35 | return true; 36 | } 37 | } 38 | return false; 39 | } 40 | 41 | bool _lf_sched_advance_tag_locked(lf_scheduler_t* sched) { 42 | environment_t* env = sched->env; 43 | logical_tag_complete(env->current_tag); 44 | 45 | // If we are using scheduling enclaves. Notify the local RTI of the time 46 | // advancement. 47 | #if defined LF_ENCLAVES 48 | rti_logical_tag_complete_locked(env->enclave_info, env->current_tag); 49 | #endif 50 | 51 | if (should_stop_locked(sched)) { 52 | return true; 53 | } 54 | 55 | _latest_tag_completed = env->current_tag; 56 | 57 | // Advance time. 58 | // _lf_next_locked() may block waiting for real time to pass or events to appear on the event queue. 59 | tracepoint_scheduler_advancing_time_starts(env); 60 | _lf_next_locked(env); 61 | tracepoint_scheduler_advancing_time_ends(env); 62 | 63 | LF_PRINT_DEBUG("Scheduler: Done waiting for _lf_next_locked()."); 64 | return false; 65 | } 66 | #endif 67 | -------------------------------------------------------------------------------- /include/core/utils/pqueue.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file pqueue.h 3 | * @author Marten Lohstroh 4 | * @author Edward A. Lee 5 | * @author Byeonggil Jun 6 | * 7 | * @brief Priority queue definitions for queues where the priority is a number that can be compared with ordinary 8 | * numerical comparisons. 9 | * 10 | * @ingroup Internal 11 | * 12 | * This is used for the reaction queue. The event queue uses a `tag_t` struct for its priority, so it cannot use this. 13 | */ 14 | 15 | #ifndef PQUEUE_H 16 | #define PQUEUE_H 17 | 18 | #include "pqueue_base.h" 19 | 20 | /** 21 | * @brief Return 1 if the first argument is greater than the second and zero otherwise. 22 | * @ingroup Internal 23 | * @param thiz First argument. 24 | * @param that Second argument. 25 | */ 26 | int in_reverse_order(pqueue_pri_t thiz, pqueue_pri_t that); 27 | 28 | /** 29 | * @brief Return 0 regardless of argument order. 30 | * @ingroup Internal 31 | * @param thiz First argument. 32 | * @param that Second argument. 33 | */ 34 | int in_no_particular_order(pqueue_pri_t thiz, pqueue_pri_t that); 35 | 36 | /** 37 | * @brief Return 1 if the two arguments are identical pointers. 38 | * @ingroup Internal 39 | * @param a First argument. 40 | * @param b Second argument. 41 | */ 42 | int reaction_matches(void* a, void* b); 43 | 44 | /** 45 | * @brief Report a priority equal to the index of the given reaction. 46 | * @ingroup Internal 47 | * Used for sorting pointers to reaction_t structs in the blocked and executing queues. 48 | * @param reaction A pointer to a reaction_t. 49 | */ 50 | pqueue_pri_t get_reaction_index(void* reaction); 51 | 52 | /** 53 | * @brief Return the given reaction's position in the queue. 54 | * @ingroup Internal 55 | * @param reaction A pointer to a reaction_t. 56 | */ 57 | size_t get_reaction_position(void* reaction); 58 | 59 | /** 60 | * @brief Set the given reaction's position in the queue. 61 | * @ingroup Internal 62 | * @param reaction A pointer to a reaction_t. 63 | * @param pos The position. 64 | */ 65 | void set_reaction_position(void* reaction, size_t pos); 66 | 67 | /** 68 | * @brief Print some information about the given reaction. 69 | * @ingroup Internal 70 | * This only prints something if logging is set to DEBUG. 71 | * @param reaction A pointer to a reaction_t. 72 | */ 73 | void print_reaction(void* reaction); 74 | 75 | #endif /* PQUEUE_H */ 76 | -------------------------------------------------------------------------------- /python/include/python_action.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Soroush Bateni 4 | * @author Hou Seng Wong 5 | * 6 | * @brief Data structures for Python actions. 7 | */ 8 | 9 | #ifndef PYTHON_ACTION_H 10 | #define PYTHON_ACTION_H 11 | 12 | #include "pythontarget.h" 13 | #include 14 | #include 15 | #include "python_capsule_extension.h" 16 | #include "lf_types.h" 17 | 18 | extern PyTypeObject py_action_capsule_t; 19 | 20 | /** 21 | * The struct used to instantiate an action. 22 | * This is used 23 | * in the PythonGenerator instead of redefining 24 | * a struct for each action. 25 | * This can be used for any Python object, 26 | * including lists and tuples. 27 | * PyObject* value: the value of the action with the generic Python type 28 | * is_present: indicates if the action is present 29 | * at the current logical time 30 | **/ 31 | typedef struct { 32 | token_type_t type; 33 | lf_token_t* token; 34 | size_t length; 35 | bool is_present; 36 | lf_action_internal_t _base; 37 | self_base_t* parent; 38 | bool has_value; 39 | int source_id; 40 | PyObject* value; 41 | FEDERATED_GENERIC_EXTENSION 42 | } generic_action_instance_struct; 43 | 44 | /** 45 | * The struct used to hold an action 46 | * that is sent to a Python reaction. 47 | * 48 | * The "action" field holds a PyCapsule of the 49 | * void * pointer to an action. 50 | * 51 | * The "value" field holds the action value 52 | * if anything is given. This value is copied over 53 | * from action->value each time an action is passed 54 | * to a Python reaction. 55 | * 56 | * The "is_present" field is copied over 57 | * from action->value each time an action is passed 58 | * to a Python reaction. 59 | **/ 60 | typedef struct { 61 | PyObject_HEAD PyObject* 62 | action; // Hold the void* pointer to a C action instance. However, passing void* directly 63 | // to Python is considered unsafe practice. Instead, this void* pointer to the C action 64 | // will be stored in a PyCapsule. @see https://docs.python.org/3/c-api/capsule.html 65 | PyObject* value; // This value will be copied from the C action->value 66 | bool is_present; // Same as value, is_present will be copied from the C action->is_present 67 | FEDERATED_CAPSULE_EXTENSION 68 | } generic_action_capsule_struct; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /python/lib/python_time.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Soroush Bateni 4 | * @author Hou Seng Wong 5 | * 6 | * @brief Implementation of time-related functions. 7 | */ 8 | #include 9 | #include 10 | 11 | #include "python_port.h" 12 | #include "tag.h" 13 | 14 | ///////// Time-keeping functions ////////// 15 | /** 16 | * Return the logical time in nanoseconds. 17 | */ 18 | PyObject* py_lf_time_logical(PyObject* self, PyObject* args) { 19 | return PyLong_FromLongLong(lf_time_logical(top_level_environment)); 20 | } 21 | 22 | /** 23 | * Return the elapsed logical time in nanoseconds. 24 | */ 25 | PyObject* py_lf_time_logical_elapsed(PyObject* self, PyObject* args) { 26 | return PyLong_FromLongLong(lf_time_logical_elapsed(top_level_environment)); 27 | } 28 | 29 | /** 30 | * Return the physical time in nanoseconds. 31 | */ 32 | PyObject* py_lf_time_physical(PyObject* self, PyObject* args) { return PyLong_FromLongLong(lf_time_physical()); } 33 | 34 | /** 35 | * Return the elapsed physical time in nanoseconds. 36 | */ 37 | PyObject* py_lf_time_physical_elapsed(PyObject* self, PyObject* args) { 38 | return PyLong_FromLongLong(lf_time_physical_elapsed()); 39 | } 40 | 41 | /** 42 | * Return the start time in nanoseconds. 43 | */ 44 | PyObject* py_lf_time_start(PyObject* self, PyObject* args) { return PyLong_FromLongLong(lf_time_start()); } 45 | 46 | PyTypeObject PyTimeType; 47 | 48 | PyMethodDef PyTimeTypeMethods[] = { 49 | {"logical", (PyCFunction)py_lf_time_logical, METH_NOARGS | METH_STATIC, "Get the current logical time."}, 50 | {"logical_elapsed", (PyCFunction)py_lf_time_logical_elapsed, METH_NOARGS | METH_STATIC, 51 | "Get the current elapsed logical time"}, 52 | {"physical", (PyCFunction)py_lf_time_physical, METH_NOARGS | METH_STATIC, "Get the current physical time"}, 53 | {"physical_elapsed", (PyCFunction)py_lf_time_physical_elapsed, METH_NOARGS | METH_STATIC, 54 | "Get the current elapsed physical time"}, 55 | {"start", (PyCFunction)py_lf_time_start, METH_NOARGS | METH_STATIC, "Get the start time"}, 56 | {NULL} /* Sentinel */ 57 | }; 58 | 59 | /** 60 | * Definition of the PyTimeType Object. 61 | **/ 62 | PyTypeObject PyTimeType = { 63 | PyVarObject_HEAD_INIT(NULL, 0).tp_name = "LinguaFranca.TimeType", 64 | .tp_doc = "Time object", 65 | .tp_basicsize = 0, 66 | .tp_itemsize = 0, 67 | .tp_flags = Py_TPFLAGS_DEFAULT, 68 | .tp_new = PyType_GenericNew, 69 | .tp_methods = PyTimeTypeMethods, 70 | }; 71 | -------------------------------------------------------------------------------- /util/wave_file_reader.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wave_file_reader.h 3 | * @author Edward A. Lee 4 | * 5 | * @brief Utility function for reading WAV audio files. 6 | * @ingroup Utilities 7 | * 8 | * This defines functions and data types for importing audio files with the 9 | * wave audio format. The main function is read_wave_file(), which, given 10 | * a path to a .wav file, reads the file and, if the format of the file is 11 | * supported, returns an lf_waveform_t struct, which contains the raw 12 | * audio data in 16-bit linear PCM form. 13 | * 14 | * This code has few dependencies, so it should run on just about any platform. 15 | * 16 | * To use this, include the following flags in your target properties: 17 | * 18 | * ``` 19 | * target C { 20 | * files: [ 21 | * "/lib/c/reactor-c/util/wave_file_reader.c", 22 | * "/lib/c/reactor-c/util/wave_file_reader.h" 23 | * ], 24 | * cmake-include: [ 25 | * "/lib/c/reactor-c/util/wave_file_reader.cmake" 26 | * ] 27 | * } 28 | * ``` 29 | * 30 | * In addition, you need this in your Lingua Franca file or reactor: 31 | * 32 | * ``` 33 | * preamble {= 34 | * #include "wave_file_reader.h" 35 | * =} 36 | * ``` 37 | */ 38 | 39 | #ifndef WAVE_FILE_READER_H 40 | #define WAVE_FILE_READER_H 41 | 42 | /** 43 | * @brief Waveform in 16-bit linear-PCM format. 44 | * @ingroup Utilities 45 | * 46 | * The waveform element is an array containing audio samples. 47 | * If there are two channels, then they are interleaved left and right channel. 48 | * The length is the total number of samples, a multiple of the number of channels. 49 | */ 50 | typedef struct lf_waveform_t { 51 | uint32_t length; 52 | uint16_t num_channels; 53 | int16_t* waveform; 54 | } lf_waveform_t; 55 | 56 | /** 57 | * @brief Open a wave file, check that the format is supported, allocate memory for the sample data, 58 | * and fill the memory with the sample data. 59 | * @ingroup Utilities 60 | * 61 | * It is up to the caller to free the memory when done with it. 62 | * That code should first free the waveform element of the returned struct, then the struct itself. 63 | * This implementation supports only 16-bit linear PCM files. 64 | * On a Mac, you can convert audio files into this format using the afconvert utility. 65 | * 66 | * @param path The path to the file. 67 | * @return An array of sample data or NULL if the file can't be opened 68 | * or has an usupported format. 69 | */ 70 | lf_waveform_t* read_wave_file(const char* path); 71 | 72 | #endif // WAVE_FILE_READER_H -------------------------------------------------------------------------------- /util/tracing/visualization/README.md: -------------------------------------------------------------------------------- 1 | # Trace sequence diagram visualizer 2 | 3 | `fedsd` is a utility that reports the interactions (exchanged messages) 4 | between federates and the RTI in a sequence-diagram-like format. 5 | It also reports the interactions between different encalves in an enclaved execution, where no RTI trace file is provided. 6 | 7 | 8 | To use `fedsd`, you need to first obtain an execution trace. To do this, enable the tracing mechanism in your Lingua Franca program by setting the `tracing` target property to `true` and then compile and run the program. 9 | 10 | This utility starts by transforming each `.lft` file into a `.csv` file, by 11 | internally running `trace_to_csv`. It then aggregates the data from all `.csv` 12 | files to do the matching and draw the sequence diagram. 13 | 14 | # Installing 15 | `fedsd` is installed together with the rest of the tracing tools. For instructions refer to `util/tracing/README.md`. 16 | 17 | 18 | # Running 19 | 20 | In case the federation is launched using the `bash` script under `bin`, an `.lft` trace 21 | file will be generated for each of the federates, in addition to `rti.lft`. The latter 22 | contains the RTI trace. 23 | 24 | If, however, the federation is launched manually, then running the `RTI` command should be passed the `-t` flag in order to make sure that it, too, has tracing enabled: 25 | ```bash 26 | RTI -n -t 27 | ``` 28 | 29 | It is most convenient to launch the RTI and all federates from the same working directory so that they will all write their trace file to that directory. 30 | 31 | Once the federation stopped executing, running `fedsd` will operate on all the `.lft` files in the current directory: 32 | ```bash 33 | fedsd 34 | ``` 35 | It is also possible to operate on specific files. In such a case, run `fedsd` with `-r` flag to provide the RTI trace file, and `-f` flag to provide the list of federate. 36 | 37 | ```bash 38 | fedsd -r -f 39 | ``` 40 | 41 | If the trace is too long, the target time interval can be specified. Running `fedsd` with `-s ` will show the messages with the tag later than or equal to the start time and with `-e ` will show the messages with the tag strictly earlier than the end_time. 42 | 43 | ```bash 44 | fedsd -s -e 45 | ``` 46 | 47 | The output is an html file named `trace_svg.html` (in the current directory) that contains the sequence of interactions between the federates and the RTI. 48 | -------------------------------------------------------------------------------- /core/utils/hashset/hashset_itr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hashset_itr.c 3 | * 4 | * Copyright 2012 Couchbase, Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * In Nov. 2022, Edward A. Lee fixed bug where next could advance to a removed item. 19 | * Also, changed the logic of the API a bit as inidicated in the .h file. 20 | */ 21 | 22 | #include 23 | #include 24 | #include "hashset/hashset_itr.h" 25 | 26 | hashset_itr_t hashset_iterator(hashset_t set) { 27 | hashset_itr_t itr = calloc(1, sizeof(struct hashset_itr_st)); 28 | if (itr == NULL) { 29 | return NULL; 30 | } 31 | itr->set = set; 32 | itr->index = -1; 33 | 34 | return itr; 35 | } 36 | 37 | int hashset_iterator_has_next(hashset_itr_t itr) { 38 | assert(itr != NULL); 39 | size_t index = itr->index + 1; 40 | 41 | /* empty or end of the set */ 42 | if (itr->set->nitems == 0 || index == itr->set->capacity) { 43 | return 0; 44 | } 45 | /* peek to find another entry */ 46 | while (index < itr->set->capacity) { 47 | void* value = itr->set->items[index++]; 48 | if (value != 0 && value != (void*)1) 49 | return 1; 50 | } 51 | 52 | /* Otherwise */ 53 | return 0; 54 | } 55 | 56 | int hashset_iterator_next(hashset_itr_t itr) { 57 | assert(itr != NULL); 58 | 59 | size_t index = itr->index + 1; 60 | 61 | /* empty or end of the set */ 62 | if (itr->set->nitems == 0 || index == itr->set->capacity) { 63 | return -1; 64 | } 65 | 66 | while (index < itr->set->capacity) { 67 | if (itr->set->items[index] != 0 && itr->set->items[index] != (void*)1) { 68 | // Found one. 69 | itr->index = (int)index; 70 | return index; 71 | } 72 | index++; 73 | } 74 | 75 | return -1; 76 | } 77 | 78 | void* hashset_iterator_value(hashset_itr_t itr) { 79 | 80 | // Check that hashset_iterator_next() has been called. 81 | assert(itr->index >= 0 && itr->set->items[itr->index] != 0 && itr->set->items[itr->index] != (void*)1); 82 | 83 | return itr->set->items[itr->index]; 84 | } 85 | -------------------------------------------------------------------------------- /include/core/utils/lf_semaphore.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_semaphore.h 3 | * @author Soroush Bateni 4 | * 5 | * @brief Semaphore utility for reactor C. 6 | * @ingroup Internal 7 | */ 8 | 9 | #ifndef LF_SEMAPHORE_H 10 | #define LF_SEMAPHORE_H 11 | 12 | #ifndef NUMBER_OF_WORKERS 13 | #define NUMBER_OF_WORKERS 1 14 | #endif // NUMBER_OF_WORKERS 15 | 16 | #include "low_level_platform.h" 17 | #include 18 | 19 | /** 20 | * @brief A semaphore. 21 | * @ingroup Internal 22 | * 23 | * A semaphore is a synchronization primitive that maintains a count. 24 | * The count is decremented by acquire operations and incremented by release operations. 25 | * If the count would become negative, the acquire operation blocks until the count 26 | * becomes positive again. 27 | */ 28 | typedef struct { 29 | /** 30 | * @brief The current count of the semaphore. 31 | * This value is protected by the mutex and can be modified 32 | * only while holding the mutex lock. 33 | */ 34 | size_t count; 35 | 36 | /** 37 | * @brief Mutex used to protect access to the count. 38 | * Ensures that count modifications are atomic and 39 | * coordinates access between multiple threads. 40 | */ 41 | lf_mutex_t mutex; 42 | 43 | /** 44 | * @brief Condition variable used for blocking operations. 45 | * Threads waiting for the semaphore to become available 46 | * block on this condition variable. 47 | */ 48 | lf_cond_t cond; 49 | } lf_semaphore_t; 50 | 51 | /** 52 | * @brief Create a new semaphore. 53 | * @ingroup Internal 54 | * 55 | * @param count The count to start with. 56 | * @return lf_semaphore_t* Can be NULL on error. 57 | */ 58 | lf_semaphore_t* lf_semaphore_new(size_t count); 59 | 60 | /** 61 | * @brief Release the 'semaphore' and add 'i' to its count. 62 | * @ingroup Internal 63 | * 64 | * @param semaphore Instance of a semaphore 65 | * @param i The count to add. 66 | */ 67 | void lf_semaphore_release(lf_semaphore_t* semaphore, size_t i); 68 | 69 | /** 70 | * @brief Acquire the 'semaphore'. Will block if count is 0. 71 | * @ingroup Internal 72 | * 73 | * @param semaphore Instance of a semaphore. 74 | */ 75 | void lf_semaphore_acquire(lf_semaphore_t* semaphore); 76 | 77 | /** 78 | * @brief Wait on the 'semaphore' if count is 0. 79 | * @ingroup Internal 80 | * 81 | * @param semaphore Instance of a semaphore. 82 | */ 83 | void lf_semaphore_wait(lf_semaphore_t* semaphore); 84 | 85 | /** 86 | * @brief Destroy the 'semaphore'. 87 | * @ingroup Internal 88 | * 89 | * @param semaphore Instance of a semaphore. 90 | */ 91 | void lf_semaphore_destroy(lf_semaphore_t* semaphore); 92 | 93 | #endif // LF_SEMAPHORE_H 94 | -------------------------------------------------------------------------------- /docs/assets/doxygen-awesome-paragraph-link.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2022 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | class DoxygenAwesomeParagraphLink { 31 | // Icon from https://fonts.google.com/icons 32 | // Licensed under the Apache 2.0 license: 33 | // https://www.apache.org/licenses/LICENSE-2.0.html 34 | static icon = `` 35 | static title = "Permanent Link" 36 | static init() { 37 | $(function() { 38 | $(document).ready(function() { 39 | document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => { 40 | let anchorlink = document.createElement("a") 41 | anchorlink.setAttribute("href", `#${node.getAttribute("id")}`) 42 | anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title) 43 | anchorlink.classList.add("anchorlink") 44 | node.classList.add("anchor") 45 | anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon 46 | node.parentElement.appendChild(anchorlink) 47 | }) 48 | }) 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/general/utils/hashmap_test.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "core/utils/impl/pointer_hashmap.h" 5 | #include "rand_utils.h" 6 | #include "core/utils/util.h" 7 | 8 | #define CAPACITY 100 9 | #define N 5000 10 | #define RANDOM_SEED 1830 11 | 12 | static int distribution[2] = {50, 50}; 13 | 14 | static hashmap_object2int_entry_t mock[CAPACITY]; 15 | static size_t mock_size = 0; 16 | 17 | void test_put(hashmap_object2int_t* h) { 18 | int* key = NULL; 19 | while (!key) 20 | key += (rand() % CAPACITY); // Generate a dummy pointer. 21 | int value = rand(); 22 | hashmap_object2int_entry_t entry = (hashmap_object2int_entry_t){.key = key, .value = value}; 23 | hashmap_object2int_put(h, entry.key, entry.value); 24 | // printf("Putting (%p, %d).\n", entry.key, entry.value); 25 | mock[mock_size++] = entry; 26 | } 27 | 28 | void test_get(hashmap_object2int_t* h) { 29 | if (!mock_size) 30 | return; 31 | size_t r = rand() % mock_size; 32 | hashmap_object2int_entry_t desired = mock[r]; 33 | int found = hashmap_object2int_get(h, desired.key); 34 | // printf("Getting (%p, %d) from %d.\n", desired.key, desired.value, r); 35 | if (desired.value != found) { 36 | // It is possible that two distinct values were associated with the same key. Search the 37 | // "mock" array to check if this is the case. 38 | for (int i = (int)mock_size - 1; i >= 0; i--) { 39 | if (mock[i].key == desired.key) { 40 | if (mock[i].value == found) 41 | return; // Everything is OK. 42 | break; 43 | } 44 | } 45 | lf_print_error_and_exit("Expected %d but got %d when getting from a hashmap.\n", desired.value, found); 46 | } 47 | } 48 | 49 | /** 50 | * @brief Run a randomly selected test on `h`. 51 | * 52 | * @param h A hashmap. 53 | * @param distribution The desired probability distribution with 54 | * which each of two actions are performed, expressed as percents. 55 | */ 56 | void run_test(hashmap_object2int_t* h, int* distribution) { 57 | int r = rand(); 58 | int choice = (r < 0 ? -r : r) % 100; 59 | if ((choice = choice - distribution[0]) < 0) { 60 | test_put(h); 61 | } else { 62 | test_get(h); 63 | } 64 | } 65 | 66 | int main() { 67 | srand(RANDOM_SEED); 68 | for (int i = 0; i < N; i++) { 69 | int perturbed[2]; 70 | perturb(distribution, 2, perturbed); 71 | LF_PRINT_DEBUG("Distribution: %d, %d", perturbed[0], perturbed[1]); 72 | hashmap_object2int_t* h = hashmap_object2int_new(CAPACITY, NULL); 73 | int j = rand() % (CAPACITY / 2); 74 | while (j--) { 75 | run_test(h, perturbed); 76 | } 77 | hashmap_object2int_free(h); 78 | mock_size = 0; 79 | } 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /test/scheduling/scheduling_api_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * This tests the real-time scheduling API implementation in Linux. 3 | */ 4 | #include 5 | #include 6 | #include "core/utils/util.h" 7 | #include "low_level_platform.h" 8 | 9 | #if !defined PLATFORM_Linux 10 | #error scheduling_api_test.c should only be compiled on Linux 11 | #endif 12 | 13 | int main() { 14 | int res; 15 | 16 | // Set the CPU Set of the current thread. 17 | res = lf_thread_set_cpu(lf_thread_self(), lf_available_cores() - 1); 18 | if (res != 0) { 19 | lf_print_error_and_exit("lf_thread_set_cpu failed with %d", res); 20 | } 21 | 22 | // Configure SCHED_FIFO 23 | { 24 | lf_scheduling_policy_t cfg; 25 | cfg.policy = LF_SCHED_PRIORITY; 26 | cfg.priority = 99; 27 | cfg.time_slice = 0; 28 | res = lf_thread_set_scheduling_policy(lf_thread_self(), &cfg); 29 | if (res != 0) { 30 | lf_print_error_and_exit("lf_thread_set_scheduling_policy FIFO failed with %d", res); 31 | } 32 | } 33 | 34 | // Configure SCHED_RR 35 | { 36 | lf_scheduling_policy_t cfg; 37 | cfg.policy = LF_SCHED_TIMESLICE; 38 | cfg.priority = 99; 39 | cfg.time_slice = 0; 40 | res = lf_thread_set_scheduling_policy(lf_thread_self(), &cfg); 41 | if (res != 0) { 42 | lf_print_error_and_exit("lf_thread_set_scheduling_policy RR failed with %d", res); 43 | } 44 | } 45 | 46 | // Try illegal priority 47 | { 48 | lf_scheduling_policy_t cfg; 49 | cfg.policy = LF_SCHED_TIMESLICE; 50 | cfg.time_slice = 0; 51 | cfg.priority = 10000; 52 | res = lf_thread_set_scheduling_policy(lf_thread_self(), &cfg); 53 | if (res == 0) { 54 | lf_print_error_and_exit("lf_thread_set_scheduling_policy should have failed with illegal priority"); 55 | } 56 | } 57 | 58 | // Set the priority 59 | res = lf_thread_set_priority(lf_thread_self(), 50); 60 | if (res != 0) { 61 | lf_print_error_and_exit("lf_thread_set_priority failed with %d", res); 62 | } 63 | 64 | // Try negative priority 65 | res = lf_thread_set_priority(lf_thread_self(), -50); 66 | if (res == 0) { 67 | lf_print_error_and_exit("lf_thread_set_priority should have failed for -50"); 68 | } 69 | 70 | // Configure back to SCHED_OTHER 71 | { 72 | lf_scheduling_policy_t cfg; 73 | cfg.policy = LF_SCHED_FAIR; 74 | res = lf_thread_set_scheduling_policy(lf_thread_self(), &cfg); 75 | if (res != 0) { 76 | lf_print_error_and_exit("lf_thread_set_scheduling_policy RR failed with %d", res); 77 | } 78 | } 79 | 80 | // Try pinning to non-existant CPU core. 81 | res = lf_thread_set_cpu(lf_thread_self(), lf_available_cores()); 82 | if (res == 0) { 83 | lf_print_error_and_exit("lf_thread_set_cpu should fail for too high CPU id"); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_atomic_irq.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Erling Rennemo Jellum 4 | * @brief Implements the atomics API by disabling interrupts. Typically used for platforms that 5 | * do not support atomic operations. 6 | * 7 | * The platforms need to implement `lf_enable_interrupts_nested` 8 | * and `lf_disable_interrupts_nested`. 9 | */ 10 | #if defined(PLATFORM_ARDUINO) || defined(PLATFORM_NRF52) || defined(PLATFORM_ZEPHYR) || defined(PLATFORM_RP2040) || \ 11 | defined(PLATFORM_FLEXPRET) || defined(PLATFORM_PATMOS) 12 | 13 | #include "platform/lf_atomic.h" 14 | #include "low_level_platform.h" 15 | 16 | // Forward declare the functions for enabling/disabling interrupts. Must be 17 | // implemented in the platform support file of the target. 18 | int lf_disable_interrupts_nested(); 19 | int lf_enable_interrupts_nested(); 20 | 21 | int lf_atomic_fetch_add(int* ptr, int value) { 22 | lf_disable_interrupts_nested(); 23 | int res = *ptr; 24 | *ptr += value; 25 | lf_enable_interrupts_nested(); 26 | return res; 27 | } 28 | 29 | int64_t lf_atomic_fetch_add64(int64_t* ptr, int64_t value) { 30 | lf_disable_interrupts_nested(); 31 | int64_t res = *ptr; 32 | *ptr += value; 33 | lf_enable_interrupts_nested(); 34 | return res; 35 | } 36 | 37 | int lf_atomic_add_fetch(int* ptr, int value) { 38 | lf_disable_interrupts_nested(); 39 | int res = *ptr + value; 40 | *ptr = res; 41 | lf_enable_interrupts_nested(); 42 | return res; 43 | } 44 | 45 | int64_t lf_atomic_add_fetch64(int64_t* ptr, int64_t value) { 46 | lf_disable_interrupts_nested(); 47 | int64_t res = *ptr + value; 48 | *ptr = res; 49 | lf_enable_interrupts_nested(); 50 | return res; 51 | } 52 | 53 | bool lf_atomic_bool_compare_and_swap(int* ptr, int oldval, int newval) { 54 | lf_disable_interrupts_nested(); 55 | bool res = false; 56 | if ((*ptr) == oldval) { 57 | *ptr = newval; 58 | res = true; 59 | } 60 | lf_enable_interrupts_nested(); 61 | return res; 62 | } 63 | 64 | bool lf_atomic_bool_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval) { 65 | lf_disable_interrupts_nested(); 66 | bool res = false; 67 | if ((*ptr) == oldval) { 68 | *ptr = newval; 69 | res = true; 70 | } 71 | lf_enable_interrupts_nested(); 72 | return res; 73 | } 74 | 75 | int lf_atomic_val_compare_and_swap(int* ptr, int oldval, int newval) { 76 | lf_disable_interrupts_nested(); 77 | int res = *ptr; 78 | if ((*ptr) == oldval) { 79 | *ptr = newval; 80 | } 81 | lf_enable_interrupts_nested(); 82 | return res; 83 | } 84 | 85 | int64_t lf_atomic_val_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval) { 86 | lf_disable_interrupts_nested(); 87 | int64_t res = *ptr; 88 | if ((*ptr) == oldval) { 89 | *ptr = newval; 90 | } 91 | lf_enable_interrupts_nested(); 92 | return res; 93 | } 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /docs/doxygen.h: -------------------------------------------------------------------------------- 1 | // Doxygen group definitions 2 | 3 | /** 4 | * @defgroup API API for Reactions 5 | * @brief API for use in inline reaction bodies. 6 | * 7 | * These macros and functions are automatically available in any reaction body that is inlined 8 | * inside the `.lf` file; there is no need to add any `#include`. 9 | * 10 | * \note 11 | * For reactions that are defined in ordinary C files instead of inline in the `.lf` file, 12 | * (see [Reaction Declarations](http://lf-lang.org/docs/next/writing-reactors/reaction-declarations)), 13 | * you may need to use a function directly instead of a macro. 14 | * The corresponding very likely takes an `environment` argument. 15 | * The self struct struct has an `environment` field, so you can pass as the argument `self->environment`. 16 | */ 17 | 18 | /** 19 | * @defgroup Constants Constants 20 | * @brief Constants provided for convenience and readability. 21 | * 22 | * These macros are provided for convenience and readability. 23 | */ 24 | 25 | /** 26 | * @defgroup Utilities Utilities 27 | * @brief Useful functions for application developers. 28 | * 29 | * This collection of functions provides commonly used functionality that is not part of the core API. 30 | */ 31 | 32 | /** 33 | * @defgroup Platform Platform API 34 | * @brief API for functions that have platform-specific implementations. 35 | * 36 | * These functions are not meant to be used directly by users, but are useful for developers. 37 | * They are used to implement the platform-independent API for specific platforms. 38 | */ 39 | 40 | /** 41 | * @defgroup Internal Internal 42 | * @brief API mainly used internally, but occasionally useful for users. 43 | * 44 | * These functions and types are not meant to be used directly by users, but are useful for developers. 45 | */ 46 | 47 | /** 48 | * @defgroup Modal Modal 49 | * @brief API used internally to support modal reactors. 50 | * 51 | * These functions and types are not meant to be used directly by users, but are useful for developers. 52 | * @note Only used in modal reactor execution. 53 | */ 54 | 55 | /** 56 | * @defgroup Federated Federated 57 | * @brief Functions for federated execution. 58 | * 59 | * This group contains functions for federated execution. 60 | * The message types and protocols are defined in @ref net_common.h. 61 | */ 62 | 63 | /** 64 | * @defgroup Tracing Tracing 65 | * @brief Functions for tracing. 66 | * 67 | * This group contains functions for tracing. 68 | * Tracing is described in the [Lingua Franca handbook](https://www.lf-lang.org/docs/next/reference/tracing). 69 | */ 70 | 71 | /** 72 | * @defgroup RTI RTI 73 | * @brief Functions for the runtime infrastructure for federated execution. 74 | * 75 | * This group contains functions for the runtime infrastructure for federated execution. 76 | * The message types and protocols are defined in @ref net_common.h. 77 | */ 78 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_flexpret_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_flexpret_support.h 3 | * @brief Platform-specific support for FlexPRET in the Lingua Franca C runtime. 4 | * 5 | * @author Magnus Mæhlum 6 | * 7 | * This header file provides platform-specific definitions and implementations 8 | * for running Lingua Franca programs on FlexPRET. It includes type definitions, 9 | * platform-specific constants, and interrupt handling mechanisms needed for 10 | * the runtime. 11 | */ 12 | 13 | #ifndef LF_FLEXPRET_SUPPORT_H 14 | #define LF_FLEXPRET_SUPPORT_H 15 | 16 | #include 17 | 18 | /** 19 | * Like nRF52, for FlexPRET, each mutex will control an interrupt. 20 | * 21 | * The mutex holds the interrupt number. 22 | * For example, a mutex might be defined for the GPIOTE peripheral interrupt number 23 | * 24 | * When initialized, the interrupt is inserted into a global linked list 25 | * for disabling and enabling all interrupts during sleep functions. 26 | * - All interrupts are disabled by default after initialization 27 | * - Priority levels are restricted between (0-7) 28 | * 29 | */ 30 | 31 | #include // Needed to define PRId64 and PRIu32 32 | #define PRINTF_TIME "%" PRId64 33 | #define PRINTF_MICROSTEP "%" PRIu32 34 | 35 | // For convenience, the following string can be inserted in a printf 36 | // format for printing both time and microstep as follows: 37 | // printf("Tag is " PRINTF_TAG "\n", time_value, microstep); 38 | #define PRINTF_TAG "(%" PRId64 ", %" PRIu32 ")" 39 | 40 | #if !defined(LF_SINGLE_THREADED) 41 | typedef fp_lock_t lf_mutex_t; 42 | typedef fp_thread_t lf_thread_t; 43 | typedef fp_cond_t lf_cond_t; 44 | #endif 45 | 46 | // This will filter out some unecessary calls to standard library functions 47 | // and save code space 48 | #define NO_CLI 49 | #define MINIMAL_STDLIB 50 | 51 | /** 52 | * Need to include `stdio` here, because we #define `fprintf` and `vfprintf` below. 53 | * Since stdio.h contains declarations for these functions, including it 54 | * after will result in the following: 55 | * 56 | * #define fprintf(s, f, ...) printf(f, ##__VA_ARGS__) 57 | * 58 | * int fprintf (FILE *__restrict, const char *__restrict, ...) 59 | * _ATTRIBUTE ((__format__ (__printf__, 2, 3))); 60 | * 61 | * Which the preprocessor will replace with: 62 | * 63 | * int printf (FILE *__restrict, const char *__restrict, ...) 64 | * _ATTRIBUTE ((__format__ (__printf__, 2, 3))); 65 | * 66 | * Which will yield an error. 67 | * 68 | */ 69 | #include 70 | 71 | // Likewise, fprintf is used to print to `stderr`, but FlexPRET has no `stderr` 72 | // We instead redirect its output to normal printf 73 | // Note: Most compilers do not support passing this on the command line, so CMake 74 | // will drop it if you try... But that would be the better option. 75 | #define fprintf(stream, fmt, ...) printf(fmt, ##__VA_ARGS__) 76 | #define vfprintf(fp, fmt, args) vprintf(fmt, args) 77 | 78 | #endif // LF_FLEXPRET_SUPPORT_H 79 | -------------------------------------------------------------------------------- /core/clock.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Erling Jellum 4 | * 5 | * @brief Implementations of functions in clock.h. 6 | * 7 | * By providing the following compile def, the user can provide their own 8 | * implementation of the clock functions. This allows controlling the 9 | * physical time the runtime sees. It is useful for integration with simulators 10 | * or for repeatable test environments. 11 | * 12 | * Steps to provide an external clock plugin: 13 | * 1. Use the cmake-include target property to add a custom CMake file to the build. 14 | * 2. Add `target_compile_definition(reactor-uc PUBLIC LF_EXTERNAL_CLOCK_PLUGIN)` to the custom CMake file. 15 | * 3. Implement the functions in clock.h in a separate file, e.g. my_clock.c 16 | * 4. Add `target_sources(${LF_MAIN_TARGET} PUBLIC my_clock.c)` to the custom CMake file. 17 | */ 18 | #if !defined(LF_EXTERNAL_CLOCK_PLUGIN) 19 | #include "clock.h" 20 | #include "low_level_platform.h" 21 | 22 | // If we are federated, include clock-sync API (and implementation) 23 | #if defined(FEDERATED) 24 | #include "clock-sync.h" 25 | #else 26 | // In the unfederated case, just provide empty implementations. 27 | void clock_sync_add_offset(instant_t* t) { (void)t; } 28 | void clock_sync_subtract_offset(instant_t* t) { (void)t; } 29 | #endif // defined(FEDERATED) 30 | 31 | static instant_t last_read_physical_time = NEVER; 32 | int lf_clock_gettime(instant_t* now) { 33 | instant_t last_read_local; 34 | int res = _lf_clock_gettime(now); 35 | if (res != 0) { 36 | return -1; 37 | } 38 | clock_sync_add_offset(now); 39 | do { 40 | // Atomically fetch the last read value. This is done with 41 | // atomics to guarantee that it works on 32bit platforms as well. 42 | last_read_local = lf_atomic_fetch_add64(&last_read_physical_time, 0); 43 | 44 | // Ensure monotonicity. 45 | if (*now < last_read_local) { 46 | *now = last_read_local + 1; 47 | } 48 | 49 | // Update the last read value, atomically and also make sure that another 50 | // thread has not been here in between and changed it. If so. We must redo 51 | // the monotonicity calculation. 52 | } while (!lf_atomic_bool_compare_and_swap64(&last_read_physical_time, last_read_local, *now)); 53 | 54 | return 0; 55 | } 56 | 57 | int lf_clock_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time) { 58 | // Remove any clock sync offset and call the Platform API. 59 | clock_sync_subtract_offset(&wakeup_time); 60 | return _lf_interruptable_sleep_until_locked(env, wakeup_time); 61 | } 62 | 63 | #if !defined(LF_SINGLE_THREADED) 64 | int lf_clock_cond_timedwait(lf_cond_t* cond, instant_t wakeup_time) { 65 | // Remove any clock sync offset and call the Platform API. 66 | clock_sync_subtract_offset(&wakeup_time); 67 | return _lf_cond_timedwait(cond, wakeup_time); 68 | } 69 | #endif // !defined(LF_SINGLE_THREADED) 70 | #else // defined(LF_EXTERNAL_CLOCK_PLUGIN) 71 | // The following empty "dummy" function is here to avoid an "Empty translation unit" compiler 72 | // warning if the user has defined LF_EXTERNAL_CLOCK_PLUGIN to provide their own implementation. 73 | void __clock_dummy_function(void) {} 74 | #endif // !defined(LF_EXTERNAL_CLOCK_PLUGIN) 75 | -------------------------------------------------------------------------------- /include/core/utils/hashset/hashset_itr.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file hashset_itr.h 3 | * @brief A C hashset iterator implemenation. 4 | * @ingroup Utilities 5 | * 6 | * This is a simple iterator for a hashset. It is not thread-safe. 7 | * 8 | * ## License 9 | * 10 | * Copyright 2012 Couchbase, Inc. 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | * 24 | * Modified in 2022 by Edward A. Lee to conform to documentation standards. 25 | * Also, changed the logic to iterate using just hashset_iterator_next(). 26 | */ 27 | 28 | #ifndef HASHSET_ITR_H_ 29 | #define HASHSET_ITR_H_ 30 | 31 | #include "hashset.h" 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | /** 38 | * @brief A hashset iterator. 39 | * @ingroup Utilities 40 | */ 41 | struct hashset_itr_st { 42 | hashset_t set; 43 | int index; 44 | }; 45 | 46 | typedef struct hashset_itr_st* hashset_itr_t; 47 | 48 | /** 49 | * @brief Create a hashset iterator. 50 | * @ingroup Utilities 51 | * 52 | * The caller should then iterate over the hashset as follows: 53 | * 54 | * ```c 55 | * hashset_itr_t iterator = hashset_iterator(my_hashset); 56 | * while (hashset_iterator_next(iterator) >= 0) { 57 | * void* my_value = hashset_iterator_value(iterator); 58 | * ... 59 | * } 60 | * free(iterator); 61 | * ``` 62 | * The caller must call `free()` on this iterator after using it. 63 | * 64 | * @param set The hashset to iterate over. 65 | * @return A hashset iterator. 66 | */ 67 | hashset_itr_t hashset_iterator(hashset_t set); 68 | 69 | /** 70 | * @brief Return the value at the current index. 71 | * @ingroup Utilities 72 | * 73 | * The called should check @ref hashset_iterator_has_next before calling this. 74 | * 75 | * @param itr The hashset iterator. 76 | * @return The value at the current index. 77 | */ 78 | void* hashset_iterator_value(hashset_itr_t itr); 79 | 80 | /** 81 | * @brief Return 1 if there is a next value in the hashset and 0 otherwise. 82 | * @ingroup Utilities 83 | * 84 | * @param itr The hashset iterator. 85 | * @return 1 if there is a next value in the hashset and 0 otherwise. 86 | */ 87 | int hashset_iterator_has_next(hashset_itr_t itr); 88 | 89 | /** 90 | * @brief Advance to the next value in the hashset. 91 | * @ingroup Utilities 92 | * 93 | * This returns a non-negative number (the current index) if there is a next item 94 | * and -1 otherwise. 95 | * 96 | * @param itr The hashset iterator. 97 | * @return The current index. 98 | */ 99 | int hashset_iterator_next(hashset_itr_t itr); 100 | 101 | #endif /* HASHSET_ITR_H_ */ 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | -------------------------------------------------------------------------------- /python/include/python_port.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * @author Soroush Bateni 5 | * @author Hou Seng Wong 6 | * 7 | * @brief Python port support for Lingua Franca. 8 | */ 9 | 10 | #ifndef PYTHON_PORT_H 11 | #define PYTHON_PORT_H 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "python_capsule_extension.h" 18 | #include "lf_types.h" 19 | #include "port.h" 20 | #include "python_action.h" 21 | 22 | extern PyTypeObject py_port_capsule_t; 23 | 24 | /** 25 | * The struct used to instantiate a port in Lingua Franca. This is used 26 | * in the PythonGenerator instead of redefining a struct for each port. 27 | * This can be used for any Python object, including lists and tuples. 28 | * PyObject* value is the value of the port with the generic Python type. 29 | * NOTE: The structure here must follow exactly that of lf_port_base_t, 30 | * which includes as its first element a token_template_t, which includes 31 | * as its first element a token_type_t. 32 | */ 33 | typedef struct { 34 | size_t element_size; // token_type_t 35 | void (*destructor)(void* value); // token_type_t 36 | void* (*copy_constructor)(void* value); // token_type_t 37 | lf_token_t* token; // token_template_t 38 | size_t length; // token_template_t 39 | bool is_present; // lf_port_base_t 40 | lf_port_internal_t _base; // lf_port_internal_t 41 | PyObject* value; 42 | FEDERATED_GENERIC_EXTENSION 43 | } generic_port_instance_struct; 44 | 45 | /** 46 | * The struct used to represent ports in Python 47 | * This template is used as a blueprint to create 48 | * Python objects that follow the same structure. 49 | * The resulting Python object will have the type 50 | * py_port_capsule_t in C (LinguaFranca.port_capsule in Python). 51 | * 52 | * port: A PyCapsule (https://docs.python.org/3/c-api/capsule.html) 53 | * that safely holds a C void* inside a Python object. This capsule 54 | * is passed through the Python code and is extracted in C functions 55 | * like set and __getitem__. 56 | * value: The value of the port at the time of invocation of @see convert_C_port_to_py. 57 | * The value and is_present are copied from the port if it is not a multiport and can be accessed as 58 | * port.value. For multiports, is_present will be false and value will be None. The value of each individual 59 | * port can be accessed as port[idx].value (@see port_capsule_get_item). 60 | * Subsequent calls to set will also need to update the value and is_present fields so that they are reflected 61 | * in Python code. 62 | * is_present: Indicates if the value of the singular port is present 63 | * at the current logical time 64 | * width: Indicates the width of the multiport. This is set to -2 for non-multiports. 65 | * current_index: Used to facilitate iterative functions (@see port_iter) 66 | **/ 67 | typedef struct { 68 | PyObject_HEAD PyObject* port; 69 | PyObject* value; 70 | bool is_present; 71 | int width; 72 | long current_index; 73 | FEDERATED_CAPSULE_EXTENSION 74 | } generic_port_capsule_struct; 75 | 76 | void python_count_decrement(void* py_object); 77 | #endif 78 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_POSIX_threads_support.c: -------------------------------------------------------------------------------- 1 | #if !defined(LF_SINGLE_THREADED) && !defined(PLATFORM_ARDUINO) 2 | #include "low_level_platform.h" 3 | #include "platform/lf_POSIX_threads_support.h" 4 | #include "platform/lf_unix_clock_support.h" 5 | 6 | #include 7 | #include 8 | #include // For fixed-width integral types 9 | #include 10 | 11 | int lf_available_cores() { return (int)sysconf(_SC_NPROCESSORS_ONLN); } 12 | 13 | int lf_thread_create(lf_thread_t* thread, void* (*lf_thread)(void*), void* arguments) { 14 | return pthread_create((pthread_t*)thread, NULL, lf_thread, arguments); 15 | } 16 | 17 | lf_thread_t lf_thread_self() { return pthread_self(); } 18 | 19 | int lf_thread_join(lf_thread_t thread, void** thread_return) { return pthread_join((pthread_t)thread, thread_return); } 20 | 21 | int lf_mutex_init(lf_mutex_t* mutex) { 22 | // Set up a recursive mutex 23 | pthread_mutexattr_t attr; 24 | pthread_mutexattr_init(&attr); 25 | // Initialize the mutex to be recursive, meaning that it is OK 26 | // for the same thread to lock and unlock the mutex even if it already holds 27 | // the lock. 28 | // FIXME: This is dangerous. The docs say this: "It is advised that an 29 | // application should not use a PTHREAD_MUTEX_RECURSIVE mutex with 30 | // condition variables because the implicit unlock performed for a 31 | // pthread_cond_wait() or pthread_cond_timedwait() may not actually 32 | // release the mutex (if it had been locked multiple times). 33 | // If this happens, no other thread can satisfy the condition 34 | // of the predicate.” This seems like a bug in the implementation of 35 | // pthreads. Maybe it has been fixed? 36 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 37 | return pthread_mutex_init((pthread_mutex_t*)mutex, &attr); 38 | } 39 | 40 | int lf_mutex_lock(lf_mutex_t* mutex) { return pthread_mutex_lock((pthread_mutex_t*)mutex); } 41 | 42 | int lf_mutex_unlock(lf_mutex_t* mutex) { return pthread_mutex_unlock((pthread_mutex_t*)mutex); } 43 | 44 | int lf_cond_init(lf_cond_t* cond, lf_mutex_t* mutex) { 45 | cond->mutex = mutex; 46 | pthread_condattr_t cond_attr; 47 | pthread_condattr_init(&cond_attr); 48 | // Limit the scope of the condition variable to this process (default) 49 | pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_PRIVATE); 50 | return pthread_cond_init(&cond->condition, &cond_attr); 51 | } 52 | 53 | int lf_cond_broadcast(lf_cond_t* cond) { return pthread_cond_broadcast((pthread_cond_t*)&cond->condition); } 54 | 55 | int lf_cond_signal(lf_cond_t* cond) { return pthread_cond_signal((pthread_cond_t*)&cond->condition); } 56 | 57 | int lf_cond_wait(lf_cond_t* cond) { 58 | return pthread_cond_wait((pthread_cond_t*)&cond->condition, (pthread_mutex_t*)cond->mutex); 59 | } 60 | 61 | int _lf_cond_timedwait(lf_cond_t* cond, instant_t wakeup_time) { 62 | struct timespec timespec_absolute_time = convert_ns_to_timespec(wakeup_time); 63 | int return_value = 64 | pthread_cond_timedwait((pthread_cond_t*)&cond->condition, (pthread_mutex_t*)cond->mutex, ×pec_absolute_time); 65 | switch (return_value) { 66 | case ETIMEDOUT: 67 | return_value = LF_TIMEOUT; 68 | break; 69 | 70 | default: 71 | break; 72 | } 73 | return return_value; 74 | } 75 | #endif 76 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_patmos_support.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Patmos support for the C target of Lingua Franca. 4 | * 5 | * @author Ehsan Khodadad 6 | * @author Luca Pezzarossa 7 | * @author Martin Schoeberl 8 | */ 9 | #if defined(PLATFORM_PATMOS) 10 | #include 11 | #include 12 | #include 13 | #include "platform/lf_patmos_support.h" 14 | #include "low_level_platform.h" 15 | #include 16 | #include 17 | #include 18 | 19 | // Keep track of physical actions being entered into the system 20 | static volatile bool _lf_async_event = false; 21 | // Keep track of whether we are in a critical section or not 22 | static volatile int _lf_num_nested_critical_sections = 0; 23 | /** 24 | * @brief Sleep until an absolute time. 25 | * Since there is no sleep mode in Patmos, and energy saving is not important for real-time systems, 26 | * we just used a busy sleep. 27 | * 28 | * @param wakeup int64_t time of wakeup 29 | * @return int 0 if successful sleep, -1 if awoken by async event 30 | */ 31 | 32 | int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup) { 33 | instant_t now; 34 | _lf_async_event = false; 35 | lf_enable_interrupts_nested(); 36 | 37 | // Do busy sleep 38 | do { 39 | _lf_clock_gettime(&now); 40 | } while ((now < wakeup) && !_lf_async_event); 41 | 42 | lf_disable_interrupts_nested(); 43 | 44 | if (_lf_async_event) { 45 | _lf_async_event = false; 46 | return -1; 47 | } else { 48 | return 0; 49 | } 50 | } 51 | 52 | int lf_sleep(interval_t sleep_duration) { 53 | instant_t now; 54 | _lf_clock_gettime(&now); 55 | instant_t wakeup = now + sleep_duration; 56 | 57 | // Do busy sleep 58 | do { 59 | _lf_clock_gettime(&now); 60 | } while ((now < wakeup)); 61 | return 0; 62 | } 63 | 64 | /** 65 | * Pause execution for a number of nanoseconds. 66 | * 67 | * @return 0 for success, or -1 for failure. In case of failure, errno will be 68 | * set appropriately (see `man 2 clock_nanosleep`). 69 | */ 70 | int lf_nanosleep(interval_t requested_time) { return lf_sleep(requested_time); } 71 | 72 | /** 73 | * Patmos clock does not need initialization. 74 | */ 75 | void _lf_initialize_clock() {} 76 | 77 | /** 78 | * Write the current time in nanoseconds into the location given by the argument. 79 | * This returns 0 (it never fails, assuming the argument gives a valid memory location). 80 | */ 81 | 82 | int _lf_clock_gettime(instant_t* t) { 83 | 84 | assert(t != NULL); 85 | 86 | *t = get_cpu_usecs() * 1000; 87 | 88 | return 0; 89 | } 90 | 91 | #if defined(LF_SINGLE_THREADED) 92 | 93 | int lf_disable_interrupts_nested() { 94 | if (_lf_num_nested_critical_sections++ == 0) { 95 | intr_disable(); 96 | } 97 | return 0; 98 | } 99 | 100 | int lf_enable_interrupts_nested() { 101 | if (_lf_num_nested_critical_sections <= 0) { 102 | return 1; 103 | } 104 | 105 | if (--_lf_num_nested_critical_sections == 0) { 106 | intr_enable(); 107 | } 108 | return 0; 109 | } 110 | 111 | int _lf_single_threaded_notify_of_event() { 112 | _lf_async_event = true; 113 | return 0; 114 | } 115 | #endif // LF_SINGLE_THREADED 116 | 117 | #endif // PLATFORM_PATMOS 118 | -------------------------------------------------------------------------------- /python/include/python_capsule_extension.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Soroush Bateni 4 | * @author Hou Seng Wong 5 | * 6 | * @brief Definitions for Python federated extensions. 7 | */ 8 | 9 | #ifndef PYTHON_CAPSULE_EXTENSION_H 10 | #define PYTHON_CAPSULE_EXTENSION_H 11 | 12 | #ifdef FEDERATED 13 | #ifdef FEDERATED_DECENTRALIZED 14 | #define FEDERATED_GENERIC_EXTENSION \ 15 | tag_t intended_tag; \ 16 | instant_t physical_time_of_arrival; 17 | 18 | #define FEDERATED_CAPSULE_EXTENSION \ 19 | py_tag_t* intended_tag; \ 20 | instant_t physical_time_of_arrival; 21 | 22 | #define FEDERATED_CAPSULE_MEMBER \ 23 | {"intended_tag", T_OBJECT, offsetof(generic_port_capsule_struct, intended_tag), READONLY, \ 24 | "Original intended tag of the event."}, \ 25 | {"physical_time_of_arrival", T_LONG, offsetof(generic_port_capsule_struct, physical_time_of_arrival), READONLY, \ 26 | "Physical time of arrival of the original message."}, 27 | 28 | #define FEDERATED_ASSIGN_FIELDS(py_port, c_port) \ 29 | do { \ 30 | py_port->intended_tag = convert_C_tag_to_py(c_port->intended_tag); \ 31 | py_port->physical_time_of_arrival = c_port->physical_time_of_arrival; \ 32 | } while (0) 33 | 34 | #else // FEDERATED_CENTRALIZED 35 | #define FEDERATED_GENERIC_EXTENSION instant_t physical_time_of_arrival; 36 | 37 | #define FEDERATED_CAPSULE_EXTENSION FEDERATED_GENERIC_EXTENSION 38 | 39 | #define FEDERATED_CAPSULE_MEMBER \ 40 | {"physical_time_of_arrival", T_INT, offsetof(generic_port_capsule_struct, physical_time_of_arrival), READONLY, \ 41 | "Physical time of arrival of the original message."}, 42 | 43 | #define FEDERATED_ASSIGN_FIELDS(py_port, c_port) \ 44 | do { \ 45 | py_port->physical_time_of_arrival = c_port->physical_time_of_arrival; \ 46 | } while (0) 47 | #endif // FEDERATED_DECENTRALIZED 48 | #else // not FEDERATED 49 | #define FEDERATED_GENERIC_EXTENSION // Empty 50 | #define FEDERATED_CAPSULE_EXTENSION // Empty 51 | #define FEDERATED_CAPSULE_MEMBER // Empty 52 | #define FEDERATED_ASSIGN_FIELDS(py_port, c_port) // Empty 53 | #define FEDERATED_COPY_FIELDS(py_port1, py_port2) // Empty 54 | #endif // FEDERATED 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /include/core/threaded/scheduler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file scheduler.h 3 | * @author Soroush Bateni 4 | * @author Edward A. Lee 5 | * 6 | * @brief Scheduler API for the threaded C runtime. 7 | * @ingroup Internal 8 | * 9 | * A scheduler for the threaded runtime of reactor-c should provide an 10 | * implementation for functions that are defined in this header file. 11 | */ 12 | 13 | #ifndef LF_SCHEDULER_H 14 | #define LF_SCHEDULER_H 15 | 16 | #include "lf_types.h" 17 | #include "scheduler_instance.h" 18 | 19 | /** 20 | * @brief Initialize the scheduler. 21 | * @ingroup Internal 22 | * 23 | * This has to be called before other functions of the scheduler can be used. 24 | * If the scheduler is already initialized, this will be a no-op. 25 | * 26 | * @param env The environment in which we should initialize the scheduler 27 | * @param number_of_workers Indicate how many workers this scheduler will be 28 | * managing. 29 | * @param parameters Pointer to a `sched_params_t` struct containing additional 30 | * scheduler parameters. Can be NULL. 31 | */ 32 | void lf_sched_init(environment_t* env, size_t number_of_workers, sched_params_t* parameters); 33 | 34 | /** 35 | * @brief Free the memory used by the scheduler. 36 | * @ingroup Internal 37 | * 38 | * @param scheduler The scheduler 39 | * 40 | * This must be called when the scheduler is no longer needed. 41 | */ 42 | void lf_sched_free(lf_scheduler_t* scheduler); 43 | 44 | /** 45 | * @brief Ask the scheduler for one more reaction. 46 | * @ingroup Internal 47 | * 48 | * This function blocks until it can return a ready reaction for worker thread 49 | * 'worker_number' or it is time for the worker thread to stop and exit (where a 50 | * NULL value would be returned). 51 | * This function assumes that the environment mutex is not locked. 52 | * 53 | * @param scheduler The scheduler 54 | * @param worker_number For the calling worker thread. 55 | * @return reaction_t* A reaction for the worker to execute. NULL if the calling 56 | * worker thread should exit. 57 | */ 58 | reaction_t* lf_sched_get_ready_reaction(lf_scheduler_t* scheduler, int worker_number); 59 | 60 | /** 61 | * @brief Inform the scheduler that worker thread 'worker_number' is done 62 | * executing the 'done_reaction'. 63 | * @ingroup Internal 64 | * 65 | * @param worker_number The worker number for the worker thread that has 66 | * finished executing 'done_reaction'. 67 | * @param done_reaction The reaction that is done. 68 | */ 69 | void lf_sched_done_with_reaction(size_t worker_number, reaction_t* done_reaction); 70 | 71 | /** 72 | * @brief Inform the scheduler that worker thread 'worker_number' would like to 73 | * trigger 'reaction' at the current tag. 74 | * @ingroup Internal 75 | * 76 | * If a worker number is not available (e.g., this function is not called by a 77 | * worker thread), -1 should be passed as the 'worker_number'. 78 | * 79 | * The scheduler will ensure that the same reaction is not triggered twice in 80 | * the same tag. 81 | * 82 | * @param scheduler The scheduler. 83 | * @param reaction The reaction to trigger at the current tag. 84 | * @param worker_number The ID of the worker that is making this call. 0 should be 85 | * used if there is only one worker (e.g., when the program is using the 86 | * single-threaded C runtime). -1 is used for an anonymous call in a context where a 87 | * worker number does not make sense (e.g., the caller is not a worker thread). 88 | * 89 | */ 90 | void lf_scheduler_trigger_reaction(lf_scheduler_t* scheduler, reaction_t* reaction, int worker_number); 91 | 92 | #endif // LF_SCHEDULER_H 93 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_arduino_support.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_arduino_support.h 3 | * @brief Platform-specific support for Arduino boards in the Lingua Franca C runtime. 4 | * 5 | * @author Anirudh Rengarajan 6 | * 7 | * This header file provides platform-specific definitions and implementations 8 | * for running Lingua Franca programs on Arduino boards. It includes board 9 | * detection, type definitions, and platform-specific constants needed for 10 | * the runtime. 11 | */ 12 | 13 | #ifndef LF_ARDUINO_SUPPORT_H 14 | #define LF_ARDUINO_SUPPORT_H 15 | 16 | #include // For fixed-width integral types 17 | #include // For CLOCK_MONOTONIC 18 | #include 19 | 20 | #define AVR 0 21 | #define megaAVR 1 22 | #define SAMD 2 23 | #define SAM 3 24 | #define MBED 4 25 | 26 | #ifndef BOARD 27 | #if defined(ARDUINO_AVR_ADK) 28 | #define BOARD AVR 29 | #elif defined(ARDUINO_AVR_BT) // Bluetooth 30 | #define BOARD AVR 31 | #elif defined(ARDUINO_AVR_DUEMILANOVE) 32 | #define BOARD AVR 33 | #elif defined(ARDUINO_AVR_ESPLORA) 34 | #define BOARD AVR 35 | #elif defined(ARDUINO_AVR_ETHERNET) 36 | #define BOARD AVR 37 | #elif defined(ARDUINO_AVR_FIO) 38 | #define BOARD AVR 39 | #elif defined(ARDUINO_AVR_GEMMA) 40 | #define BOARD AVR 41 | #elif defined(ARDUINO_AVR_LEONARDO) 42 | #define BOARD AVR 43 | #elif defined(ARDUINO_AVR_LILYPAD) 44 | #define BOARD AVR 45 | #elif defined(ARDUINO_AVR_LILYPAD_USB) 46 | #define BOARD AVR 47 | #elif defined(ARDUINO_AVR_MEGA) 48 | #define BOARD AVR 49 | #elif defined(ARDUINO_AVR_MEGA2560) 50 | #define BOARD AVR 51 | #elif defined(ARDUINO_AVR_MICRO) 52 | #define BOARD AVR 53 | #elif defined(ARDUINO_AVR_MINI) 54 | #define BOARD AVR 55 | #elif defined(ARDUINO_AVR_NANO) 56 | #define BOARD AVR 57 | #elif defined(ARDUINO_AVR_NG) 58 | #define BOARD AVR 59 | #elif defined(ARDUINO_AVR_PRO) 60 | #define BOARD AVR 61 | #elif defined(ARDUINO_AVR_ROBOT_CONTROL) 62 | #define BOARD AVR 63 | #elif defined(ARDUINO_AVR_ROBOT_MOTOR) 64 | #define BOARD AVR 65 | #elif defined(ARDUINO_AVR_UNO) || defined(__AVR_ATmega4809__) 66 | #define BOARD AVR 67 | #elif defined(ARDUINO_AVR_YUN) 68 | #define BOARD AVR 69 | 70 | // These boards must be installed separately: 71 | #elif defined(ARDUINO_SAM_DUE) 72 | #define BOARD SAM 73 | #elif defined(ARDUINO_SAMD_ZERO) 74 | #define BOARD SAMD 75 | #elif defined(ARDUINO_ARC32_TOOLS) 76 | #define BOARD SAM 77 | #elif defined(ARDUINO_ARDUINO_NANO33BLE) 78 | #define BOARD MBED 79 | #endif 80 | #endif 81 | 82 | #define __STDC_FORMAT_MACROS 83 | #include 84 | 85 | #ifndef __timespec_defined 86 | #define __timespec_defined 87 | #ifndef _SYS__TIMESPEC_H_ 88 | #define _SYS__TIMESPEC_H_ 89 | struct timespec { 90 | long long tv_sec; /* seconds */ 91 | long tv_nsec; /* and nanoseconds */ 92 | }; 93 | #endif 94 | #endif 95 | 96 | #if !defined(LF_SINGLE_THREADED) 97 | #warning "Threaded support on Arduino is still experimental" 98 | 99 | typedef void* lf_mutex_t; 100 | typedef void* lf_cond_t; 101 | typedef void* lf_thread_t; 102 | 103 | #endif // !LF_SNIGLE_THREADED 104 | 105 | #define PRINTF_TIME "%" PRIu32 106 | #define PRINTF_MICROSTEP "%" PRIu32 107 | #define PRINTF_TAG "(" PRINTF_TIME ", " PRINTF_MICROSTEP ")" 108 | 109 | #define LLONG_MAX __LONG_LONG_MAX__ 110 | #define LLONG_MIN (-LLONG_MAX - 1LL) 111 | #define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) 112 | 113 | // Arduinos are embedded platforms with no command line interface 114 | #define NO_CLI 115 | 116 | #endif // LF_ARDUINO_SUPPORT_H 117 | -------------------------------------------------------------------------------- /util/tracing/codegen/src/tracepoint_to_rs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "trace.h" 7 | #include "trace_types.h" 8 | 9 | int is_alphanumeric(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } 10 | 11 | void to_camel_case(char* s) { 12 | int capitalize_next = 1; // Flag to indicate whether the next character should be capitalized 13 | int j = 0; 14 | for (int i = 0; s[i] != '\0'; ++i) { 15 | if (!is_alphanumeric(s[i])) { 16 | capitalize_next = 1; // Treat non-alphanumeric characters as whitespace 17 | } else { 18 | if (capitalize_next) { 19 | s[j] = toupper(s[i]); 20 | capitalize_next = 0; // Reset the flag 21 | } else { 22 | s[j] = tolower(s[i]); // Convert to lowercase if not capitalizing 23 | } 24 | j++; 25 | } 26 | } 27 | s[j] = '\0'; 28 | } 29 | 30 | typedef void (*string_consumer_t)(int, const char*, const char*); 31 | 32 | void print_enum_variant(int idx, const char* camel_case, const char* description) { 33 | printf(" %s = %d,\n", camel_case, idx); 34 | } 35 | 36 | void print_match_case(int idx, const char* camel_case, const char* description) { 37 | printf(" EventType::%s => write!(f, \"%s\"),\n", camel_case, description); 38 | } 39 | 40 | void print_from_int(int idx, const char* camel_case, const char* description) { 41 | printf(" %d => Ok(EventType::%s),\n", idx, camel_case); 42 | } 43 | 44 | void do_for_each_camelcase(string_consumer_t sc) { 45 | for (int i = 0; i < NUM_EVENT_TYPES; i++) { 46 | size_t length = strlen(trace_event_names[i]); 47 | 48 | // Allocate memory for the new string including the null terminator 49 | char* destination = (char*)malloc((length + 1) * sizeof(char)); 50 | 51 | // Check if memory allocation was successful 52 | if (destination == NULL) { 53 | perror("Memory allocation failed"); 54 | exit(1); 55 | } 56 | 57 | // Copy the source string to the newly allocated buffer 58 | strcpy(destination, trace_event_names[i]); 59 | to_camel_case(destination); 60 | sc(i, destination, trace_event_names[i]); 61 | } 62 | } 63 | 64 | void print_display_impl() { 65 | printf("%s\n", "impl std::fmt::Display for EventType {"); 66 | printf("%s\n", " fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {"); 67 | printf("%s\n", " match self {"); 68 | do_for_each_camelcase(print_match_case); 69 | printf("%s\n", " }"); 70 | printf("%s\n", " }"); 71 | printf("%s\n", "}"); 72 | } 73 | 74 | void print_rs_enum() { 75 | printf("%s\n", "#[derive(Debug)]"); 76 | printf("%s\n", "pub enum EventType {"); 77 | do_for_each_camelcase(print_enum_variant); 78 | printf("}\n"); 79 | } 80 | 81 | void print_warning() { 82 | printf("%s\n", "/// Do not edit. Code in this file is generated from"); 83 | printf("%s\n", "/// reactor-c/util/tracing/codegen/src/tracepoint_to_rs.c"); 84 | } 85 | 86 | void print_rs_from_int() { 87 | printf("%s\n", "impl EventType {"); 88 | printf("%s\n", " pub fn try_from_int(i: i32) -> Result {"); 89 | printf("%s\n", " match i {"); 90 | do_for_each_camelcase(print_from_int); 91 | printf("%s\n", " _ => Err(\"invalid event type\"),"); 92 | printf("%s\n", " }"); 93 | printf("%s\n", " }"); 94 | printf("%s\n", "}"); 95 | } 96 | 97 | int main() { 98 | print_warning(); 99 | printf("%s", "\n"); 100 | print_rs_enum(); 101 | printf("%s", "\n"); 102 | print_display_impl(); 103 | printf("%s", "\n"); 104 | print_rs_from_int(); 105 | } 106 | -------------------------------------------------------------------------------- /test/general/utils/vector_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "vector.h" 4 | #include "rand_utils.h" 5 | #include "util.h" 6 | 7 | #define CAPACITY 100 8 | #define MAX_PUSHALL 8 9 | #define N 5000 10 | #define RANDOM_SEED 1614 11 | 12 | static void* mock[CAPACITY]; 13 | static size_t mock_size = 0; 14 | 15 | static int distribution[4] = {30, 50, 5, 15}; 16 | 17 | /** 18 | * @brief Test the append functionality of `v`. 19 | * 20 | * @param v A vector. 21 | * @param x Any pointer. 22 | */ 23 | void test_push(vector_t* v) { 24 | LF_PRINT_DEBUG("push."); 25 | void* x = mock + rand(); 26 | vector_push(v, x); 27 | mock[mock_size++] = x; 28 | } 29 | 30 | /** 31 | * @brief Test the pop functionality of `v`. 32 | * 33 | * @param v A vector. 34 | */ 35 | void test_pop(vector_t* v) { 36 | LF_PRINT_DEBUG("pop."); 37 | void* expected; 38 | void* found; 39 | if (mock_size && ((found = vector_pop(v)) != (expected = mock[--mock_size]))) { 40 | lf_print_error_and_exit("Expected %p but got %p while popping from a vector.", expected, found); 41 | } 42 | } 43 | 44 | /** 45 | * @brief Test the "push all" functionality of `v`. 46 | * 47 | * @param v A vector 48 | * @return The number of items pushed to `v`. 49 | */ 50 | int test_pushall(vector_t* v) { 51 | LF_PRINT_DEBUG("pushall."); 52 | int count = rand() % MAX_PUSHALL; 53 | void** mock_start = mock + mock_size; 54 | for (int i = 0; i < count; i++) { 55 | mock[mock_size++] = mock - rand(); 56 | } 57 | vector_pushall(v, mock_start, count); 58 | return count; 59 | } 60 | 61 | /** 62 | * @brief Check that the result of a random access to `v` yields the 63 | * correct result. 64 | * 65 | * @param v A vector. 66 | */ 67 | void test_random_access(vector_t* v) { 68 | if (mock_size) { 69 | int idx = rand() % mock_size; 70 | if (v->start[idx] != mock[idx]) { 71 | lf_print_error_and_exit("Expected %p but got %p while randomly accessing a vector.", mock[idx], v->start[idx]); 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * @brief Check that voting does not cause an error. 78 | * 79 | * @param v A vector. 80 | */ 81 | void test_vote(vector_t* v) { 82 | LF_PRINT_DEBUG("vote."); 83 | vector_vote(v); 84 | } 85 | 86 | /** 87 | * @brief Run a randomly selected test on `v`. 88 | * 89 | * @param v A vector. 90 | * @param distribution The desired probability distribution with 91 | * which each of four actions are performed, expressed as percents. 92 | * @return An integer that is lower-bounded by 1 and upper-bounded 93 | * by the number of items added to `v`. 94 | */ 95 | int run_test(vector_t* v, int* distribution) { 96 | int result = 1; 97 | int choice = rand() % 100; 98 | if ((choice = choice - distribution[0]) < 0) { 99 | test_push(v); 100 | } else if ((choice = choice - distribution[1]) < 0) { 101 | test_pop(v); 102 | } else if ((choice = choice - distribution[2]) < 0) { 103 | result += test_pushall(v); 104 | } else { 105 | test_vote(v); 106 | } 107 | test_random_access(v); 108 | return result; 109 | } 110 | 111 | int main() { 112 | srand(RANDOM_SEED); 113 | for (int i = 0; i < N; i++) { 114 | int perturbed[4]; 115 | perturb(distribution, 4, perturbed); 116 | LF_PRINT_DEBUG("Distribution: %d, %d, %d, %d", perturbed[0], perturbed[1], perturbed[2], perturbed[3]); 117 | // FIXME: Decide whether it should be possible to initialize 118 | // vectors with zero capacity. 119 | vector_t v = vector_new(rand() % CAPACITY + 1); 120 | mock_size = 0; 121 | int j = 0; 122 | while (j < CAPACITY) { 123 | j += run_test(&v, perturbed); 124 | } 125 | vector_free(&v); 126 | } 127 | return 0; 128 | } 129 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_zephyr_clock_kernel.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @brief Implementation of the timing-related platform API ontop of the kernel 4 | * timer of Zephyr. This is less precise, but more portable than the alternative 5 | * Counter based implementation. 6 | * 7 | * @author Erling Jellum 8 | * @author Marten Lohstroh 9 | */ 10 | #if defined(PLATFORM_ZEPHYR) 11 | #include "platform/lf_zephyr_board_support.h" 12 | #if !defined(LF_ZEPHYR_CLOCK_COUNTER) 13 | 14 | #include 15 | #include 16 | 17 | #include "platform/lf_zephyr_support.h" 18 | #include "low_level_platform.h" 19 | #include "logging_macros.h" 20 | 21 | // Convert Zephyr ticks into an interval_t. According to Zephyr docs the 22 | // ticks are 100Hz for QEMU emulations, and normally a multiple of 10. 23 | #if CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100 24 | #define TICKS_TO_NSEC(ticks) MSEC(10 * ticks) 25 | #elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 1000 26 | #define TICKS_TO_NSEC(ticks) MSEC(ticks) 27 | #elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 10000 28 | #define TICKS_TO_NSEC(ticks) USEC(100 * ticks) 29 | #elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 100000 30 | #define TICKS_TO_NSEC(ticks) USEC(10 * ticks) 31 | #elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 1000000 32 | #define TICKS_TO_NSEC(ticks) USEC(1 * ticks) 33 | #elif CONFIG_SYS_CLOCK_TICKS_PER_SEC == 10000000 34 | #define TICKS_TO_NSEC(ticks) NSEC(100 * ticks) 35 | #else 36 | #define TICKS_TO_NSEC(ticks) ((SECONDS(1) / CONFIG_SYS_CLOCK_TICKS_PER_SEC) * ticks) 37 | #endif 38 | 39 | static uint32_t timer_freq; 40 | static volatile bool async_event = false; 41 | 42 | // Statically create an initialize the semaphore used for sleeping. 43 | K_SEM_DEFINE(sleeping_sem, 0, 1) 44 | 45 | void _lf_initialize_clock() { 46 | timer_freq = CONFIG_SYS_CLOCK_TICKS_PER_SEC; 47 | lf_print("--- Using LF Zephyr Kernel Clock with a frequency of %u Hz", timer_freq); 48 | } 49 | 50 | /** Uses Zephyr's monotonic increasing uptime count. */ 51 | int _lf_clock_gettime(instant_t* t) { 52 | interval_t uptime = k_uptime_ticks(); 53 | *t = TICKS_TO_NSEC(uptime); 54 | return 0; 55 | } 56 | 57 | /** Interruptable sleep is implemented by a taking a semaphore with a timeout. */ 58 | int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup) { 59 | async_event = false; 60 | 61 | interval_t duration = wakeup - lf_time_physical(); 62 | if (duration <= 0) { 63 | return 0; 64 | } 65 | 66 | // Reset the semaphore. This is safe to do before we leave the critical 67 | // section. 68 | k_sem_reset(&sleeping_sem); 69 | 70 | if (lf_critical_section_exit(env)) { 71 | lf_print_error_and_exit("Failed to exit critical section."); 72 | } 73 | 74 | int res = k_sem_take(&sleeping_sem, K_NSEC(duration)); 75 | 76 | if (lf_critical_section_enter(env)) { 77 | lf_print_error_and_exit("Failed to exit critical section."); 78 | } 79 | 80 | if (res == 0) { 81 | // We got the semaphore, this means there should be a new event 82 | if (!async_event) { 83 | lf_print_warning("Sleep was interrupted, but no new event"); 84 | } 85 | async_event = false; 86 | return -1; 87 | } else if (res == -EAGAIN) { 88 | // This means we timed out and have reached our wakeup instant. 89 | return 0; 90 | } else { 91 | lf_print_error_and_exit("k_sem_take returned %d", res); 92 | return -1; 93 | } 94 | } 95 | 96 | /** 97 | * Asynchronous events are notified by signalling a semaphore which will wakeup 98 | * the runtime if it is sleeping, and setting a flag to indicate what has 99 | * happened. 100 | */ 101 | int _lf_single_threaded_notify_of_event() { 102 | async_event = true; 103 | k_sem_give(&sleeping_sem); 104 | return 0; 105 | } 106 | 107 | #endif 108 | #endif 109 | -------------------------------------------------------------------------------- /util/deque.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file deque.h 3 | * @author Arthur Deng 4 | * @author Edward A. Lee 5 | * 6 | * @brief Implementation of a double-ended queue. 7 | * @ingroup Utilities 8 | * 9 | * This is the header file for an implementation of a double-ended queue. 10 | * Each node in the queue contains a void* pointer. 11 | * 12 | * To use this, include the following in your target properties: 13 | * 14 | * ``` 15 | * target C { 16 | * cmake-include: "/lib/c/reactor-c/util/deque.cmake" 17 | * files: ["/lib/c/reactor-c/util/deque.c", "/lib/c/reactor-c/util/deque.h"] 18 | * }; 19 | * ``` 20 | * In addition, you need this in your Lingua Franca file: 21 | * 22 | * ``` 23 | * preamble {= 24 | * #include "deque.h" 25 | * =} 26 | * ``` 27 | * To create a deque, use calloc to ensure that it gets initialized 28 | * with null pointers and zero size: 29 | * 30 | * ``` 31 | * deque_t* my_deque = (deque_t*) calloc(1, sizeof(deque_t)); 32 | * ``` 33 | * Alternatively, you can call initialize: 34 | * 35 | * ``` 36 | * deque my_deque; 37 | * deque_initialize(&my_deque); 38 | * ``` 39 | */ 40 | 41 | #ifndef DEQUE_H 42 | #define DEQUE_H 43 | 44 | #include // Defines size_t 45 | #include // Defines bool 46 | #include // Defines malloc and free 47 | 48 | /** 49 | * @brief A double-ended queue data structure. 50 | * @ingroup Utilities 51 | */ 52 | typedef struct deque_t { 53 | struct deque_node_t* front; 54 | struct deque_node_t* back; 55 | size_t size; 56 | } deque_t; 57 | 58 | /** 59 | * @brief Initialize the specified deque to an empty deque. 60 | * @ingroup Utilities 61 | * 62 | * @param d The deque. 63 | */ 64 | void deque_initialize(deque_t* d); 65 | 66 | /** 67 | * @brief Return true if the queue is empty. 68 | * @ingroup Utilities 69 | * 70 | * @param d The deque. 71 | */ 72 | bool deque_is_empty(deque_t* d); 73 | 74 | /** 75 | * @brief Return the size of the queue. 76 | * @ingroup Utilities 77 | * 78 | * @param d The deque. 79 | * @return The size of the queue. 80 | */ 81 | size_t deque_size(deque_t* d); 82 | 83 | /** 84 | * @brief Push a value to the front of the queue. 85 | * @ingroup Utilities 86 | * 87 | * @param d The queue. 88 | * @param value The value to push. 89 | */ 90 | void deque_push_front(deque_t* d, void* value); 91 | 92 | /** 93 | * @brief Push a value to the back of the queue. 94 | * @ingroup Utilities 95 | * 96 | * @param d The queue. 97 | * @param value The value to push. 98 | */ 99 | void deque_push_back(deque_t* d, void* value); 100 | 101 | /** 102 | * @brief Pop a value from the front of the queue, removing it from the queue. 103 | * @ingroup Utilities 104 | * 105 | * @param d The queue. 106 | * @return The value on the front of the queue or NULL if the queue is empty. 107 | */ 108 | void* deque_pop_front(deque_t* d); 109 | 110 | /** 111 | * @brief Pop a value from the back of the queue, removing it from the queue. 112 | * @ingroup Utilities 113 | * 114 | * @param d The queue. 115 | * @return The value on the back of the queue or NULL if the queue is empty. 116 | */ 117 | void* deque_pop_back(deque_t* d); 118 | 119 | /** 120 | * @brief Peek at the value on the front of the queue, leaving it on the queue. 121 | * @ingroup Utilities 122 | * 123 | * @param d The queue. 124 | * @return The value on the front of the queue or NULL if the queue is empty. 125 | */ 126 | void* deque_peek_back(deque_t* d); 127 | 128 | /** 129 | * @brief Peek at the value on the back of the queue, leaving it on the queue. 130 | * @ingroup Utilities 131 | * 132 | * @param d The queue. 133 | * @return The value on the back of the queue or NULL if the queue is empty. 134 | */ 135 | void* deque_peek_front(deque_t* d); 136 | 137 | #endif // DEQUE_H 138 | -------------------------------------------------------------------------------- /docs/assets/doxygen-awesome-interactive-toc.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2022 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | class DoxygenAwesomeInteractiveToc { 31 | static topOffset = 38 32 | static hideMobileMenu = true 33 | static headers = [] 34 | 35 | static init() { 36 | window.addEventListener("load", () => { 37 | let toc = document.querySelector(".contents > .toc") 38 | if(toc) { 39 | toc.classList.add("interactive") 40 | if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) { 41 | toc.classList.add("open") 42 | } 43 | document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => { 44 | if(toc.classList.contains("open")) { 45 | toc.classList.remove("open") 46 | } else { 47 | toc.classList.add("open") 48 | } 49 | }) 50 | 51 | document.querySelectorAll(".contents > .toc > ul a").forEach((node) => { 52 | let id = node.getAttribute("href").substring(1) 53 | DoxygenAwesomeInteractiveToc.headers.push({ 54 | node: node, 55 | headerNode: document.getElementById(id) 56 | }) 57 | 58 | document.getElementById("doc-content")?.addEventListener("scroll",this.throttle(DoxygenAwesomeInteractiveToc.update, 100)) 59 | }) 60 | DoxygenAwesomeInteractiveToc.update() 61 | } 62 | }) 63 | } 64 | 65 | static update() { 66 | let active = DoxygenAwesomeInteractiveToc.headers[0]?.node 67 | DoxygenAwesomeInteractiveToc.headers.forEach((header) => { 68 | let position = header.headerNode.getBoundingClientRect().top 69 | header.node.classList.remove("active") 70 | header.node.classList.remove("aboveActive") 71 | if(position < DoxygenAwesomeInteractiveToc.topOffset) { 72 | active = header.node 73 | active?.classList.add("aboveActive") 74 | } 75 | }) 76 | active?.classList.add("active") 77 | active?.classList.remove("aboveActive") 78 | } 79 | 80 | static throttle(func, delay) { 81 | let lastCall = 0; 82 | return function (...args) { 83 | const now = new Date().getTime(); 84 | if (now - lastCall < delay) { 85 | return; 86 | } 87 | lastCall = now; 88 | return setTimeout(() => {func(...args)}, delay); 89 | }; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /test/general/utils/pqueue_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "pqueue_tag.h" 6 | #include "tag.h" 7 | 8 | static void trivial(void) { 9 | // Create an event queue. 10 | pqueue_tag_t* q = pqueue_tag_init(1); 11 | assert(q != NULL); 12 | assert(pqueue_is_valid((pqueue_t*)q)); 13 | pqueue_print((pqueue_t*)q, NULL); 14 | pqueue_tag_free(q); 15 | } 16 | 17 | static void insert_on_queue(pqueue_tag_t* q) { 18 | tag_t t1 = {.time = USEC(3), .microstep = 0}; 19 | tag_t t2 = {.time = USEC(2), .microstep = 1}; 20 | tag_t t3 = {.time = USEC(2), .microstep = 0}; 21 | tag_t t4 = {.time = USEC(1), .microstep = 2}; 22 | assert(!pqueue_tag_insert_tag(q, t1)); 23 | assert(!pqueue_tag_insert_tag(q, t2)); 24 | assert(!pqueue_tag_insert_tag(q, t3)); 25 | 26 | assert(!pqueue_tag_insert_if_no_match(q, t4)); 27 | assert(pqueue_tag_insert_if_no_match(q, t1)); 28 | assert(pqueue_tag_insert_if_no_match(q, t4)); 29 | printf("======== Contents of the queue:\n"); 30 | pqueue_print((pqueue_t*)q, NULL); 31 | assert(pqueue_tag_size(q) == 4); 32 | } 33 | 34 | static void find_from_queue(pqueue_tag_t* q) { 35 | tag_t t1 = {.time = USEC(3), .microstep = 0}; 36 | tag_t t2 = {.time = USEC(2), .microstep = 1}; 37 | tag_t t3 = {.time = USEC(2), .microstep = 0}; 38 | tag_t t4 = {.time = USEC(1), .microstep = 2}; 39 | tag_t t5 = {.time = USEC(0), .microstep = 0}; 40 | tag_t t6 = {.time = USEC(3), .microstep = 2}; 41 | assert(pqueue_tag_find_with_tag(q, t1) != NULL); 42 | assert(pqueue_tag_find_with_tag(q, t2) != NULL); 43 | assert(pqueue_tag_find_with_tag(q, t3) != NULL); 44 | assert(pqueue_tag_find_with_tag(q, t4) != NULL); 45 | assert(pqueue_tag_find_with_tag(q, t5) == NULL); 46 | assert(pqueue_tag_find_with_tag(q, t6) == NULL); 47 | } 48 | 49 | static void insert_if_no_match(pqueue_tag_t* q) { 50 | size_t size = pqueue_tag_size(q); 51 | tag_t t1 = {.time = USEC(3), .microstep = 0}; 52 | tag_t t4 = {.time = USEC(1), .microstep = 2}; 53 | // Return value is non-zero on failure to insert: 54 | assert(pqueue_tag_insert_if_no_match(q, t1)); 55 | assert(pqueue_tag_insert_if_no_match(q, t4)); 56 | assert(size == pqueue_tag_size(q)); 57 | } 58 | 59 | static void pop_from_queue(pqueue_tag_t* q) { 60 | tag_t t1_back = pqueue_tag_pop_tag(q); 61 | assert(t1_back.time == USEC(1)); 62 | assert(t1_back.microstep == 2); 63 | tag_t t2_back = pqueue_tag_pop_tag(q); 64 | assert(t2_back.time == USEC(2)); 65 | assert(t2_back.microstep == 0); 66 | tag_t t3_back = pqueue_tag_pop_tag(q); 67 | assert(t3_back.time == USEC(2)); 68 | assert(t3_back.microstep == 1); 69 | tag_t t4_back = pqueue_tag_pop_tag(q); 70 | assert(t4_back.time == USEC(3)); 71 | assert(t4_back.microstep == 0); 72 | } 73 | 74 | static void pop_empty(pqueue_tag_t* q) { 75 | assert(pqueue_tag_size(q) == 0); 76 | assert(pqueue_tag_pop(q) == NULL); 77 | } 78 | 79 | static void remove_from_queue(pqueue_tag_t* q, pqueue_tag_element_t* e1, pqueue_tag_element_t* e2) { 80 | assert(pqueue_tag_insert(q, e1) == 0); 81 | assert(pqueue_tag_insert(q, e2) == 0); 82 | pqueue_tag_remove(q, e1); 83 | assert(pqueue_tag_peek(q) == e2); 84 | assert(pqueue_tag_size(q) == 1); 85 | } 86 | 87 | int main() { 88 | trivial(); 89 | // Create an event queue. 90 | pqueue_tag_t* q = pqueue_tag_init(2); 91 | 92 | insert_on_queue(q); 93 | find_from_queue(q); 94 | insert_if_no_match(q); 95 | pop_from_queue(q); 96 | pop_empty(q); 97 | 98 | pqueue_tag_element_t e1 = {.tag = {.time = USEC(3), .microstep = 0}, .pos = 0, .is_dynamic = 0}; 99 | pqueue_tag_element_t e2 = {.tag = {.time = USEC(2), .microstep = 0}, .pos = 0, .is_dynamic = 0}; 100 | 101 | remove_from_queue(q, &e1, &e2); 102 | 103 | pqueue_tag_free(q); 104 | } 105 | -------------------------------------------------------------------------------- /docs/assets/doxygen-awesome-sidebar-only.css: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2021 - 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | html { 31 | /* side nav width. MUST be = `TREEVIEW_WIDTH`. 32 | * Make sure it is wide enough to contain the page title (logo + title + version) 33 | */ 34 | --side-nav-fixed-width: 335px; 35 | --menu-display: none; 36 | 37 | --top-height: 120px; 38 | --toc-sticky-top: -25px; 39 | --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px); 40 | } 41 | 42 | #projectname { 43 | white-space: nowrap; 44 | } 45 | 46 | 47 | @media screen and (min-width: 768px) { 48 | html { 49 | --searchbar-background: var(--page-background-color); 50 | } 51 | 52 | #side-nav { 53 | min-width: var(--side-nav-fixed-width); 54 | max-width: var(--side-nav-fixed-width); 55 | top: var(--top-height); 56 | overflow: visible; 57 | } 58 | 59 | #nav-tree, #side-nav { 60 | height: calc(100vh - var(--top-height)) !important; 61 | } 62 | 63 | #nav-tree { 64 | padding: 0; 65 | } 66 | 67 | #top { 68 | display: block; 69 | border-bottom: none; 70 | height: var(--top-height); 71 | margin-bottom: calc(0px - var(--top-height)); 72 | max-width: var(--side-nav-fixed-width); 73 | overflow: hidden; 74 | background: var(--side-nav-background); 75 | } 76 | #main-nav { 77 | float: left; 78 | padding-right: 0; 79 | } 80 | 81 | .ui-resizable-handle { 82 | cursor: default; 83 | width: 1px !important; 84 | background: var(--separator-color); 85 | box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color); 86 | } 87 | 88 | #nav-path { 89 | position: fixed; 90 | right: 0; 91 | left: var(--side-nav-fixed-width); 92 | bottom: 0; 93 | width: auto; 94 | } 95 | 96 | #doc-content { 97 | height: calc(100vh - 31px) !important; 98 | padding-bottom: calc(3 * var(--spacing-large)); 99 | padding-top: calc(var(--top-height) - 80px); 100 | box-sizing: border-box; 101 | margin-left: var(--side-nav-fixed-width) !important; 102 | } 103 | 104 | #MSearchBox { 105 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium))); 106 | } 107 | 108 | #MSearchField { 109 | width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px); 110 | } 111 | 112 | #MSearchResultsWindow { 113 | left: var(--spacing-medium) !important; 114 | right: auto; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /trace/api/types/trace_types.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file trace_types.h 3 | * @author Peter Donovan 4 | * 5 | * @brief Definitions that are needed by both implementors and callers of the 6 | * trace API regardless of whether tracing is enabled at compile time. 7 | * @ingroup Tracing 8 | */ 9 | 10 | #ifndef TRACE_TYPES_H 11 | #define TRACE_TYPES_H 12 | 13 | /** 14 | * @brief Trace event types. 15 | * @ingroup Tracing 16 | * 17 | * If you update this, be sure to update the string representation below. 18 | * Also, create a tracepoint function for each event type. 19 | */ 20 | typedef enum { 21 | reaction_starts, 22 | reaction_ends, 23 | reaction_deadline_missed, 24 | schedule_called, 25 | user_event, 26 | user_value, 27 | worker_wait_starts, 28 | worker_wait_ends, 29 | scheduler_advancing_time_starts, 30 | scheduler_advancing_time_ends, 31 | federated, // Everything below this is for tracing federated interactions. 32 | // Sending messages 33 | send_ACK, 34 | send_FAILED, 35 | send_TIMESTAMP, 36 | send_NET, 37 | send_LTC, 38 | send_STOP_REQ, 39 | send_STOP_REQ_REP, 40 | send_STOP_GRN, 41 | send_FED_ID, 42 | send_PTAG, 43 | send_TAG, 44 | send_REJECT, 45 | send_RESIGN, 46 | send_PORT_ABS, 47 | send_CLOSE_RQ, 48 | send_TAGGED_MSG, 49 | send_P2P_TAGGED_MSG, 50 | send_MSG, 51 | send_P2P_MSG, 52 | send_ADR_AD, 53 | send_ADR_QR, 54 | send_DNET, 55 | // Receiving messages 56 | receive_ACK, 57 | receive_FAILED, 58 | receive_TIMESTAMP, 59 | receive_NET, 60 | receive_LTC, 61 | receive_STOP_REQ, 62 | receive_STOP_REQ_REP, 63 | receive_STOP_GRN, 64 | receive_FED_ID, 65 | receive_PTAG, 66 | receive_TAG, 67 | receive_REJECT, 68 | receive_RESIGN, 69 | receive_PORT_ABS, 70 | receive_CLOSE_RQ, 71 | receive_TAGGED_MSG, 72 | receive_P2P_TAGGED_MSG, 73 | receive_MSG, 74 | receive_P2P_MSG, 75 | receive_ADR_AD, 76 | receive_ADR_QR, 77 | receive_DNET, 78 | receive_UNIDENTIFIED, 79 | NUM_EVENT_TYPES 80 | } trace_event_t; 81 | 82 | /** 83 | * @brief String description of event types. 84 | * @ingroup Tracing 85 | */ 86 | static const char* trace_event_names[] = { 87 | "Reaction starts", 88 | "Reaction ends", 89 | "Reaction deadline missed", 90 | "Schedule called", 91 | "User-defined event", 92 | "User-defined valued event", 93 | "Worker wait starts", 94 | "Worker wait ends", 95 | "Scheduler advancing time starts", 96 | "Scheduler advancing time ends", 97 | "Federated marker", 98 | // Sending messages 99 | "Sending ACK", 100 | "Sending FAILED", 101 | "Sending TIMESTAMP", 102 | "Sending NET", 103 | "Sending LTC", 104 | "Sending STOP_REQ", 105 | "Sending STOP_REQ_REP", 106 | "Sending STOP_GRN", 107 | "Sending FED_ID", 108 | "Sending PTAG", 109 | "Sending TAG", 110 | "Sending REJECT", 111 | "Sending RESIGN", 112 | "Sending PORT_ABS", 113 | "Sending CLOSE_RQ", 114 | "Sending TAGGED_MSG", 115 | "Sending P2P_TAGGED_MSG", 116 | "Sending MSG", 117 | "Sending P2P_MSG", 118 | "Sending ADR_AD", 119 | "Sending ADR_QR", 120 | "Sending DNET", 121 | // Receiving messages 122 | "Receiving ACK", 123 | "Receiving FAILED", 124 | "Receiving TIMESTAMP", 125 | "Receiving NET", 126 | "Receiving LTC", 127 | "Receiving STOP_REQ", 128 | "Receiving STOP_REQ_REP", 129 | "Receiving STOP_GRN", 130 | "Receiving FED_ID", 131 | "Receiving PTAG", 132 | "Receiving TAG", 133 | "Receiving REJECT", 134 | "Receiving RESIGN", 135 | "Receiving PORT_ABS", 136 | "Receiving CLOSE_RQ", 137 | "Receiving TAGGED_MSG", 138 | "Receiving P2P_TAGGED_MSG", 139 | "Receiving MSG", 140 | "Receiving P2P_MSG", 141 | "Receiving ADR_AD", 142 | "Receiving ADR_QR", 143 | "Receiving DNET", 144 | "Receiving UNIDENTIFIED", 145 | }; 146 | 147 | static inline void _suppress_unused_variable_warning_for_static_variable() { (void)trace_event_names; } 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /docs/assets/doxygen-awesome-tabs.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Doxygen Awesome 4 | https://github.com/jothepro/doxygen-awesome-css 5 | 6 | MIT License 7 | 8 | Copyright (c) 2023 jothepro 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | class DoxygenAwesomeTabs { 31 | 32 | static init() { 33 | window.addEventListener("load", () => { 34 | document.querySelectorAll(".tabbed:not(:empty)").forEach((tabbed, tabbedIndex) => { 35 | let tabLinkList = [] 36 | tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => { 37 | tab.id = "tab_" + tabbedIndex + "_" + tabIndex 38 | let header = tab.querySelector(".tab-title") 39 | let tabLink = document.createElement("button") 40 | tabLink.classList.add("tab-button") 41 | tabLink.appendChild(header) 42 | header.title = header.textContent 43 | tabLink.addEventListener("click", () => { 44 | tabbed.querySelectorAll(":scope > ul > li").forEach((tab) => { 45 | tab.classList.remove("selected") 46 | }) 47 | tabLinkList.forEach((tabLink) => { 48 | tabLink.classList.remove("active") 49 | }) 50 | tab.classList.add("selected") 51 | tabLink.classList.add("active") 52 | }) 53 | tabLinkList.push(tabLink) 54 | if(tabIndex == 0) { 55 | tab.classList.add("selected") 56 | tabLink.classList.add("active") 57 | } 58 | }) 59 | let tabsOverview = document.createElement("div") 60 | tabsOverview.classList.add("tabs-overview") 61 | let tabsOverviewContainer = document.createElement("div") 62 | tabsOverviewContainer.classList.add("tabs-overview-container") 63 | tabLinkList.forEach((tabLink) => { 64 | tabsOverview.appendChild(tabLink) 65 | }) 66 | tabsOverviewContainer.appendChild(tabsOverview) 67 | tabbed.before(tabsOverviewContainer) 68 | 69 | function resize() { 70 | let maxTabHeight = 0 71 | tabbed.querySelectorAll(":scope > ul > li").forEach((tab, tabIndex) => { 72 | let visibility = tab.style.display 73 | tab.style.display = "block" 74 | maxTabHeight = Math.max(tab.offsetHeight, maxTabHeight) 75 | tab.style.display = visibility 76 | }) 77 | tabbed.style.height = `${maxTabHeight + 10}px` 78 | } 79 | 80 | resize() 81 | new ResizeObserver(resize).observe(tabbed) 82 | }) 83 | }) 84 | 85 | } 86 | 87 | static resize(tabbed) { 88 | 89 | } 90 | } -------------------------------------------------------------------------------- /core/port.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * 5 | * @brief Header file for macros, functions, and structs for optimized sparse I/O 6 | * through multiports. 7 | */ 8 | #include 9 | 10 | #include "port.h" 11 | #include "vector.h" 12 | 13 | /** 14 | * Compare two non-negative integers pointed to. Return -1 if a < b, 0 if a == b, 15 | * and 1 if a > b. 16 | * @param a Pointer to the first integer. 17 | * @param b Pointer to the second integer. 18 | */ 19 | int compare_sizes(const void* a, const void* b) { 20 | if (*(size_t*)a < *(size_t*)b) { 21 | return -1; 22 | } else if (*(size_t*)a > *(size_t*)b) { 23 | return 1; 24 | } else { 25 | return 0; 26 | } 27 | } 28 | 29 | /** 30 | * Given an array of pointers to port structs, return an iterator 31 | * that can be used to iterate over the present channels. 32 | * @param port An array of pointers to port structs. 33 | * @param width The width of the multiport (or a negative number if not 34 | * a multiport). 35 | */ 36 | lf_multiport_iterator_t _lf_multiport_iterator_impl(lf_port_base_t** port, int width) { 37 | // NOTE: Synchronization is not required because all writers must have 38 | // completed by the time this is invoked. 39 | struct lf_multiport_iterator_t result = 40 | (lf_multiport_iterator_t){.next = -1, 41 | .idx = -1, // Indicate that lf_multiport_next() has not been called. 42 | .port = port, 43 | .width = width}; 44 | if (width <= 0) 45 | return result; 46 | if (port[0]->sparse_record && port[0]->sparse_record->size >= 0) { 47 | // Sparse record is enabled and ready to use. 48 | if (port[0]->sparse_record->size > 0) { 49 | // Need to sort it first (if the length is greater than 1). 50 | if (port[0]->sparse_record->size > 1) { 51 | qsort(&port[0]->sparse_record->present_channels[0], (size_t)port[0]->sparse_record->size, sizeof(size_t), 52 | &compare_sizes); 53 | } 54 | // NOTE: Following cast is unsafe if there more than 2^31 channels. 55 | result.next = (int)port[0]->sparse_record->present_channels[0]; 56 | } 57 | return result; 58 | } 59 | // Fallback is to iterate over all port structs representing channels. 60 | int start = 0; 61 | while (start < width) { 62 | if (port[start]->is_present) { 63 | result.next = start; 64 | return result; 65 | } 66 | start++; 67 | } 68 | return result; 69 | } 70 | 71 | /** 72 | * Return the channel number of the next present input on the multiport 73 | * or -1 if there are no more present channels. 74 | * @param iterator The iterator. 75 | */ 76 | int lf_multiport_next(lf_multiport_iterator_t* iterator) { 77 | // If the iterator has not been used, return next. 78 | if (iterator->idx < 0) { 79 | iterator->idx = 0; 80 | return iterator->next; 81 | } 82 | // If the iterator is already exhausted, return. 83 | if (iterator->next < 0 || iterator->width <= 0) { 84 | return -1; 85 | } 86 | struct lf_sparse_io_record_t* sparse_record = iterator->port[iterator->idx]->sparse_record; 87 | if (sparse_record && sparse_record->size >= 0) { 88 | // Sparse record is enabled and ready to use. 89 | iterator->idx++; 90 | if (iterator->idx >= sparse_record->size) { 91 | // No more present channels. 92 | iterator->next = -1; 93 | } else { 94 | // NOTE: Following cast is unsafe if there more than 2^31 channels. 95 | iterator->next = (int)sparse_record->present_channels[iterator->idx]; 96 | } 97 | return iterator->next; 98 | } else { 99 | // Fall back to iterate over all port structs representing channels. 100 | int start = iterator->next + 1; 101 | while (start < iterator->width) { 102 | if (iterator->port[start]->is_present) { 103 | iterator->next = start; 104 | return iterator->next; 105 | } 106 | start++; 107 | } 108 | // No more present channels found. 109 | iterator->next = -1; 110 | return iterator->next; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /util/generics.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file generics.h 3 | * @author Muhammad Khubaib Umer 4 | * 5 | * @brief This file provides macros for Generic Reactors in C-Target. 6 | * @ingroup Utilities 7 | * 8 | * The macros are wrappers on compiler builtin and provide the 9 | * programmer to auto-infer types and conditionals based on types 10 | */ 11 | 12 | #ifndef GENERICS_H 13 | #define GENERICS_H 14 | 15 | // If buitin are not available on target toolchain we may not be able to support generics 16 | #if defined __has_builtin 17 | // Auto-Deduce variable type based on assigned value 18 | #define var __auto_type 19 | 20 | /** 21 | * @brief Check whether the types of both `a` and `b` are same. 22 | * @ingroup Utilities 23 | * 24 | * @param a The first value to compare. 25 | * @param b The second value to compare. 26 | * @return True if the types are the same, false otherwise. 27 | */ 28 | #define lf_is_same_type(a, b) __builtin_types_compatible_p(__typeof__(a), __typeof__(b)) 29 | 30 | /** 31 | * @brief Check whether the type of `b` is same as the specified `typename`. 32 | * @ingroup Utilities 33 | * 34 | * @param typename The type to compare against. 35 | * @param b The value to compare. 36 | * @return True if the types are the same, false otherwise. 37 | */ 38 | #define lf_is_same(typename, b) __builtin_types_compatible_p(typename, __typeof__(b)) 39 | 40 | /** 41 | * @brief Check whether the type of `typename_a` is same as the type of `typename_b`. 42 | * @ingroup Utilities 43 | * 44 | * @param typename_a The first type to compare against. 45 | * @param typename_b The second type to compare against. 46 | * @return True if the types are the same, false otherwise. 47 | */ 48 | #define lf_is_type_equal(typename_a, typename_b) __builtin_types_compatible_p(typename_a, typename_b) 49 | 50 | /** 51 | * @brief Check whether the passed variable `p` is an array or a pointer. 52 | * @ingroup Utilities 53 | * 54 | * @param p The variable to check. 55 | * @return True if the variable is an array or a pointer, false otherwise. 56 | */ 57 | #define lf_is_pointer_or_array(p) (__builtin_classify_type(p) == 5) 58 | 59 | /** 60 | * @brief Decay the specified pointer. 61 | * @ingroup Utilities 62 | * 63 | * @param p The pointer to decay. 64 | * @return The decayed pointer. 65 | */ 66 | #define lf_decay(p) (&*__builtin_choose_expr(lf_is_pointer_or_array(p), p, NULL)) 67 | 68 | /** 69 | * @brief Check whether the passed variable `p` is a pointer. 70 | * @ingroup Utilities 71 | * 72 | * @param p The variable to check. 73 | * @return True if the variable is a pointer, false otherwise. 74 | */ 75 | #define lf_is_pointer(p) lf_is_same_type(p, lf_decay(p)) 76 | 77 | /** 78 | * @brief Return the pointer for specified `p`. 79 | * @ingroup Utilities 80 | * 81 | * @param p The variable to get the pointer for. 82 | * @return The pointer for the specified variable. 83 | */ 84 | #define lf_get_pointer(p) __builtin_choose_expr(lf_is_pointer(p), p, &p) 85 | 86 | /** 87 | * @brief Check types for both `left` and `right` and return appropriate value based on `left` type. 88 | * @ingroup Utilities 89 | * 90 | * @param left The left value to check. 91 | * @param right The right value to check. 92 | * @return The appropriate value based on `left` type. 93 | */ 94 | #define lf_to_left_type(left, right) \ 95 | __builtin_choose_expr(lf_is_pointer_or_array(left), \ 96 | __builtin_choose_expr(lf_is_pointer_or_array(right), (right), &(right)), \ 97 | __builtin_choose_expr(lf_is_pointer_or_array(right), *(right), (right))) 98 | 99 | #else // buitin are not available 100 | 101 | #define var 102 | #define lf_is_same_type(a, b) 103 | #define lf_is_same(typename, b) 104 | #define lf_is_pointer_or_array(p) 105 | #define lf_decay(p) 106 | #define lf_is_pointer(p) 107 | #define lf_get_pointer(p) 108 | #define lf_to_left_type(left, right) 109 | 110 | #endif // __has_builtin 111 | 112 | #endif // GENERICS_H 113 | -------------------------------------------------------------------------------- /low_level_platform/impl/src/lf_linux_support.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @author Edward A. Lee 4 | * @author Soroush Bateni 5 | * @author Marten Lohstroh 6 | * @author Erling Jellum 7 | * 8 | * @brief Platform support for the Linux operating system. 9 | */ 10 | 11 | #ifdef PLATFORM_Linux 12 | /* MacOS API support for the C target of Lingua Franca. */ 13 | 14 | #define _GNU_SOURCE // Needed to get access to Linux thread-scheduling API 15 | #include "platform/lf_linux_support.h" 16 | #include "platform/lf_platform_util.h" 17 | #include "low_level_platform.h" 18 | 19 | #include "platform/lf_unix_clock_support.h" 20 | 21 | #if defined LF_SINGLE_THREADED 22 | #include "lf_os_single_threaded_support.c" 23 | #else 24 | #include "lf_POSIX_threads_support.c" 25 | 26 | int lf_thread_set_cpu(lf_thread_t thread, size_t cpu_number) { 27 | // Create a CPU-set consisting of only the desired CPU 28 | cpu_set_t cpu_set; 29 | CPU_ZERO(&cpu_set); 30 | CPU_SET(cpu_number, &cpu_set); 31 | 32 | return pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set); 33 | } 34 | 35 | int lf_thread_set_priority(lf_thread_t thread, int priority) { 36 | int posix_policy, min_pri, max_pri, final_priority, res; 37 | struct sched_param schedparam; 38 | 39 | if (priority > LF_SCHED_MAX_PRIORITY || priority < LF_SCHED_MIN_PRIORITY) { 40 | return -1; 41 | } 42 | 43 | // Get the current scheduling policy 44 | res = pthread_getschedparam(thread, &posix_policy, &schedparam); 45 | if (res != 0) { 46 | return res; 47 | } 48 | 49 | min_pri = sched_get_priority_min(posix_policy); 50 | max_pri = sched_get_priority_max(posix_policy); 51 | if (min_pri == -1 || max_pri == -1) { 52 | return -1; 53 | } 54 | 55 | final_priority = map_priorities(priority, min_pri, max_pri); 56 | if (final_priority < 0) { 57 | return -1; 58 | } 59 | 60 | return pthread_setschedprio(thread, final_priority); 61 | } 62 | 63 | int lf_thread_set_scheduling_policy(lf_thread_t thread, lf_scheduling_policy_t* policy) { 64 | int posix_policy, res; 65 | bool set_priority; 66 | struct sched_param schedparam; 67 | 68 | // Get the current scheduling policy 69 | res = pthread_getschedparam(thread, &posix_policy, &schedparam); 70 | if (res != 0) { 71 | return res; 72 | } 73 | 74 | // Update the policy, and initially set the priority to max. 75 | // The priority value is later updated. Initializing it 76 | // is just to avoid code duplication. 77 | switch (policy->policy) { 78 | case LF_SCHED_FAIR: 79 | posix_policy = SCHED_OTHER; 80 | schedparam.sched_priority = 0; 81 | set_priority = false; 82 | break; 83 | case LF_SCHED_TIMESLICE: 84 | posix_policy = SCHED_RR; 85 | schedparam.sched_priority = sched_get_priority_max(SCHED_RR); 86 | set_priority = true; 87 | break; 88 | case LF_SCHED_PRIORITY: 89 | posix_policy = SCHED_FIFO; 90 | schedparam.sched_priority = sched_get_priority_max(SCHED_FIFO); 91 | set_priority = true; 92 | break; 93 | default: 94 | return -1; 95 | break; 96 | } 97 | 98 | // Write it back 99 | res = pthread_setschedparam(thread, posix_policy, &schedparam); 100 | if (res != 0) { 101 | return res; 102 | } 103 | 104 | // Set the priority of we chose a RT scheduler 105 | if (set_priority) { 106 | res = lf_thread_set_priority(thread, policy->priority); 107 | if (res != 0) { 108 | return res; 109 | } 110 | } 111 | 112 | return 0; 113 | } 114 | #endif 115 | 116 | int lf_sleep(interval_t sleep_duration) { 117 | const struct timespec tp = convert_ns_to_timespec(sleep_duration); 118 | struct timespec remaining; 119 | return nanosleep((const struct timespec*)&tp, (struct timespec*)&remaining); 120 | } 121 | 122 | int _lf_interruptable_sleep_until_locked(environment_t* env, instant_t wakeup_time) { 123 | (void)env; 124 | interval_t sleep_duration = wakeup_time - lf_time_physical(); 125 | 126 | if (sleep_duration <= 0) { 127 | return 0; 128 | } else { 129 | return lf_sleep(sleep_duration); 130 | } 131 | } 132 | 133 | int lf_nanosleep(interval_t sleep_duration) { return lf_sleep(sleep_duration); } 134 | #endif 135 | -------------------------------------------------------------------------------- /low_level_platform/api/platform/lf_atomic.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lf_atomic.h 3 | * @brief Platform-independent atomic operations API for Lingua Franca. 4 | * 5 | * @author Erling Rennemo Jellum 6 | * 7 | * This file defines the LF atomic API that provides platform-independent 8 | * atomic operations for memory access. These functions will have 9 | * platform-dependent implementations but provide a consistent interface 10 | * across all supported platforms. 11 | */ 12 | #ifndef LF_ATOMICS_H 13 | #define LF_ATOMICS_H 14 | 15 | #include 16 | #include 17 | 18 | /** 19 | * @brief Atomically fetch an integer from memory and add a value to it. 20 | * Return the value that was previously in memory. 21 | * 22 | * @param ptr A pointer to the memory location. 23 | * @param val The value to be added. 24 | * @return The value previously in memory. 25 | */ 26 | int lf_atomic_fetch_add(int* ptr, int val); 27 | 28 | /** 29 | * @brief Atomically fetch 64-bit integer from memory and add a value to it. 30 | * Return the value that was previously in memory. 31 | * 32 | * @param ptr A pointer to the memory location. 33 | * @param val The value to be added. 34 | * @return The value previously in memory. 35 | */ 36 | int64_t lf_atomic_fetch_add64(int64_t* ptr, int64_t val); 37 | 38 | /** 39 | * @brief Atomically fetch an integer from memory and add a value to it. 40 | * Return the new value of the memory. 41 | * 42 | * @param ptr A pointer to the memory location. 43 | * @param val The value to be added. 44 | * @return The new value in memory. 45 | */ 46 | int lf_atomic_add_fetch(int* ptr, int val); 47 | 48 | /** 49 | * @brief Atomically fetch a 64-bit integer from memory and add a value to it. 50 | * Return the new value of the memory. 51 | * 52 | * @param ptr A pointer to the memory location. 53 | * @param val The value to be added. 54 | * @return The new value in memory. 55 | */ 56 | int64_t lf_atomic_add_fetch64(int64_t* ptr, int64_t val); 57 | 58 | /** 59 | * @brief Atomically perform a compare-and-swap operation on a 32 bit integer in 60 | * memory. If the value in memory is equal to `oldval` replace it with `newval` 61 | * and return true. If not return false. 62 | * 63 | * @param ptr A pointer to the memory location. 64 | * @param oldval The value to compare with. 65 | * @param newval The value to swap in. 66 | * @return Whether a swap was performed or not. 67 | */ 68 | bool lf_atomic_bool_compare_and_swap(int* ptr, int oldval, int newval); 69 | 70 | /** 71 | * @brief Atomically perform a compare-and-swap operation on a 64 bit integer in 72 | * memory. If the value in memory is equal to `oldval` replace it with `newval` 73 | * and return true. If not return false. 74 | * 75 | * @param ptr A pointer to the memory location. 76 | * @param oldval The value to compare with. 77 | * @param newval The value to swap in. 78 | * @return Whether a swap was performed or not. 79 | */ 80 | bool lf_atomic_bool_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval); 81 | 82 | /** 83 | * @brief Atomically perform a compare-and-swap operation on an integer in 84 | * memory. If the value in memory is equal to `oldval` replace it with `newval`. 85 | * Return the content of the memory before the potential swap operation is 86 | * performed. 87 | * 88 | * @param ptr A pointer to the memory location. 89 | * @param oldval The value to compare with. 90 | * @param newval The value to swap in. 91 | * @return The value in memory prior to the swap. 92 | */ 93 | int lf_atomic_val_compare_and_swap(int* ptr, int oldval, int newval); 94 | 95 | /** 96 | * @brief Atomically perform a compare-and-swap operation on a 64 bit integer in 97 | * memory. If the value in memory is equal to `oldval` replace it with `newval`. 98 | * Return the content of the memory before the potential swap operation is 99 | * performed. 100 | * 101 | * @param ptr A pointer to the memory location. 102 | * @param oldval The value to compare with. 103 | * @param newval The value to swap in. 104 | * @return The value in memory prior to the swap. 105 | */ 106 | int64_t lf_atomic_val_compare_and_swap64(int64_t* ptr, int64_t oldval, int64_t newval); 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | types: [synchronize, opened, reopened, ready_for_review, converted_to_draft] 9 | workflow_dispatch: 10 | merge_group: 11 | 12 | concurrency: 13 | group: ci-${{ github.ref }}-${{ github.event_path }} 14 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} 15 | 16 | jobs: 17 | check-labels: 18 | uses: lf-lang/lingua-franca/.github/workflows/check-labels.yml@master 19 | if: ${{ github.event_name == 'pull_request' }} 20 | 21 | unit-tests-single: 22 | uses: ./.github/workflows/unit-tests.yml 23 | with: 24 | cmake-args: '-UNUMBER_OF_WORKERS -DLF_SINGLE_THREADED=1' 25 | 26 | unit-tests-multi: 27 | uses: ./.github/workflows/unit-tests.yml 28 | with: 29 | cmake-args: '-DNUMBER_OF_WORKERS=4 -ULF_SINGLE_THREADED' 30 | 31 | build-rti: 32 | uses: ./.github/workflows/build-rti.yml 33 | 34 | build-trace-tools: 35 | uses: ./.github/workflows/build-trace-tools.yml 36 | 37 | fetch-lf: 38 | uses: lf-lang/lingua-franca/.github/workflows/extract-ref.yml@master 39 | with: 40 | file: 'lingua-franca-ref.txt' 41 | 42 | lf-default-arduino: 43 | needs: [fetch-lf] 44 | uses: lf-lang/lingua-franca/.github/workflows/c-arduino-tests.yml@master 45 | with: 46 | runtime-ref: ${{ github.ref }} 47 | compiler-ref: ${{ needs.fetch-lf.outputs.ref }} 48 | if: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'arduino') }} 49 | 50 | lf-default-zephyr: 51 | needs: [fetch-lf] 52 | uses: lf-lang/lingua-franca/.github/workflows/c-zephyr-tests.yml@master 53 | with: 54 | runtime-ref: ${{ github.ref }} 55 | compiler-ref: ${{ needs.fetch-lf.outputs.ref }} 56 | if: ${{ !github.event.pull_request.draft ||contains( github.event.pull_request.labels.*.name, 'zephyr') }} 57 | 58 | lf-default-flexpret: 59 | needs: [fetch-lf] 60 | uses: lf-lang/lingua-franca/.github/workflows/c-flexpret-tests.yml@master 61 | with: 62 | runtime-ref: ${{ github.ref }} 63 | compiler-ref: ${{ needs.fetch-lf.outputs.ref }} 64 | if: ${{ !github.event.pull_request.draft ||contains( github.event.pull_request.labels.*.name, 'flexpret') }} 65 | 66 | lf-default: 67 | needs: [fetch-lf] 68 | uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master 69 | with: 70 | runtime-ref: ${{ github.ref }} 71 | compiler-ref: ${{ needs.fetch-lf.outputs.ref }} 72 | all-platforms: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'mac') || contains( github.event.pull_request.labels.*.name, 'windows') }} 73 | 74 | lf-python: 75 | needs: [fetch-lf] 76 | uses: lf-lang/lingua-franca/.github/workflows/py-tests.yml@master 77 | with: 78 | reactor-c-ref: ${{ github.ref }} 79 | compiler-ref: ${{ needs.fetch-lf.outputs.ref }} 80 | if: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'python') }} 81 | 82 | lf-gedf-np: 83 | needs: [fetch-lf] 84 | uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master 85 | with: 86 | runtime-ref: ${{ github.ref }} 87 | compiler-ref: ${{ needs.fetch-lf.outputs.ref }} 88 | scheduler: GEDF_NP 89 | all-platforms: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'mac') || contains( github.event.pull_request.labels.*.name, 'windows') }} 90 | if: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'schedulers') }} 91 | 92 | lf-adaptive: 93 | needs: [fetch-lf] 94 | uses: lf-lang/lingua-franca/.github/workflows/c-tests.yml@master 95 | with: 96 | runtime-ref: ${{ github.ref }} 97 | compiler-ref: ${{ needs.fetch-lf.outputs.ref }} 98 | scheduler: ADAPTIVE 99 | all-platforms: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'mac') || contains( github.event.pull_request.labels.*.name, 'windows') }} 100 | if: ${{ !github.event.pull_request.draft || contains( github.event.pull_request.labels.*.name, 'schedulers') }} 101 | --------------------------------------------------------------------------------