Skip to content

Conversation

@koudis
Copy link
Member

@koudis koudis commented Oct 22, 2025

Summary by CodeRabbit

  • New Features

    • Added asynchronous module execution capabilities for improved system performance
    • New configuration option to specify module binary paths
  • Improvements

    • Updated C++ standard to version 23 for enhanced language features
    • Upgraded and added dependencies for improved stability and functionality

@coderabbitai
Copy link

coderabbitai bot commented Oct 22, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The PR title "Baf 1155/use async client" does reference the primary focus of the changeset—the introduction of async functionality for module handling. However, the descriptive portion "use async client" is somewhat vague without additional context. A teammate scanning git history would not immediately understand what "async client" refers to (e.g., is it for module loading, general communication, or something else?), nor would they clearly grasp that this introduces an async module library handler implementation using Aeron. While the title is not completely generic, it lacks the clarity and specificity needed to fully convey the primary change at a glance. Consider revising the title to be more specific and descriptive, such as "Add async module library handler using Aeron" or "Support async module loading via inter-process communication." This would make the primary change immediately clear to team members reviewing the commit history without requiring reference to the ticket number or additional context.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

*
* @param path path to the library
*/
virtual void loadLibrary(const std::filesystem::path &path) = 0;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why for some functions the documentation is missing?


namespace bringauto::modules {

struct ConvertibleBufferReturn final {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each struct shall have its own file.

}
};

inline static const async_function_execution::FunctionDefinition getModuleNumberAsync {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function definitions shall be part of Fleet Protocol CPP - https://github.com/bringauto/fleet-protocol-cpp
(I guess not in the cxx namespace but in its own namespace?)

current_command_raw_buffer = current_command.getStructBuffer();
}

auto ret = aeronClient.callFunc(generateCommandAsync,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why ret is not checked if it contains value? What if the expected returns "error"? Check it in all occurrences please.

@@ -1,5 +1,7 @@
#include <ErrorAggregatorTests.hpp>
#include <testing_utils/DeviceIdentificationHelper.h>
#include <bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot find any related tests. Can we add unit tests for updates and verify that the updates do not "break" tests in a manner "It works, but it shall not"?

@koudis
Copy link
Member Author

koudis commented Oct 22, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Oct 22, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (10)
include/bringauto/modules/IModuleManagerLibraryHandler.hpp (1)

30-30: Add documentation for undocumented interface methods.

Several public virtual methods lack documentation comments: getModuleNumber, isDeviceTypeSupported, sendStatusCondition, statusDataValid, and commandDataValid. Since this is a new public interface, all methods should be documented to guide implementers and users.

Also applies to: 32-32, 34-34, 68-68, 70-70

resources/config/README.md (1)

25-26: Expand documentation to fully explain the two module loading modes.

The current documentation provides minimal information about module-binary-path. The previous review comment requested a more comprehensive explanation of what a ModuleBinary is and that there are now two ways of using modules in Module Gateway. Consider expanding this section to:

  1. Explain the difference between loading modules as shared libraries (local/synchronous) vs. using module binaries (async over shared memory)
  2. Describe when to use each approach
  3. Clarify the implications of leaving module-binary-path empty vs. providing a path
include/bringauto/settings/Settings.hpp (1)

36-39: Consider using std::filesystem::path for the path field.

A previous review comment suggested using std::filesystem::path instead of std::string for path fields. The new moduleBinaryPath field uses std::string, which is inconsistent with that recommendation. Using std::filesystem::path provides better type safety and path manipulation capabilities.

Note: This also applies to the existing modulePaths field, which could be addressed in a separate refactoring effort.

include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp (3)

15-51: Move structs to separate header files.

As noted in a previous review comment, ConvertibleBufferReturn and ConvertibleBuffer should each be in their own header files for better organization and maintainability.

Create:

  • include/bringauto/modules/ConvertibleBuffer.hpp
  • include/bringauto/modules/ConvertibleBufferReturn.hpp

53-105: Consider moving function definitions to fleet-protocol-cpp.

As mentioned in a previous review comment, the async function definitions should be part of the Fleet Protocol CPP library rather than being defined here, possibly in a dedicated namespace.


194-194: Address the TODO comment about tmpMutex_.

The TODO comment indicates incomplete design regarding thread safety. As noted in the implementation file review, clarify the thread safety requirements and either protect all concurrent accesses or document why selective protection is sufficient.

source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp (4)

12-28: Make aeronClient a class member or static member.

The global aeronClient introduces potential issues with lifetime management and makes it impossible to have multiple async handler instances with different configurations. This also relates to a previous review comment requesting it be part of the class.

Consider making it a static member of the class:

+// In header file
+class ModuleManagerLibraryHandlerAsync : public IModuleManagerLibraryHandler {
+private:
+    static async_function_execution::AsyncFunctionExecutor& getAeronClient();
+    // ... rest of class
+};

+// In implementation file
-async_function_execution::AsyncFunctionExecutor aeronClient {
+async_function_execution::AsyncFunctionExecutor& ModuleManagerLibraryHandlerAsync::getAeronClient() {
+    static async_function_execution::AsyncFunctionExecutor client {
-	async_function_execution::Config {
+        async_function_execution::Config {
-		.isProducer = true,
+            .isProducer = true,
-		.defaultTimeout = std::chrono::seconds(1)
+            .defaultTimeout = std::chrono::seconds(1)
-	},
+        },
-	async_function_execution::FunctionList {
+        async_function_execution::FunctionList {
-		getModuleNumberAsync,
+            getModuleNumberAsync,
             // ... rest of functions
-	}
-};
+        }
+    };
+    return client;
+}

15-15: Replace hardcoded timeout with a named constant.

The 1-second timeout is hardcoded. This should be a named constant, preferably defined in Constants.hpp alongside other timeout values, for better maintainability.

In include/bringauto/settings/Constants.hpp:

constexpr std::chrono::seconds aeron_async_call_timeout { 1 };

Then use it in the configuration:

-        .defaultTimeout = std::chrono::seconds(1)
+        .defaultTimeout = settings::aeron_async_call_timeout

54-54: Check return values before calling .value().

Multiple calls to aeronClient.callFunc(...).value() assume the optional contains a value. If an error occurs in the async call, .value() will throw std::bad_optional_access. This relates to a previous review comment requesting return value validation.

Apply defensive checks throughout:

-    return aeronClient.callFunc(getModuleNumberAsync).value();
+    auto result = aeronClient.callFunc(getModuleNumberAsync);
+    if (!result.has_value()) {
+        throw std::runtime_error("Failed to get module number from async call");
+    }
+    return result.value();

Apply similar pattern to all .value() calls in lines 54, 59, 75, 100, 123, 147, 157, 172, and 181.

Also applies to: 59-59, 75-75, 100-100, 123-123, 147-147, 157-157, 172-172, 181-181


58-58: Incomplete thread safety with tmpMutex_.

The mutex is only used in isDeviceTypeSupported, but other methods like generateCommand, aggregateStatus, etc., also call aeronClient.callFunc() and may be invoked concurrently. This relates to a previous review comment about thread safety. Either protect all calls or document why selective protection is sufficient.

Review the thread safety requirements for all methods:

#!/bin/bash
# Check which methods use aeronClient without mutex protection
rg -n "aeronClient\.callFunc" source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp -B5
🧹 Nitpick comments (5)
source/bringauto/modules/memory_management/source/memory_management.cpp (1)

17-21: Add null pointer validation for safety.

While delete[] on a nullptr is safe, the function should validate buffer_pointer itself is non-null before dereferencing to prevent undefined behavior.

Apply this diff:

 void deallocate(struct buffer *buffer_pointer){
+    if(buffer_pointer == nullptr){
+        return;
+    }
     delete[] static_cast<char *>(buffer_pointer->data);
     buffer_pointer->data = nullptr;
     buffer_pointer->size_in_bytes = 0;
 }
source/bringauto/modules/memory_management/CMakeLists.txt (1)

1-10: Consider using a unique project name.

The CMakeLists.txt uses PROJECT(ModuleGateway) which is the same name as the root project. While this may work, it's generally better practice to use a unique project name for submodules to avoid potential conflicts and improve clarity.

Consider changing line 2 to:

-PROJECT(ModuleGateway)
+PROJECT(ModuleGatewayMemoryManagement)

Otherwise, the C++23 standard and dependency configuration look correct and consistent with the rest of the project.

source/bringauto/structures/ModuleLibrary.cpp (1)

16-31: Consider optimizing handler instantiation logic.

The handler is created inside the loop for each module, which means the moduleBinaryPath.empty() check is evaluated on every iteration even though the value doesn't change. Additionally, if using the Async handler, multiple instances are created with the same moduleBinaryPath.

While each module requires its own handler instance (which is correct), the handler type determination can be optimized.

Consider this optimization:

 void ModuleLibrary::loadLibraries(const std::unordered_map<int, std::string> &libPaths, const std::string &moduleBinaryPath) {
-	std::shared_ptr<modules::IModuleManagerLibraryHandler> handler;
+	const bool useAsync = !moduleBinaryPath.empty();
 	for(auto const &[key, path]: libPaths) {
-		if (moduleBinaryPath.empty()) {
-			handler = std::make_shared<modules::ModuleManagerLibraryHandlerLocal>();
-		} else {
-			handler = std::make_shared<modules::ModuleManagerLibraryHandlerAsync>(moduleBinaryPath);
-		}
+		auto handler = useAsync 
+			? std::static_pointer_cast<modules::IModuleManagerLibraryHandler>(
+				std::make_shared<modules::ModuleManagerLibraryHandlerAsync>(moduleBinaryPath))
+			: std::static_pointer_cast<modules::IModuleManagerLibraryHandler>(
+				std::make_shared<modules::ModuleManagerLibraryHandlerLocal>());
 		handler->loadLibrary(path);

Or more cleanly with a factory function approach to eliminate the repeated conditional.

test/source/ErrorAggregatorTests.cpp (1)

20-20: Test updates look correct, but consider adding tests for the Async handler.

The existing tests have been properly updated to use ModuleManagerLibraryHandlerLocal. However, based on the past review comment requesting unit tests, consider adding separate test cases for ModuleManagerLibraryHandlerAsync to ensure the async functionality works correctly and doesn't break existing behavior.

Would you like me to generate test cases for the Async handler variant?

Also applies to: 31-31, 38-38

include/bringauto/settings/Constants.hpp (1)

95-103: Consider grouping Aeron-related constants into a dedicated structure.

As noted in a previous review comment, creating an AeronConstants struct (similar to MqttConstants) would make it clearer that these constants belong to Aeron and improve code organization.

Apply this diff to create an organized structure:

+/**
+ * @brief Constants for Aeron communication
+*/
+struct AeronConstants {
+	/**
+	 * @brief base stream id for Aeron communication from Module Gateway to module binary
+	 */
+	static constexpr unsigned int to_module_stream_id_base { 10000 };
+
+	/**
+	 * @brief base stream id for Aeron communication from module binary to Module Gateway
+	 */
+	static constexpr unsigned int to_gateway_stream_id_base { 20000 };
+
+	/**
+	 * @brief Aeron IPC connection string
+	 */
+	static constexpr std::string_view connection { "aeron:ipc" };
+};
+
-/**
- * @brief base stream id for Aeron communication from Module Gateway to module binary
- */
-constexpr unsigned int aeron_to_module_stream_id_base { 10000 };
-
-/**
- * @brief base stream id for Aeron communication from module binary to Module Gateway
- */
-constexpr unsigned int aeron_to_gateway_stream_id_base { 20000 };

Then move the AERON_CONNECTION constant from the Constants class to this struct.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb7ca23 and e87960a.

📒 Files selected for processing (33)
  • CMLibStorage.cmake (1 hunks)
  • CMakeLists.txt (4 hunks)
  • README.md (2 hunks)
  • cmake/Dependencies.cmake (1 hunks)
  • include/bringauto/external_client/ErrorAggregator.hpp (3 hunks)
  • include/bringauto/modules/Buffer.hpp (1 hunks)
  • include/bringauto/modules/IModuleManagerLibraryHandler.hpp (1 hunks)
  • include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp (1 hunks)
  • include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp (4 hunks)
  • include/bringauto/modules/StatusAggregator.hpp (3 hunks)
  • include/bringauto/settings/Constants.hpp (4 hunks)
  • include/bringauto/settings/Settings.hpp (1 hunks)
  • include/bringauto/structures/ModuleLibrary.hpp (3 hunks)
  • main.cpp (1 hunks)
  • resources/config/README.md (1 hunks)
  • resources/config/default.json (1 hunks)
  • resources/config/example.json (1 hunks)
  • resources/config/for_docker.json (1 hunks)
  • source/bringauto/external_client/ErrorAggregator.cpp (1 hunks)
  • source/bringauto/modules/ModuleHandler.cpp (1 hunks)
  • source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp (1 hunks)
  • source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp (9 hunks)
  • source/bringauto/modules/memory_management/CMakeLists.txt (1 hunks)
  • source/bringauto/modules/memory_management/source/memory_management.cpp (1 hunks)
  • source/bringauto/settings/SettingsParser.cpp (4 hunks)
  • source/bringauto/structures/ModuleLibrary.cpp (2 hunks)
  • test/CMakeLists.txt (1 hunks)
  • test/include/ErrorAggregatorTests.hpp (2 hunks)
  • test/include/ExternalConnectionTests.hpp (0 hunks)
  • test/include/StatusAggregatorTests.hpp (2 hunks)
  • test/include/testing_utils/ConfigMock.hpp (1 hunks)
  • test/source/ErrorAggregatorTests.cpp (3 hunks)
  • test/source/StatusAggregatorTests.cpp (4 hunks)
💤 Files with no reviewable changes (1)
  • test/include/ExternalConnectionTests.hpp
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-04-03T11:53:12.434Z
Learnt from: mikusaq
PR: bringauto/mission-module#23
File: CMLibStorage.cmake:1-3
Timestamp: 2025-04-03T11:53:12.434Z
Learning: In CMLibStorage.cmake, the variable declaration `SET(STORAGE_LIST DEP)` is intentional and should not be changed to `SET(STORAGE_LIST_DEP)` despite appearing to be inconsistent with the later usage.

Applied to files:

  • CMLibStorage.cmake
📚 Learning: 2025-10-02T13:53:28.773Z
Learnt from: MarioIvancik
PR: bringauto/async-function-execution#3
File: examples/main_consumer.cpp:1-6
Timestamp: 2025-10-02T13:53:28.773Z
Learning: In the bringauto/async-function-execution repository, the header file `include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp` includes both `<iostream>` and `<stdexcept>`, so files that include `AsyncFunctionExecutor.hpp` already have access to these standard library headers transitively.

Applied to files:

  • include/bringauto/settings/Constants.hpp
📚 Learning: 2025-06-29T18:12:34.666Z
Learnt from: koudis
PR: bringauto/module-gateway#43
File: CMakeLists.txt:11-14
Timestamp: 2025-06-29T18:12:34.666Z
Learning: In the ModuleGateway project's CMakeLists.txt, compile definitions for version strings are intentionally set with escaped quotes using CMDEF_COMPILE_DEFINITIONS to produce compiler flags like `-DMODULE_GATEWAY_VERSION="1.3.3"`, ensuring the macro expands to a proper string literal in C++ code.

Applied to files:

  • CMakeLists.txt
  • test/CMakeLists.txt
📚 Learning: 2024-10-03T11:24:43.894Z
Learnt from: Mayt34
PR: bringauto/transparent-module#1
File: source/bringauto/modules/devices/testing_device/testing_module_manager.cpp:25-34
Timestamp: 2024-10-03T11:24:43.894Z
Learning: In the `testing_device_aggregate_status` function for the testing device, status aggregation of current and new status is not necessary due to the module's simple logic, similar to the io-module which works without aggregation.

Applied to files:

  • source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp
📚 Learning: 2024-10-31T10:45:48.494Z
Learnt from: MarioIvancik
PR: bringauto/module-gateway#38
File: include/bringauto/structures/LoggingSettings.hpp:10-15
Timestamp: 2024-10-31T10:45:48.494Z
Learning: In the ModuleGateway project, values in the structures defined in `include/bringauto/structures/LoggingSettings.hpp` are validated in `SettingsParser.cpp`, so additional validation within the structures is unnecessary.

Applied to files:

  • source/bringauto/settings/SettingsParser.cpp
🪛 Clang (14.0.6)
include/bringauto/structures/ModuleLibrary.hpp

[error] 3-3: 'bringauto/modules/IModuleManagerLibraryHandler.hpp' file not found

(clang-diagnostic-error)

include/bringauto/external_client/ErrorAggregator.hpp

[error] 3-3: 'bringauto/modules/IModuleManagerLibraryHandler.hpp' file not found

(clang-diagnostic-error)

include/bringauto/settings/Constants.hpp

[error] 3-3: 'bringauto/modules/Buffer.hpp' file not found

(clang-diagnostic-error)

source/bringauto/modules/memory_management/source/memory_management.cpp

[error] 1-1: 'new' file not found

(clang-diagnostic-error)

source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp

[error] 1-1: 'bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp' file not found

(clang-diagnostic-error)

source/bringauto/modules/ModuleManagerLibraryHandlerAsync.cpp

[error] 1-1: 'bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp' file not found

(clang-diagnostic-error)

include/bringauto/modules/IModuleManagerLibraryHandler.hpp

[error] 3-3: 'bringauto/modules/Buffer.hpp' file not found

(clang-diagnostic-error)

include/bringauto/modules/StatusAggregator.hpp

[error] 3-3: 'bringauto/modules/IModuleManagerLibraryHandler.hpp' file not found

(clang-diagnostic-error)

include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp

[error] 3-3: 'bringauto/modules/IModuleManagerLibraryHandler.hpp' file not found

(clang-diagnostic-error)

include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp

[error] 3-3: 'bringauto/modules/IModuleManagerLibraryHandler.hpp' file not found

(clang-diagnostic-error)

🔇 Additional comments (35)
test/CMakeLists.txt (1)

4-4: LGTM! C++ standard upgrade is consistent.

The upgrade to C++23 aligns with the root CMakeLists.txt and the broader project-wide standard update.

CMLibStorage.cmake (1)

1-4: LGTM! CMCONF integration for Fleet Protocol.

The CMCONF initialization for FLEET_PROTOCOL aligns with the new fleet-protocol dependencies and the async client architecture introduced in this PR.

source/bringauto/modules/ModuleHandler.cpp (1)

197-197: Minor readability improvement.

The added empty line improves visual separation between the error handling block and the command retrieval logic.

CMakeLists.txt (6)

9-9: Version bump to 1.4.0 is appropriate.

The version increment from 1.3.5 to 1.4.0 is suitable for the addition of async client functionality and new module management architecture.


19-19: LGTM! C++23 standard upgrade is consistent.

The upgrade to C++23 is consistent with the test CMakeLists.txt and memory_management module.


57-59: New dependencies align with async client feature.

The three new dependencies support the async client architecture:

  • fleet-protocol-cxx-helpers-static 1.1.1 for C++ Fleet Protocol helpers
  • aeron 1.48.6 for high-performance messaging
  • async-function-execution-shared 1.0.0 for async execution patterns

These align with the PR objectives. However, verify the package naming consistency with Dependencies.cmake (see earlier comment on Dependencies.cmake lines 12-14).


64-64: Memory management subdirectory added.

The new memory_management module is now part of the build. Ensure this module is necessary within Module Gateway rather than being provided by external dependencies. See the architectural concern raised in the memory_management.cpp review.


78-80: Link dependencies updated for async client support.

The new link libraries complete the integration of async client functionality:

  • fleet-protocol-cxx-helpers-static for protocol helpers
  • async-function-execution-shared for async patterns
  • memory_management for the local memory management implementation

Verify that the dependency resolution order is correct and there are no circular dependencies.


50-51: I'll verify the cxxopts update as well to complete the dependency compatibility check.

No compatibility issues with updated dependencies; original review comment can be resolved.

Based on verification: The cxxopts 3.0.0 → 3.1.1 update includes only bug fixes and new features, and nlohmann_json 3.x series maintains backward compatibility. Both updates are safe and require no code changes.

resources/config/for_docker.json (1)

21-21: Verify consistency across all configuration files.

The new "module-binary-path" field aligns with the async client feature. Please ensure that all configuration files (default.json, example.json) also include this field with appropriate defaults.

Run the following script to verify the field exists in all config files:

cmake/Dependencies.cmake (2)

12-14: Verify package naming consistency with CMakeLists.txt.

There appears to be a naming discrepancy between Dependencies.cmake and CMakeLists.txt:

  • Dependencies.cmake line 12: fleet-protocol-cpp v1.1.1
  • CMakeLists.txt line 57: fleet-protocol-cxx-helpers-static 1.1.1

And similarly:

  • Dependencies.cmake line 14: async-function-execution v1.0.0
  • CMakeLists.txt line 59: async-function-execution-shared 1.0.0

Please verify these are the correct package names or if there's an inconsistency that needs resolution.


6-6: Consider upgrading cxxopts to latest stable version v3.3.1.

The latest stable release of cxxopts is v3.3.1 (published May–Jun 2025), while this PR uses v3.1.1. No widely-reported critical bugs are specific to v3.1.1, but the version is notably behind current releases. Additionally, v3 introduced breaking changes: the parser no longer modifies argc/argv and ParseResult no longer depends on the parser—verify the codebase is compatible with these changes.

source/bringauto/modules/memory_management/source/memory_management.cpp (1)

1-21: Consider the architecture: why is memory_management in Module Gateway?

A previous reviewer (koudis) questioned why memory_management is defined within Module Gateway. This is a valid architectural concern. Consider whether:

  1. This should be part of the fleet-protocol library instead
  2. It duplicates functionality that might already exist in fleet-protocol-cpp
  3. The Module Gateway should depend on external memory management rather than implementing its own
include/bringauto/modules/ModuleManagerLibraryHandlerLocal.hpp (1)

12-73: LGTM! Clean refactoring to interface-based design.

The migration from a standalone class to inheriting from IModuleManagerLibraryHandler is well-executed. All public methods are correctly marked with override, and the class structure remains intact.

source/bringauto/modules/ModuleManagerLibraryHandlerLocal.cpp (1)

1-222: LGTM! Consistent class renaming throughout the implementation.

All method implementations have been correctly updated to use the new ModuleManagerLibraryHandlerLocal class name. The logic remains unchanged, making this a safe refactoring.

resources/config/default.json (1)

17-17: LGTM! Configuration schema extended appropriately.

The new module-binary-path field is correctly added with an empty default value, maintaining consistency with the configuration structure.

resources/config/example.json (1)

23-23: LGTM! Example configuration updated consistently.

The module-binary-path field is added in the same location as in default.json, maintaining consistency across configuration files.

test/include/testing_utils/ConfigMock.hpp (1)

108-108: LGTM! Config mock updated to include new field.

The generated configuration string now includes module-binary-path with an empty value, consistent with the example configurations. If future tests need to exercise scenarios with a non-empty module-binary-path, consider adding it as a configurable member of the Config struct.

main.cpp (2)

70-71: LGTM!

The early instantiation of moduleLibrary is appropriate for the subsequent library loading operations.


74-74: Verify that moduleBinaryPath is properly configured in Settings.

The new parameter enables async client support. Ensure that context->settings->moduleBinaryPath is properly initialized and validated during settings parsing.

source/bringauto/structures/ModuleLibrary.cpp (1)

2-3: LGTM!

The new includes are necessary for the Local and Async handler implementations.

test/source/ErrorAggregatorTests.cpp (1)

3-3: LGTM!

The include is correctly updated to use the Local handler variant for testing.

test/include/ErrorAggregatorTests.hpp (1)

6-6: LGTM!

The test fixture correctly depends on the IModuleManagerLibraryHandler interface, allowing flexibility in the concrete implementation used. This follows the dependency inversion principle.

Also applies to: 34-34

source/bringauto/external_client/ErrorAggregator.cpp (1)

11-11: LGTM!

The parameter type correctly updated to accept the IModuleManagerLibraryHandler interface, enabling support for both Local and Async handler implementations.

include/bringauto/modules/Buffer.hpp (1)

21-22: LGTM!

The friend declarations are correctly updated to grant both ModuleManagerLibraryHandlerLocal and ModuleManagerLibraryHandlerAsync access to the private Buffer constructor, as friendship is not inherited through interfaces.

test/include/StatusAggregatorTests.hpp (1)

7-7: LGTM!

The test fixture correctly depends on the IModuleManagerLibraryHandler interface, consistent with the updated architecture.

Also applies to: 46-46

README.md (2)

60-60: LGTM!

Capitalization improvement for consistency with other headings.


32-42: Verify that new dependencies are required and properly integrated.

The documentation adds several new dependencies (pahomqtt, pahomqttcpp, zlib, async-function-execution) and bumps versions for existing ones. Ensure that:

  1. New dependencies are actually used in the codebase
  2. Version requirements are accurate
  3. CMakeLists.txt is updated to include these dependencies
include/bringauto/external_client/ErrorAggregator.hpp (1)

3-3: LGTM! Interface migration is correct.

The migration from concrete ModuleManagerLibraryHandler to the IModuleManagerLibraryHandler interface is consistent and allows for both Local and Async handler implementations.

Also applies to: 28-28, 114-114

include/bringauto/structures/ModuleLibrary.hpp (2)

16-16: LGTM! Default parameter addresses previous concern.

The addition of a default empty string for moduleBinaryPath parameter properly addresses the previous review comment about not forcing the path when using standard (non-async) loading. This allows flexibility for both loading modes.

Also applies to: 24-26


35-35: LGTM! Interface migration is correct.

The change to IModuleManagerLibraryHandler interface type is consistent with the architectural refactor and enables polymorphic handler selection.

test/source/StatusAggregatorTests.cpp (1)

3-3: LGTM! Test updates are consistent.

The migration to ModuleManagerLibraryHandlerLocal in tests is appropriate, as tests should use concrete implementations. All usages are consistently updated.

Also applies to: 46-46, 64-64, 99-99

include/bringauto/modules/StatusAggregator.hpp (1)

3-3: LGTM! Interface migration is correct.

The migration from concrete ModuleManagerLibraryHandler to the IModuleManagerLibraryHandler interface is consistent throughout the class and enables polymorphic handler usage.

Also applies to: 22-25, 189-189

source/bringauto/settings/SettingsParser.cpp (2)

98-101: LGTM! Proper validation for optional moduleBinaryPath.

The validation correctly checks for the existence of moduleBinaryPath only when it's non-empty, which aligns with the optional nature of the async module binary feature.


155-157: LGTM! Safer JSON parsing methods.

The use of at() and get_to() provides better error handling by throwing exceptions for missing keys, which is more robust than the bracket operator that would insert default values.

Also applies to: 161-164, 184-184

Comment on lines +21 to +27
std::span<const uint8_t> serialize() const {
size_t total_size = sizeof(int) + buffer.size_in_bytes;
uint8_t* data = new uint8_t[total_size];
std::memcpy(data, &returnCode, sizeof(int));
std::memcpy(data + sizeof(int), buffer.data, buffer.size_in_bytes);
return {data, total_size};
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical memory leak in serialize().

The serialize() method allocates memory with new uint8_t[] but returns a std::span that doesn't convey ownership. The caller has no way to delete this memory, resulting in a leak on every call.

Consider using std::vector<uint8_t> for automatic memory management:

-    std::span<const uint8_t> serialize() const {
+    std::vector<uint8_t> serialize() const {
         size_t total_size = sizeof(int) + buffer.size_in_bytes;
-        uint8_t* data = new uint8_t[total_size];
+        std::vector<uint8_t> data(total_size);
-        std::memcpy(data, &returnCode, sizeof(int));
-        std::memcpy(data + sizeof(int), buffer.data, buffer.size_in_bytes);
-        return {data, total_size};
+        std::memcpy(data.data(), &returnCode, sizeof(int));
+        std::memcpy(data.data() + sizeof(int), buffer.data, buffer.size_in_bytes);
+        return data;
     }

Similarly for ConvertibleBuffer::serialize() if it allocates dynamically.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
std::span<const uint8_t> serialize() const {
size_t total_size = sizeof(int) + buffer.size_in_bytes;
uint8_t* data = new uint8_t[total_size];
std::memcpy(data, &returnCode, sizeof(int));
std::memcpy(data + sizeof(int), buffer.data, buffer.size_in_bytes);
return {data, total_size};
}
std::vector<uint8_t> serialize() const {
size_t total_size = sizeof(int) + buffer.size_in_bytes;
std::vector<uint8_t> data(total_size);
std::memcpy(data.data(), &returnCode, sizeof(int));
std::memcpy(data.data() + sizeof(int), buffer.data, buffer.size_in_bytes);
return data;
}
🤖 Prompt for AI Agents
In include/bringauto/modules/ModuleManagerLibraryHandlerAsync.hpp around lines
21-27, serialize() currently allocates a raw uint8_t[] and returns a std::span,
leaking memory because ownership is not transferred; replace the raw allocation
with an owned container (prefer std::vector<uint8_t>) and change the method to
return std::vector<uint8_t> (or another owning type), copy the header and buffer
into that vector, and return it; apply the same fix to any
ConvertibleBuffer::serialize() that allocates dynamically so ownership is clear
and memory is automatically freed.

if (!moduleBinaryProcess_.valid()) {
throw std::runtime_error { "Failed to start module binary " + moduleBinaryPath_ };
}
std::this_thread::sleep_for(std::chrono::seconds(1)); // TODO Not sure how much time is needed.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace arbitrary sleep with proper startup verification.

The TODO comment acknowledges that the sleep duration is uncertain. Consider implementing a proper health check or ready signal from the module binary instead of an arbitrary sleep.

Possible approaches:

  1. Poll the Aeron client for connectivity with a timeout
  2. Have the module binary send a ready signal
  3. Use a configurable startup timeout with retry logic

@sonarqubecloud
Copy link

sonarqubecloud bot commented Nov 3, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
12.1% Duplication on New Code (required ≤ 3%)
D Reliability Rating on New Code (required ≥ A)
17 New Major Issues (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants