Skip to content

Conversation

@MarioIvancik
Copy link
Member

@MarioIvancik MarioIvancik commented Oct 1, 2025

Summary by CodeRabbit

  • New Features

    • Per-function timeouts, timeout-aware idle/messaging, and three runnable examples (driver, producer, consumer).
  • Breaking Changes

    • connect() now returns a status and must be checked; call/result API uses expected-style results with explicit error codes; client messaging/wait APIs accept timeouts; configuration moved from JSON string to typed per-function configs; some public signatures/qualifiers updated.
  • Documentation

    • Build, setup, and example run instructions updated.
  • Chores

    • Removed JSON dependency; bumped CMake/C++ standards and project version.
  • Tests

    • Tests updated/extended for timeouts, large-argument, and in-progress call scenarios.

@MarioIvancik MarioIvancik self-assigned this Oct 1, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 1, 2025

Walkthrough

Bumps C++ standard and project version; removes nlohmann_json; replaces JSON-string function configs with typed structures::FunctionConfigs; changes AsyncFunctionExecutor APIs (connect() → int, callFunc → std::expected, per-function timeouts and call-in-progress tracking); makes client wait APIs timeout-aware; refactors idle strategy; splits examples into producer/consumer/driver; updates CMake and tests.

Changes

Cohort / File(s) Summary
Top-level CMake & deps
CMakeLists.txt, cmake/Dependencies.cmake, test/CMakeLists.txt
C++ standard → 23; project version 0.1.0 → 1.0.0; removed nlohmann_json package/alias/linkage; replaced INCLUDE(...) with ADD_SUBDIRECTORY for tests/examples; test CMake now validates presence of async-function-execution-shared.
Project README
README.md
Documented typed functionConfigurations; require calling executorProducer.connect(); swapped Aeron setup notes for cmLib/CMLIB_DIR guidance; simplified build instructions.
Examples build & docs
examples/CMakeLists.txt, examples/README.md
Replaced single example with example_producer, example_consumer, example_driver; set C++23; added example build/run steps.
Examples sources
examples/main.cpp (removed), examples/main_producer.cpp, examples/main_consumer.cpp, examples/main_driver.cpp
Removed old monolithic example; added producer, consumer, and driver programs; introduced SerializableString helpers and per-role connect/poll/send flows.
Core API & models
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp, include/.../structures/Settings.hpp, include/.../Constants.hpp
Replaced JSON configs with structures::FunctionConfigs; added constants (POLL_FRAGMENTS_LIMIT, STREAM_CONNECTION_TIMEOUT); updated Return/Arguments/FunctionDefinition/FunctionList (concepts, deduction guide); AsyncFunctionExecutor: ctor takes const Config&, connect() → int, callFunc → std::expected<Ret,CallError>, per-function timeouts and callInProgress_ tracking; serialization/deserialization updated to std::expected/error enums.
Idle strategy
include/.../TimeoutIdleStrategy.hpp, source/.../TimeoutIdleStrategy.cpp
Moved IdleState private; replaced parameterless idle with idle(int workCount, std::chrono::nanoseconds timeout) and reset(); removed padding; implemented timeout-aware spin/yield/park state machine returning -1 on timeout.
Client layer
include/.../clients/ClientInterface.hpp, include/.../clients/AeronClient.hpp
waitForMessage now accepts std::chrono::nanoseconds timeout; AeronClient connect/wait wrapped with timeout-aware logic and try/catch; sendMessage/waitForMessage validate channels and return -1/empty span on errors/timeouts.
Structures / Settings
include/.../structures/Settings.hpp
Added FunctionConfigs wrapper (map FunctionId→FunctionConfig) with accessors; Settings stores FunctionConfigs; removed in-file nlohmann/json parsing and ctor JSON param.
Tests & test utilities
test/CMakeLists.txt, test/include/*, test/source/*, test/integration_tests/*
Tests migrated from JSON string configs to structured FunctionConfigs; FunctionList initializer simplified; MockClient made final and updated waitForMessage signature and helpers; many tests updated to check connect() return values and use .value()/.error() on std::expected; new tests added for argument-size and call-in-progress.
Aeron driver
include/.../AeronDriver.hpp, source/.../AeronDriver.cpp
AeronDriver::run() marked const; isRunning() annotated [[nodiscard]]; signal include and nullptr usage adjusted.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App
  participant Exec as AsyncFunctionExecutor
  participant Client as ClientInterface/AeronClient

  rect rgb(250,250,255)
    note right of App: Init & connect
    App->>Exec: AsyncFunctionExecutor(config, FunctionList, client?)
    App->>Exec: connect()
    Exec->>Client: connect(...)
    Client-->>Exec: int status
    Exec-->>App: return status (0 == ok)
  end
Loading
sequenceDiagram
  autonumber
  actor App
  participant Exec as AsyncFunctionExecutor (Producer)
  participant Client as ClientInterface

  rect rgb(245,255,245)
    note right of App: Producer call flow (per-function timeout)
    App->>Exec: callFunc(FunctionDef, args...)
    Exec->>Client: sendMessage(callChannel, encodedArgs)
    Exec->>Client: waitForMessage(returnChannel, timeout = fn.timeout || default)
    alt message received
      Client-->>Exec: span(bytes)
      Exec-->>App: std::expected with value
    else timeout / empty
      Client-->>Exec: empty span
      Exec-->>App: std::expected with CallError (Timeout/…)
    end
  end
Loading
sequenceDiagram
  autonumber
  actor App
  participant Exec as AsyncFunctionExecutor (Consumer)
  participant Client as ClientInterface
  participant Handler as UserCode

  App->>Exec: connect()
  loop poll loop
    Exec->>Client: waitForMessage(callChannel, timeout)
    alt got call
      Client-->>Exec: span(call bytes)
      Exec->>Handler: getFunctionArgs -> decoded args
      Handler-->>Exec: sendReturnMessage(fnId, retVal)
      Exec->>Client: sendMessage(returnChannel, encodedRet)
    else no message / timeout
      Note over Exec: idle(workCount, timeout) and continue
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Points to focus during review:

  • AsyncFunctionExecutor: std::expected error branches, callInProgress_ concurrency and lifetime of client_, per-function timeout enforcement and channelOffset handling.
  • TimeoutIdleStrategy: correctness of timeout return (-1) semantics vs. header contract and state transitions.
  • ClientInterface / AeronClient: timeout semantics (empty span vs. error), exception handling in connect, and channel existence checks.
  • Tests & MockClient: updated waitForMessage signature and serialization/deserialization const-correctness.

Possibly related PRs

  • First release #2 — Overlapping edits to CMake, Settings/FunctionConfigs vs JSON, Constants and TimeoutIdleStrategy/API changes; directly related and likely to conflict at the code level.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The pull request title 'First proposed release' is vague and does not clearly communicate the substantive changes being made. While the branch name suggests this is related to a 1.0.0 release, the title itself uses generic phrasing that doesn't describe what the release entails. A reviewer scanning the git history would not understand from this title alone what major updates, breaking changes, or feature additions are included. The title would benefit from being more specific about the scope of changes, such as 'Release v1.0.0: Refactor function execution model and update dependencies' or similar. Consider revising the pull request title to be more descriptive and specific about the major changes being introduced in this release. The title should convey key information such as the version bump and the nature of significant changes (e.g., API refactoring, dependency updates, feature additions) so that developers can quickly understand the release's purpose and scope.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BAF-1155/1.0.0_release

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.

@MarioIvancik MarioIvancik changed the title added missing documentation; fixed examples; additional CMakeLists checks First proposed release Oct 1, 2025
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
include/bringauto/async_function_execution/clients/AeronClient.hpp (2)

109-114: Unchecked map access may dereference null publication.

operator[] inserts a default‑constructed shared_ptr and ->offer will crash if the key is unknown. Check presence.

- return aeronPublications_[channelId]->offer(srcBuffer, 0, messageBytes.size());
+ const auto it = aeronPublications_.find(channelId);
+ if (it == aeronPublications_.end() || !it->second) {
+   std::cerr << "Unknown or null publication for channel " << channelId << std::endl;
+   return -1;
+ }
+ return it->second->offer(srcBuffer, 0, messageBytes.size());

124-136: Guard missing subscription/message entries in waitForMessage.

operator[] will create null entries; dereference causes UB. Validate keys and message presence.

- aeronPolling_[channelId] = true;
+ if (aeronSubscriptions_.find(channelId) == aeronSubscriptions_.end()) {
+   std::cerr << "Unknown subscription for channel " << channelId << std::endl;
+   return {};
+ }
+ aeronPolling_[channelId] = true;
@@
- const auto& msg = aeronMessages_[channelId];
- return std::span<const uint8_t>(msg.data, msg.length);
+ const auto itMsg = aeronMessages_.find(channelId);
+ if (itMsg == aeronMessages_.end() || itMsg->second.data == nullptr) {
+   return {};
+ }
+ return std::span<const uint8_t>(itMsg->second.data, itMsg->second.length);
🧹 Nitpick comments (18)
include/bringauto/async_function_execution/Constants.hpp (1)

13-14: Consider clarifying the origin of this size limit.

The value 65535 (2^16 - 1) is a standard limit. If this relates to Aeron MTU, transport layer constraints, or serialization format requirements, consider adding that context to the documentation for future maintainers.

examples/CMakeLists.txt (1)

7-13: Consider using PRIVATE linkage for executable targets.

The three example executables link to async-function-execution-shared with PUBLIC visibility. Since these are final executables (not libraries), PRIVATE linkage is typically more appropriate and prevents unnecessary transitive dependency exposure.

Apply this diff to use PRIVATE linkage:

-TARGET_LINK_LIBRARIES(example_producer PUBLIC async-function-execution-shared)
-TARGET_LINK_LIBRARIES(example_consumer PUBLIC async-function-execution-shared)
-TARGET_LINK_LIBRARIES(example_driver PUBLIC async-function-execution-shared)
+TARGET_LINK_LIBRARIES(example_producer PRIVATE async-function-execution-shared)
+TARGET_LINK_LIBRARIES(example_consumer PRIVATE async-function-execution-shared)
+TARGET_LINK_LIBRARIES(example_driver PRIVATE async-function-execution-shared)
examples/main_driver.cpp (1)

7-11: Consider adding error handling and documenting shutdown.

The example demonstrates minimal usage but lacks error handling for driver.run() and provides no shutdown mechanism. For a production-like example, consider:

  • Checking the return value of driver.run() if it returns an error code
  • Adding signal handling (SIGINT/SIGTERM) to gracefully stop the driver
  • Including comments explaining expected behavior and shutdown

Example enhancement:

+#include <csignal>
+
+static AeronDriver* globalDriver = nullptr;
+
+void signalHandler(int signal) {
+	if (globalDriver) {
+		globalDriver->stop();
+	}
+}
+
 int main() {
 	AeronDriver driver;
+	globalDriver = &driver;
+	std::signal(SIGINT, signalHandler);
+	std::signal(SIGTERM, signalHandler);
+	
+	std::cout << "Starting Aeron driver (press Ctrl+C to stop)..." << std::endl;
 	driver.run();
+	std::cout << "Driver stopped." << std::endl;
 	return 0;
 }
examples/main_producer.cpp (2)

1-6: Include what you use; avoid transitive headers.

Add headers for iostream/chrono/span/string/cstdint used below.

 #include <bringauto/async_function_execution/AsyncFunctionExecutor.hpp>
+#include <iostream>
+#include <string>
+#include <span>
+#include <chrono>
+#include <cstdint>

40-45: Optional: demonstrate per‑function timeouts in the example config.

Since the docs mention functionConfigurations, consider adding 1 example override to mirror tests.

test/include/AsyncFunctionExecutorTests.hpp (3)

6-6: Add the chrono header.

You use std::chrono types below.

 #include <gtest/gtest.h>
+#include <chrono>

58-65: Per‑function timeouts (1–5 ms) may be flaky on CI.

Nanosecond literals equate to 1–5 ms here, which is tight under load. Consider ≥50 ms unless you’re explicitly testing timeout behavior.


95-97: Assert connect() succeeds.

Turn silent failures into early, actionable test errors.

-		executorProducer.connect();
-		executorConsumer.connect();
+		ASSERT_EQ(0, executorProducer.connect());
+		ASSERT_EQ(0, executorConsumer.connect());
examples/README.md (1)

22-22: Add trailing newline at EOF.

Minor formatting hygiene for POSIX tools.

examples/main_consumer.cpp (3)

2-2: Remove unused include.

AeronDriver isn’t used here.

-#include <bringauto/async_function_execution/AeronDriver.hpp>

1-6: Include what you use.

Add iostream/string/span/chrono/cstdint to avoid relying on transitive includes.

 #include <bringauto/async_function_execution/AsyncFunctionExecutor.hpp>
+#include <iostream>
+#include <string>
+#include <span>
+#include <chrono>
+#include <cstdint>

48-49: Check connect() result and handle failure.

Prevents spinning in the poll loop after a failed init.

-	executor.connect();
+	const int rc = executor.connect();
+	if (rc != 0) {
+		std::cerr << "Consumer: connect() failed, rc=" << rc << std::endl;
+		return rc;
+	}
source/bringauto/async_function_execution/TimeoutIdleStrategy.cpp (2)

28-30: Reset on timeout to avoid sticky state across calls.

Without reset(), subsequent idle() calls may immediately keep returning -1 due to preserved startTime_/state.

-		if (std::chrono::duration_cast<std::chrono::nanoseconds>(now - startTime_) >= timeoutNs) {
-			return -1;
-		}
+		if (std::chrono::duration_cast<std::chrono::nanoseconds>(now - startTime_) >= timeoutNs) {
+			reset(); // ensure next wait starts fresh
+			return -1;
+		}

1-4: Add direct includes for used facilities.

std::min, std::this_thread, and chrono are used here; include explicitly.

 #include <bringauto/async_function_execution/TimeoutIdleStrategy.hpp>
+#include <algorithm>  // std::min
+#include <thread>     // std::this_thread::yield/sleep_for
+#include <chrono>     // std::chrono::*
README.md (3)

39-40: Show connect() return handling.

connect() returns int; demonstrate checking it to guide users.

-executorProducer.connect();
+if (executorProducer.connect() != 0) {
+  throw std::runtime_error("Failed to connect executorProducer");
+}

42-54: Clarify duration units and example.

Text says “in nanoseconds” but example uses seconds. State “any std::chrono duration convertible to ns” and show a ns example.

-The functionConfigurations parameter accepts an unordered map representing per function configurations. Syntax:
+functionConfigurations accepts an unordered_map of per‑function configs. Durations are any std::chrono duration convertible to nanoseconds. Syntax:
@@
-  - timeout: replaces the default timeout value for that function (in nanoseconds)
+  - timeout: replaces the default timeout for that function (stored as nanoseconds)

Example:

-  { 1, { std::chrono::seconds(2) } }
+  { 1, { std::chrono::nanoseconds{2'000'000'000} } }

24-30: Fix markdown lint: replace hard tabs and list indentation.

Replace tabs with spaces and align the “Supported parameters” list to column 0 (MD010, MD007).

Also applies to: 48-49, 53-53

include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (1)

343-343: Clarify size calculation comment.

The comment states "Each argument: size bytes + data", but the actual size calculation adds 2 + sizeof(args) per argument (2 bytes for the size field, plus the argument size). Consider updating the comment to explicitly mention the 2-byte size prefix for clarity.

Apply this diff to clarify the comment:

-		((totalSize += 2 + sizeof(args)), ...); // Each argument: size bytes + data
+		((totalSize += 2 + sizeof(args)), ...); // Each argument: 2-byte size prefix + data
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a17856 and ea6466b.

📒 Files selected for processing (24)
  • CMakeLists.txt (3 hunks)
  • README.md (2 hunks)
  • cmake/Dependencies.cmake (0 hunks)
  • examples/CMakeLists.txt (1 hunks)
  • examples/README.md (1 hunks)
  • examples/main.cpp (0 hunks)
  • examples/main_consumer.cpp (1 hunks)
  • examples/main_driver.cpp (1 hunks)
  • examples/main_producer.cpp (1 hunks)
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (12 hunks)
  • include/bringauto/async_function_execution/Constants.hpp (1 hunks)
  • include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1 hunks)
  • include/bringauto/async_function_execution/clients/AeronClient.hpp (5 hunks)
  • include/bringauto/async_function_execution/clients/ClientInterface.hpp (2 hunks)
  • include/bringauto/async_function_execution/structures/Settings.hpp (2 hunks)
  • source/bringauto/async_function_execution/TimeoutIdleStrategy.cpp (1 hunks)
  • test/CMakeLists.txt (1 hunks)
  • test/README.md (1 hunks)
  • test/include/AsyncFunctionExecutorTests.hpp (1 hunks)
  • test/include/MockClient.hpp (1 hunks)
  • test/integration_tests/main_segfault_consumer.cpp (2 hunks)
  • test/integration_tests/main_segfault_producer.cpp (2 hunks)
  • test/integration_tests/main_simple_function.cpp (2 hunks)
  • test/source/AsyncFunctionExecutorTests.cpp (1 hunks)
💤 Files with no reviewable changes (2)
  • examples/main.cpp
  • cmake/Dependencies.cmake
🧰 Additional context used
🧬 Code graph analysis (7)
examples/main_consumer.cpp (2)
examples/main_producer.cpp (3)
  • reinterpret_cast (11-13)
  • bytes (14-16)
  • bytes (14-14)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (4)
  • bytes (400-407)
  • bytes (400-400)
  • funcId (331-337)
  • funcId (331-331)
test/include/MockClient.hpp (3)
include/bringauto/async_function_execution/clients/AeronClient.hpp (5)
  • channelId (111-114)
  • channelId (111-111)
  • channelId (124-136)
  • channelId (124-124)
  • channelId (144-163)
include/bringauto/async_function_execution/clients/ClientInterface.hpp (2)
  • channelId (33-33)
  • channelId (42-42)
include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1)
  • timeout (69-69)
include/bringauto/async_function_execution/clients/ClientInterface.hpp (3)
include/bringauto/async_function_execution/clients/AeronClient.hpp (5)
  • channelId (111-114)
  • channelId (111-111)
  • channelId (124-136)
  • channelId (124-124)
  • channelId (144-163)
test/include/MockClient.hpp (4)
  • channelId (22-78)
  • channelId (22-22)
  • channelId (80-88)
  • channelId (80-80)
include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1)
  • timeout (69-69)
source/bringauto/async_function_execution/TimeoutIdleStrategy.cpp (1)
include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (2)
  • workCount (47-47)
  • timeout (69-69)
examples/main_producer.cpp (3)
examples/main_consumer.cpp (3)
  • reinterpret_cast (13-15)
  • bytes (16-18)
  • bytes (16-16)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (2)
  • bytes (400-407)
  • bytes (400-400)
test/include/MockClient.hpp (4)
  • bytes (113-127)
  • bytes (113-113)
  • bytes (130-143)
  • bytes (130-130)
include/bringauto/async_function_execution/clients/AeronClient.hpp (3)
include/bringauto/async_function_execution/clients/ClientInterface.hpp (3)
  • subscriptionIds (24-24)
  • channelId (33-33)
  • channelId (42-42)
test/include/MockClient.hpp (6)
  • subscriptionIds (17-20)
  • subscriptionIds (17-17)
  • channelId (22-78)
  • channelId (22-22)
  • channelId (80-88)
  • channelId (80-80)
include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1)
  • timeout (69-69)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (1)
test/include/MockClient.hpp (4)
  • funcId (146-153)
  • funcId (146-146)
  • funcId (156-167)
  • funcId (156-156)
🪛 markdownlint-cli2 (0.18.1)
README.md

24-24: Hard tabs
Column: 1

(MD010, no-hard-tabs)


25-25: Hard tabs
Column: 1

(MD010, no-hard-tabs)


26-26: Hard tabs
Column: 1

(MD010, no-hard-tabs)


27-27: Hard tabs
Column: 1

(MD010, no-hard-tabs)


28-28: Hard tabs
Column: 1

(MD010, no-hard-tabs)


29-29: Hard tabs
Column: 1

(MD010, no-hard-tabs)


30-30: Hard tabs
Column: 1

(MD010, no-hard-tabs)


48-48: Hard tabs
Column: 1

(MD010, no-hard-tabs)


53-53: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🔇 Additional comments (31)
include/bringauto/async_function_execution/Constants.hpp (4)

1-8: LGTM!

Header structure is clean with appropriate include guard and minimal dependencies. The namespace declaration is correct.


9-10: Clarify the rationale for the polling limit value.

The value 10 appears arbitrary. Consider documenting why this specific limit was chosen (e.g., based on Aeron best practices, performance testing, or latency requirements). If this value may need tuning in different deployment scenarios, consider making it configurable rather than a hard-coded constant.


11-12: Document assumptions about function ID ranges to prevent collisions.

The offset value assumes function IDs remain below a certain range to avoid collision between request and return channels. Consider documenting the expected range of function IDs (e.g., "Assumes function IDs are in range [0, 999]") or implementing validation to ensure functionId + MESSAGE_RETURN_CHANNEL_OFFSET doesn't collide with existing channel IDs.


16-17: LGTM!

The Aeron IPC connection string is correct, and using std::string_view for a compile-time string constant is appropriate. The documentation clearly indicates this is for shared memory communication.

test/CMakeLists.txt (1)

6-8: LGTM! Good guard to enforce correct build workflow.

The target existence check ensures tests are built as part of the main project, preventing misconfiguration. The error message is clear and actionable.

test/README.md (1)

9-14: LGTM! Documentation correctly reflects the integrated build approach.

The simplified build instructions align with the CMakeLists.txt target check and make the process clearer for developers.

include/bringauto/async_function_execution/clients/ClientInterface.hpp (2)

6-6: LGTM! Include added to support timeout parameter.


39-42: All waitForMessage call sites include the timeout parameter.
No missing invocations detected across source, header, or test files.

examples/CMakeLists.txt (1)

3-5: LGTM! Consistent guard with test/CMakeLists.txt.

The target existence check is consistent with the test configuration and ensures examples are built correctly.

test/integration_tests/main_segfault_consumer.cpp (2)

32-34: LGTM! Simplified FunctionList initialization.

The removal of the std::tuple wrapper makes the initialization cleaner and aligns with the refactored FunctionList type system.


60-60: LGTM! Added missing end-of-file newline.

test/include/MockClient.hpp (1)

80-88: LGTM! Timeout parameter correctly added to match updated interface.

The method signature now correctly overrides ClientInterface::waitForMessage with the timeout parameter. The test assertion at line 82 validates that per-function timeout values are correctly calculated based on channel ID, which is appropriate for a mock in test scenarios.

test/integration_tests/main_segfault_producer.cpp (1)

33-35: LGTM! Updated FunctionList initialization aligns with API refactor.

The direct braced initialization is clearer and more idiomatic than the previous tuple-based approach.

CMakeLists.txt (3)

7-7: LGTM! Version bumped to 1.0.0 for release.

The major version bump appropriately signals API maturity and the timeout configuration changes introduced in this PR.


60-60: LGTM! Using ADD_SUBDIRECTORY for tests is standard practice.

This provides better encapsulation and allows the test directory to manage its own CMake scope.


82-82: LGTM! Examples path normalized.

Removing the trailing slash is a minor cleanup with no functional impact.

test/integration_tests/main_simple_function.cpp (2)

20-22: LGTM! Updated FunctionList initialization for producer.

The direct braced initialization aligns with the API refactor and improves code clarity.


29-31: LGTM! Updated FunctionList initialization for consumer.

Consistent with the producer initialization, maintaining code uniformity.

test/source/AsyncFunctionExecutorTests.cpp (1)

113-128: Connect verification already implemented: AsyncFunctionExecutorTests::SetUpTestSuite calls executorProducer.connect() and executorConsumer.connect() (lines 95–96), so the suggestion can be removed.

include/bringauto/async_function_execution/clients/AeronClient.hpp (2)

124-131: Use TimeoutIdleStrategy as the default IdleStrategy
AeronClient.hpp currently defaults to aeron::BackoffIdleStrategy, which does not support the call idle(fragmentsRead, timeout). Change the template default to TimeoutIdleStrategy or introduce an adapter that provides both idle(int, timeout) and idle(int) overloads to avoid compilation errors.


152-155: idle(fragmentsRead) already respects the configured timeout – it calls idle(fragmentsRead, std::chrono::nanoseconds{0}), which falls back to the default timeout in TimeoutIdleStrategy; no change needed.

Likely an incorrect or invalid review comment.

include/bringauto/async_function_execution/structures/Settings.hpp (2)

18-26: LGTM! Clean refactor to structured configuration.

The introduction of FunctionConfigs as a typed wrapper around the configuration map is a solid improvement over JSON string parsing. The move semantics in the constructor are correctly applied.


38-42: LGTM! Constructor signature is clear and idiomatic.

The updated Settings constructor correctly accepts FunctionConfigs by const reference with a sensible default, maintaining good API ergonomics.

include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (3)

16-37: LGTM! Constructor documentation is clear.

The updated documentation accurately describes the constructor parameters and their defaults.


39-52: LGTM! Public API is well-designed.

The new idle(int workCount, std::chrono::nanoseconds timeout) signature provides a clean interface for timeout-aware idling, and the reset() method allows state reinitialization.


54-69: Good encapsulation by moving IdleState to private scope.

Moving the IdleState enum to the private section is a positive change that reduces the public API surface and prevents external dependencies on internal state representation.

include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (5)

22-22: LGTM! Consistent with Settings.hpp refactor.

The change to structures::FunctionConfigs aligns with the structured configuration approach introduced in Settings.hpp.


89-126: LGTM! Well-designed type traits and deduction guide.

The is_function_definition trait, IsFunctionDefinition concept, and the deduction guide for FunctionList provide strong compile-time guarantees and improve API usability.


143-161: LGTM! Constructor validation improves robustness.

The constructor correctly validates that all function IDs in the configuration are defined in the FunctionList, catching configuration errors early. The optional client parameter adds useful flexibility for testing and custom implementations.


172-185: LGTM! Return type change improves error handling.

Changing connect() to return int allows callers to detect connection failures, which is a significant improvement over the previous void return.


212-216: Zero timeout yields infinite wait (no fallback to defaultTimeout). Verified that when FunctionConfig.timeout is zero, waitForMessage passes nanoseconds(0) to TimeoutIdleStrategy, which skips the timeout check and idles indefinitely (per its docs and implementation).

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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
test/integration_tests/main_simple_function.cpp (1)

1-6: Include what you use: add and .

This TU uses std::thread and std::this_thread::sleep_for but doesn’t include their headers. Add explicit includes to avoid transitive‑include breakage.

 #include <bringauto/async_function_execution/AsyncFunctionExecutor.hpp>
 #include <bringauto/async_function_execution/AeronDriver.hpp>
 
 #include <iostream>
+#include <thread>
+#include <chrono>
test/integration_tests/main_segfault_producer.cpp (1)

1-6: Include what you use: add and .

This TU launches a std::thread and sleeps.

 #include <bringauto/async_function_execution/AsyncFunctionExecutor.hpp>
 #include <bringauto/async_function_execution/AeronDriver.hpp>
 
 #include <iostream>
+#include <thread>
+#include <chrono>
 
 namespace baafe = bringauto::async_function_execution;

Also applies to: 9-14

include/bringauto/async_function_execution/clients/AeronClient.hpp (1)

19-20: Default IdleStrategy mismatches idle(fragmentsRead, timeout); set to TimeoutIdleStrategy.

BackoffIdleStrategy typically lacks idle(int, std::chrono::nanoseconds) and returns void. Using it as default can cause compile errors if instantiated. Make TimeoutIdleStrategy the default.

-template<typename IdleStrategy = aeron::BackoffIdleStrategy>
+template<typename IdleStrategy = bringauto::async_function_execution::TimeoutIdleStrategy>
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (1)

143-161: Verify function config validation logic.

Lines 156-160 validate that all function IDs in the configuration are defined in the FunctionList. However, the error message (line 158) says "Warning:" but then throws an exception, which is contradictory.

Apply this diff to make the error message consistent:

-				throw std::runtime_error("Warning: Function ID " + std::to_string(funcId) + " in configuration is not defined in FunctionList.");
+				throw std::runtime_error("Function ID " + std::to_string(funcId) + " in configuration is not defined in FunctionList.");
♻️ Duplicate comments (2)
examples/main_consumer.cpp (1)

58-73: Return type fixed to SerializableString.

Calls to sendReturnMessage now pass SerializableString as required. Resolves prior review.

include/bringauto/async_function_execution/clients/AeronClient.hpp (1)

69-99: Bounded waits in connect() are a solid improvement.

Timeouts with early returns and logging address the previous busy‑wait risk.

🧹 Nitpick comments (10)
README.md (1)

15-31: Replace hard tabs with spaces to satisfy markdownlint.

The new blocks introduced literal tab characters and a mis-indented bullet, triggering MD010/MD007 from markdownlint-cli2. Swap tabs for spaces (e.g., four spaces) and align the bullet list so the pipeline stays green.

Also applies to: 49-55

include/bringauto/async_function_execution/structures/Settings.hpp (1)

23-26: Include <utility> for std::move.

This header now calls std::move, but <utility> isn’t included. Pull it in here to avoid depending on indirect includes.

test/include/AsyncFunctionExecutorTests.hpp (1)

94-97: Assert connect() success in SetUpTestSuite.

connect() now returns int. Assert/FAIL on non‑zero to avoid hidden failures.

 static void SetUpTestSuite() {
-    executorProducer.connect();
-    executorConsumer.connect();
+    ASSERT_EQ(0, executorProducer.connect()) << "Producer connect failed";
+    ASSERT_EQ(0, executorConsumer.connect()) << "Consumer connect failed";
 }
test/source/AsyncFunctionExecutorTests.cpp (1)

134-138: ArgumentTooLarge test adds useful boundary coverage.

Consider adding a 65535‑byte success case to pin the exact limit.

include/bringauto/async_function_execution/clients/AeronClient.hpp (4)

171-174: Verify IdleStrategy interface consistency in waitForAnyMessage.

Here idle(fragmentsRead) is called (1 arg), but waitForMessage calls idle(fragmentsRead, timeout). Ensure the chosen IdleStrategy supports both or refactor to a single consistent signature.


74-82: Improve log context for timeouts.

Include pub/sub IDs to aid diagnosis.

- std::cerr << "Aeron connection error: Timeout while waiting for publication" << std::endl;
+ std::cerr << "Aeron connection error: Timeout while waiting for publication id=" << pubId << std::endl;
@@
- std::cerr << "Aeron connection error: Timeout while waiting for subscription" << std::endl;
+ std::cerr << "Aeron connection error: Timeout while waiting for subscription id=" << subId << std::endl;

Also applies to: 89-97


121-129: Minor: avoid const_cast by constructing AtomicBuffer from a mutable copy.

Not critical, but reduces UB risk if the impl writes. Optional.


65-110: Optional: brief sleep in connect loops to reduce CPU.

Yield-only loops can still spin. A tiny sleep (e.g., 1ms) reduces load without hurting latency.

include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (1)

401-407: Redundant empty check.

Line 401 checks if bytes.empty() before accessing bytes[0] on line 404. However, this check is redundant because:

  • In pollFunction() (line 244), an empty span is already returned if requestBytes.empty()
  • The check adds minimal value since callers should validate before calling this private method

Consider removing the redundant check or documenting why it's defensive:

 	std::tuple<FunctionId, std::span<const uint8_t>> deserializeRequest(const std::span<const uint8_t>& bytes) {
-		if (bytes.empty()) {
-			throw std::invalid_argument("Not enough data to deserialize request");
-		}
+		// bytes guaranteed non-empty by pollFunction()
 		FunctionId funcId{ bytes[0] };
 		std::span<const uint8_t> args(bytes.begin() + 1, bytes.end());
 		return std::make_tuple(funcId, args);
 	}
test/include/MockClient.hpp (1)

80-88: Derive expected timeout from test configuration instead of hardcoding formula
In MockClient::waitForMessage (test/include/MockClient.hpp:80–82), replace the literal (channelId - 1000) * 1'000'000 with a lookup of the configured timeout for functionId = channelId - 1000. This ensures the EXPECT_EQ compares against the actual test setup and remains correct if timeouts change.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea6466b and 5dca83f.

📒 Files selected for processing (19)
  • README.md (2 hunks)
  • examples/README.md (1 hunks)
  • examples/main_consumer.cpp (1 hunks)
  • examples/main_producer.cpp (1 hunks)
  • include/bringauto/async_function_execution/AeronDriver.hpp (1 hunks)
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (17 hunks)
  • include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1 hunks)
  • include/bringauto/async_function_execution/clients/AeronClient.hpp (4 hunks)
  • include/bringauto/async_function_execution/clients/ClientInterface.hpp (2 hunks)
  • include/bringauto/async_function_execution/structures/Settings.hpp (2 hunks)
  • source/bringauto/async_function_execution/AeronDriver.cpp (2 hunks)
  • source/bringauto/async_function_execution/TimeoutIdleStrategy.cpp (1 hunks)
  • test/README.md (1 hunks)
  • test/include/AsyncFunctionExecutorTests.hpp (2 hunks)
  • test/include/MockClient.hpp (6 hunks)
  • test/integration_tests/main_segfault_consumer.cpp (3 hunks)
  • test/integration_tests/main_segfault_producer.cpp (4 hunks)
  • test/integration_tests/main_simple_function.cpp (3 hunks)
  • test/source/AsyncFunctionExecutorTests.cpp (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • examples/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/main_producer.cpp
  • source/bringauto/async_function_execution/TimeoutIdleStrategy.cpp
🧰 Additional context used
🧬 Code graph analysis (8)
test/integration_tests/main_simple_function.cpp (1)
test/include/AsyncFunctionExecutorTests.hpp (1)
  • executorProducer (94-97)
include/bringauto/async_function_execution/clients/ClientInterface.hpp (3)
include/bringauto/async_function_execution/clients/AeronClient.hpp (5)
  • channelId (121-129)
  • channelId (121-121)
  • channelId (139-156)
  • channelId (139-139)
  • channelId (164-183)
test/include/MockClient.hpp (4)
  • channelId (22-78)
  • channelId (22-22)
  • channelId (80-88)
  • channelId (80-80)
include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1)
  • timeout (64-64)
test/source/AsyncFunctionExecutorTests.cpp (1)
test/include/AsyncFunctionExecutorTests.hpp (1)
  • executorProducer (94-97)
test/include/MockClient.hpp (4)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (4)
  • funcId (331-337)
  • funcId (331-331)
  • bytes (400-407)
  • bytes (400-400)
include/bringauto/async_function_execution/clients/AeronClient.hpp (5)
  • channelId (121-129)
  • channelId (121-121)
  • channelId (139-156)
  • channelId (139-139)
  • channelId (164-183)
include/bringauto/async_function_execution/clients/ClientInterface.hpp (2)
  • channelId (32-32)
  • channelId (41-41)
include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1)
  • timeout (64-64)
test/integration_tests/main_segfault_producer.cpp (1)
test/include/AsyncFunctionExecutorTests.hpp (1)
  • executorProducer (94-97)
include/bringauto/async_function_execution/clients/AeronClient.hpp (3)
include/bringauto/async_function_execution/clients/ClientInterface.hpp (3)
  • subscriptionIds (23-23)
  • channelId (32-32)
  • channelId (41-41)
test/include/MockClient.hpp (6)
  • subscriptionIds (17-20)
  • subscriptionIds (17-17)
  • channelId (22-78)
  • channelId (22-22)
  • channelId (80-88)
  • channelId (80-80)
include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1)
  • timeout (64-64)
examples/main_consumer.cpp (2)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (4)
  • bytes (400-407)
  • bytes (400-400)
  • funcId (331-337)
  • funcId (331-331)
test/include/MockClient.hpp (8)
  • bytes (113-127)
  • bytes (113-113)
  • bytes (130-143)
  • bytes (130-130)
  • funcId (146-153)
  • funcId (146-146)
  • funcId (156-167)
  • funcId (156-156)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (2)
test/include/MockClient.hpp (8)
  • funcId (146-153)
  • funcId (146-146)
  • funcId (156-167)
  • funcId (156-156)
  • bytes (113-127)
  • bytes (113-113)
  • bytes (130-143)
  • bytes (130-130)
include/bringauto/async_function_execution/clients/AeronClient.hpp (1)
  • buffer (186-191)
🪛 markdownlint-cli2 (0.18.1)
README.md

24-24: Hard tabs
Column: 1

(MD010, no-hard-tabs)


25-25: Hard tabs
Column: 1

(MD010, no-hard-tabs)


26-26: Hard tabs
Column: 1

(MD010, no-hard-tabs)


27-27: Hard tabs
Column: 1

(MD010, no-hard-tabs)


28-28: Hard tabs
Column: 1

(MD010, no-hard-tabs)


29-29: Hard tabs
Column: 1

(MD010, no-hard-tabs)


30-30: Hard tabs
Column: 1

(MD010, no-hard-tabs)


49-49: Hard tabs
Column: 1

(MD010, no-hard-tabs)


54-54: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)

🔇 Additional comments (35)
source/bringauto/async_function_execution/AeronDriver.cpp (3)

3-3: Good modernization: C++ header preferred.

Replacing <signal.h> with <csignal> aligns with modern C++ best practices.


29-29: Good modernization: nullptr is type-safe.

Using nullptr instead of NULL is a C++11 best practice that provides better type safety.


37-42: Const-correctness applied logically.

Marking run() as const is technically valid since it doesn't modify the observable state of the AeronDriver wrapper object itself. The underlying aeron_driver_t state modifications are external to this wrapper's members.

This design treats the wrapper as a handle to the driver rather than the driver itself—a reasonable approach for RAII/wrapper classes.

include/bringauto/async_function_execution/AeronDriver.hpp (2)

27-27: Declaration matches implementation.

The const qualifier aligns with the implementation in AeronDriver.cpp and correctly reflects that this method doesn't modify the wrapper object's observable state.


33-33: [[nodiscard]] appropriately applied.

Adding [[nodiscard]] to isRunning() is correct since this is a pure query method with no side effects. Ignoring its return value would be a logic error that this attribute helps catch at compile time.

test/integration_tests/main_simple_function.cpp (2)

20-23: FunctionList brace-init looks good.

Also applies to: 29-32


65-72: Good: handle connect() failures.

Checking the int return and failing fast improves test reliability.

test/include/AsyncFunctionExecutorTests.hpp (3)

15-15: Explicit SerializableString ctor is appropriate.


58-66: Typed per‑function timeouts wired correctly.

Config uses FunctionConfigs with nanoseconds per ID; consistent with MockClient timeout checks.


67-74: FunctionList brace-init update is fine.

test/integration_tests/main_segfault_consumer.cpp (1)

39-43: Good: handle connect() failure.

test/integration_tests/main_segfault_producer.cpp (2)

54-57: Good: connect() error handling.


64-65: Good: pass SerializableString to callFunc.

test/source/AsyncFunctionExecutorTests.cpp (2)

118-126: Undefined function config test updated correctly.


30-38: Potential flakiness: PollFunction has no arranged request.

If no producer request is sent before this test, pollFunction may block or return empty. Ensure the test sends a request (or uses a mock setup) within this test.

include/bringauto/async_function_execution/clients/AeronClient.hpp (2)

139-151: Channel existence checks and timeout-aware idle are good.

Nice guard on missing channel and timeout return path.


1-207: Ignore unintended default‐instantiation warning: all AeronClient usages (e.g., in AsyncFunctionExecutor) explicitly specify <TimeoutIdleStrategy>, so no default template argument is ever used.

Likely an incorrect or invalid review comment.

test/include/MockClient.hpp (7)

12-12: LGTM!

Marking the mock class as final is appropriate and enables compiler optimizations since this is a leaf test class with no intended inheritance.


45-78: Const-correctness improvements look good.

The addition of const qualifiers to local variables (funcId, args, sum, product) improves code clarity and prevents accidental mutations. The logic remains unchanged and correct.


113-127: Const-correctness improvements look good.

The parameter is now const std::span<const uint8_t> &bytes (line 113), and local variables argCount (line 115) and argSize (line 119) are marked const. This prevents accidental mutations and improves readability.


130-143: Const-correctness improvements look good.

Similar to deserializeIntRequest, the parameter and local variables (argCount at line 132, argSize at line 136) are now properly const-qualified.


146-153: Const-correctness improvements look good.

The parameters funcId and returnValue are now const-qualified (line 146), and the local variable at line 149 is marked const. The serialization logic remains correct.


156-167: Const-correctness improvements look good.

The parameters are const-qualified (line 156), and local size calculation (lines 162-164) is properly const. The validation at lines 159-161 correctly prevents oversized strings from being serialized.


91-109: No action needed: size encoding matches AsyncFunctionExecutor’s little-endian 16-bit format.

include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (11)

22-23: LGTM!

The Config structure now uses structures::FunctionConfigs instead of a raw type, providing better type safety and structure for per-function timeout configurations.


28-32: Document supported range for FunctionId.

Line 28 documents that the supported range is 0-255, which is appropriate given value is uint8_t. This is a helpful clarification for users.


46-72: Explicit constructors improve type safety.

The Return<T> (line 51), Return<void> (line 60), and Arguments<Args...> (line 71) constructors are now explicit, preventing implicit conversions and improving type safety. The added value and values members (lines 50, 70) make the structures more self-documenting.


77-127: Strong typing for FunctionDefinition and FunctionList.

The additions provide compile-time guarantees:

  • Lines 89-108: Type trait is_function_definition and concept IsFunctionDefinition ensure only valid FunctionDefinition types are used
  • Lines 115-119: FunctionList is now constrained by IsFunctionDefinition and uses an explicit constructor
  • Lines 122-126: Deduction guide simplifies construction

These changes strengthen the type system and improve compile-time safety.


176-179: Const-correctness in lambda captures looks good.

Lines 176 and 179 now use const auto& in lambda captures within std::apply, preventing unnecessary copies and accidental mutations.


212-216: Per-function timeout lookup is correct.

Lines 212-216 implement per-function timeout resolution:

  • First checks if the function ID exists in functionConfigs.configs
  • Falls back to defaultTimeout if not found
  • Uses the ternary operator for concise selection

The logic is correct and handles the fallback case properly.


243-250: Const-correctness improvements look good.

Line 243 adds const to requestBytes, preventing accidental modification. The rest of the method remains unchanged.


271-303: Const-correctness and validation improvements.

Line 271 adds an empty() check before accessing argBytes[pos], preventing potential out-of-bounds access. Line 285 marks the length variable const. These changes improve safety and code clarity.


333-337: Const-correctness in lambda captures looks good.

Lines 333 and 335 use const auto& in lambda captures, consistent with the changes at lines 176-179.


343-381: Const-correctness in serialization helpers looks good.

Lines 343, 355, 370, 372, 376, and 378 add const qualifiers to size calculations and intermediate variables. This prevents accidental mutations and improves code clarity without changing behavior.


412-412: Documentation improvement acknowledged.

The comment clarifies that client_ can be a custom ClientInterface implementation, which is helpful for users extending the library.

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: 1

🧹 Nitpick comments (2)
examples/main_producer.cpp (1)

52-57: Consider demonstrating error handling for callFunc().

The example calls .value() directly on the std::expected return, which will throw on error. Since this is a producer example demonstrating the API, consider showing the recommended error-handling pattern:

auto result1 = executor.callFunc(ExampleFunc1, 42, "Hello", 3.14f);
if (!result1.has_value()) {
    std::cerr << "ExampleFunc1 failed with error" << std::endl;
    return 1;
}
std::cout << result1.value().value << std::endl;

This would help users understand how to handle errors gracefully.

include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (1)

149-175: Consider updating the error message consistency.

The error message on line 172 starts with "Warning:" but immediately throws a std::runtime_error. Consider changing "Warning:" to "Error:" for consistency since this is a fatal error that prevents construction.

-				throw std::runtime_error("Warning: Function ID " + std::to_string(funcId) + " in configuration is not defined in FunctionList.");
+				throw std::runtime_error("Error: Function ID " + std::to_string(funcId) + " in configuration is not defined in FunctionList.");

Otherwise, the validation logic is sound and provides good protection against configuration errors.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5dca83f and 67d16fb.

📒 Files selected for processing (10)
  • CMakeLists.txt (3 hunks)
  • examples/CMakeLists.txt (1 hunks)
  • examples/main_producer.cpp (1 hunks)
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (17 hunks)
  • test/CMakeLists.txt (1 hunks)
  • test/include/AsyncFunctionExecutorTests.hpp (2 hunks)
  • test/include/MockClient.hpp (6 hunks)
  • test/integration_tests/main_segfault_producer.cpp (4 hunks)
  • test/integration_tests/main_simple_function.cpp (3 hunks)
  • test/source/AsyncFunctionExecutorTests.cpp (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/CMakeLists.txt
  • test/CMakeLists.txt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-02T13:53:35.438Z
Learnt from: MarioIvancik
PR: bringauto/async-function-execution#3
File: include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp:172-185
Timestamp: 2025-10-02T13:53:35.438Z
Learning: In the async-function-execution project tests (test/include/AsyncFunctionExecutorTests.hpp), the MockClient's connect() method is a no-op that always returns 0, so checking return values in test setup is not required.

Applied to files:

  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
  • test/include/MockClient.hpp
🧬 Code graph analysis (6)
test/integration_tests/main_simple_function.cpp (1)
test/include/AsyncFunctionExecutorTests.hpp (1)
  • executorProducer (102-105)
examples/main_producer.cpp (2)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (2)
  • bytes (420-427)
  • bytes (420-420)
test/include/MockClient.hpp (4)
  • bytes (119-133)
  • bytes (119-119)
  • bytes (136-149)
  • bytes (136-136)
test/source/AsyncFunctionExecutorTests.cpp (1)
test/include/AsyncFunctionExecutorTests.hpp (1)
  • executorProducer (102-105)
test/integration_tests/main_segfault_producer.cpp (1)
test/include/AsyncFunctionExecutorTests.hpp (1)
  • executorProducer (102-105)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (2)
test/include/MockClient.hpp (10)
  • funcId (152-159)
  • funcId (152-152)
  • funcId (162-173)
  • funcId (162-162)
  • funcId (176-180)
  • funcId (176-176)
  • bytes (119-133)
  • bytes (119-119)
  • bytes (136-149)
  • bytes (136-136)
include/bringauto/async_function_execution/clients/AeronClient.hpp (1)
  • buffer (186-191)
test/include/MockClient.hpp (3)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (4)
  • funcId (347-353)
  • funcId (347-347)
  • bytes (420-427)
  • bytes (420-420)
include/bringauto/async_function_execution/clients/ClientInterface.hpp (2)
  • channelId (32-32)
  • channelId (41-41)
include/bringauto/async_function_execution/clients/AeronClient.hpp (5)
  • channelId (121-129)
  • channelId (121-121)
  • channelId (139-156)
  • channelId (139-139)
  • channelId (164-183)
🔇 Additional comments (39)
examples/main_producer.cpp (3)

6-17: LGTM!

The SerializableString implementation is straightforward and correct for the example's purpose. The serialize() method returns a span pointing to the internal string data, which is safe given the usage pattern in this example.


19-35: LGTM!

The function definitions are well-structured and demonstrate the variety of argument types supported by the AsyncFunctionExecutor.


39-45: LGTM!

The AsyncFunctionExecutor construction is correct and follows the new configuration pattern with per-function capabilities.

test/include/MockClient.hpp (6)

12-12: LGTM!

Marking the test mock as final is good practice and prevents unintended inheritance.


86-94: LGTM!

The timeout verification logic correctly validates that per-function timeouts are properly propagated to the client layer. The formula (channelId - 1000) * 1000000 aligns with the test configuration in AsyncFunctionExecutorTests.hpp.


55-59: LGTM!

The FunctionWait handler correctly demonstrates void return type handling and simulates asynchronous work with a sleep. This aligns with the new FunctionWait definition in the test suite.


119-149: LGTM!

The const-correctness improvements to the deserialization methods are well done. Using const qualifiers consistently improves code clarity and prevents accidental modifications.


152-173: LGTM!

The serialization methods now use const parameters consistently, which improves API clarity and safety.


175-180: LGTM!

The new serializeVoidResponse method correctly handles void return types by serializing only the function ID. This is essential for testing functions with void returns.

include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (14)

20-24: LGTM!

The Config struct now uses a typed FunctionConfigs structure instead of string-based configuration, improving type safety and API clarity.


28-33: LGTM!

The documentation update clarifies the supported function ID range (0-255), which aligns with the uint8_t storage type.


49-62: LGTM!

The Return constructors are well-designed with explicit keyword to prevent implicit conversions, perfect forwarding for efficiency, and constexpr for compile-time evaluation where possible.


69-73: LGTM!

The Arguments constructor follows the same good design principles as Return, using explicit, constexpr, and perfect forwarding.


90-109: LGTM!

The type trait and concept provide compile-time validation that FunctionList only contains valid FunctionDefinition types, improving type safety.


112-127: LGTM!

The FunctionList design with concept constraints and deduction guide provides a clean, type-safe API for defining function lists.


130-140: LGTM!

The CallError enum provides comprehensive error states for function calls, enabling proper error handling and diagnostics.


180-200: LGTM!

The connect() method correctly initializes the in-progress tracking map and returns the connection status. The use of std::apply with a lambda to populate the channel vectors and in-progress map is elegant.


203-244: LGTM!

The callFunc() method is well-implemented with proper error handling, in-progress tracking, and per-function timeout support. The validation checks cover all error cases, and the in-progress flag is properly managed on both success and error paths.


247-266: LGTM!

The pollFunction() method correctly retrieves incoming function calls and deserializes the request into function ID and arguments.


269-319: LGTM!

The getFunctionArgs() method has proper validation, const-correctness, and handles both serializable and trivially-copyable types correctly.


322-344: LGTM!

The sendReturnMessage() method has appropriate validation and returns proper error codes.


400-417: LGTM!

The deserializeReturn() method correctly handles both void and non-void return types using if constexpr, and properly returns std::expected for error handling.


430-437: LGTM!

The in-progress tracking map using std::atomic_bool provides thread-safe tracking of concurrent function calls, preventing race conditions.

test/integration_tests/main_simple_function.cpp (3)

15-32: LGTM!

The FunctionList initialization uses the new direct braced form, which is clean and leverages the deduction guide.


65-72: LGTM!

The connect() calls now have proper error handling with descriptive error messages and early returns on failure.


80-80: LGTM!

Using .value() directly in an integration test is acceptable, as any error will cause the test to fail appropriately.

test/include/AsyncFunctionExecutorTests.hpp (4)

12-23: LGTM!

Making the SerializableString constructor explicit prevents implicit conversions and improves type safety in the tests.


55-59: LGTM!

The FunctionWait definition is useful for testing void return types and function call in-progress tracking.


61-83: LGTM!

The per-function timeout configuration uses the new typed FunctionConfigs structure and provides good test coverage with distinct timeouts for each function. The timeout pattern (funcId * 1000000 ns) aligns with the MockClient's verification logic.


85-97: LGTM!

The consumer configuration is appropriately simplified, and the FunctionList correctly excludes FunctionWait since it's only needed for producer tests.

test/integration_tests/main_segfault_producer.cpp (4)

9-21: LGTM!

The explicit constructor for SerializableString is consistent with other test files and improves type safety.


29-37: LGTM!

The FunctionList initialization uses the new direct braced form correctly.


54-57: LGTM!

The connect() error handling is properly implemented with descriptive error messages.


64-64: LGTM!

The callFunc call now uses explicit SerializableString construction, which is required due to the explicit constructor. Using .value() is acceptable in integration tests.

test/source/AsyncFunctionExecutorTests.cpp (5)

5-24: LGTM!

The tests correctly use .value() to unwrap std::expected results and use explicit SerializableString construction where needed.


72-107: LGTM!

The tests now use the std::expected API correctly, checking .error() for expected error cases instead of catching exceptions. This is cleaner and more explicit.


110-128: LGTM!

The test correctly verifies that providing configuration for undefined function IDs throws an error, using the new typed FunctionConfigs structure.


131-138: LGTM!

The ArgumentTooLarge test correctly validates that arguments exceeding the 2^16 byte limit are properly rejected with std::invalid_argument.


141-155: LGTM!

The FunctionCallInProgress test correctly validates that concurrent calls to the same function are prevented by the in-progress tracking mechanism.

Note: This test relies on timing (50ms sleep) to catch the in-progress state. While the 200ms function execution time in MockClient provides margin, timing-based tests can occasionally be flaky in heavily loaded CI environments.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (1)

347-357: Incorrect size calculation for types with serialize().

Line 351 uses sizeof(args) to estimate buffer size, but if an argument has a serialize() method, its actual serialized size may differ from sizeof. This causes over- or under-reservation in serializationBuffer_.

While reserve() is just a performance hint and won't cause correctness issues, accurate sizing avoids unnecessary reallocations. To fix this, you'd need to call serialize() on each argument conditionally, which complicates the code. Consider whether accurate pre-sizing is worth the complexity, or document that the reserve is a heuristic.

The same issue exists at Line 363 in serializeReturn.

♻️ Duplicate comments (1)
examples/main_consumer.cpp (1)

21-23: Avoid hardcoded function ID constants.

The function definitions already provide IDs via their id.value members. Using the constants in switch cases creates maintenance burden if IDs change.

Based on learnings

Remove these constants and use ExampleFunc1.id.value, ExampleFunc2.id.value, and ExampleFunc3.id.value directly in the switch cases (lines 61, 67, 73).

🧹 Nitpick comments (5)
include/bringauto/async_function_execution/structures/Settings.hpp (2)

26-31: Optimize getConfig to avoid double lookup.

The method calls both find() and at(), performing two map lookups when one suffices.

Apply this diff:

 FunctionConfig getConfig(const uint8_t functionId) const {
-	if (configs.find(functionId) != configs.end()) {
-		return configs.at(functionId);
+	auto it = configs.find(functionId);
+	if (it != configs.end()) {
+		return it->second;
 	}
 	return FunctionConfig{};
 }

37-43: Pre-allocate vector capacity in getFunctionIds.

Reserving capacity upfront avoids multiple reallocations.

Apply this diff:

 std::vector<uint8_t> getFunctionIds() const {
 	std::vector<uint8_t> functionIds;
+	functionIds.reserve(configs.size());
 	for (const auto& [funcId, _] : configs) {
 		functionIds.push_back(funcId);
 	}
 	return functionIds;
 }
examples/main_consumer.cpp (1)

57-83: Handle empty function ID from pollFunction.

When pollFunction() times out or fails, it returns an empty FunctionId{} (value 0). The switch doesn't guard against this, so value 0 would hit the default case and throw, which may not be the desired behavior for a timeout.

Add a check before the switch:

 while (true) {
 	auto [funcId, argBytes] = executor.pollFunction();
+	
+	if (funcId.value == 0 && argBytes.empty()) {
+		continue; // Timeout or no message, retry
+	}
 	
 	switch (funcId.value) {
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (2)

163-167: Remove "Warning:" prefix from exception message.

The message begins with "Warning:" but immediately throws, making it an error rather than a warning.

Apply this diff:

 	if (!isFunctionDefined(FunctionId{funcId})) {
-		throw std::runtime_error("Warning: Function ID " + std::to_string(funcId) + " in configuration is not defined in FunctionList.");
+		throw std::runtime_error("Function ID " + std::to_string(funcId) + " in configuration is not defined in FunctionList");
 	}

339-345: Simplify fold expression for clarity.

The ternary operator in Line 342 is correct but potentially confusing. The false branch doesn't modify found, relying on the reader to understand ternary semantics.

Use a clearer idiom:

 bool isFunctionDefined(const FunctionId &funcId) {
 	bool found = false;
 	std::apply([&](const auto&... funcDefs) {
-		((funcDefs.id.value == funcId.value ? found = true : false), ...);
+		((found = found || (funcDefs.id.value == funcId.value)), ...);
 	}, functions_.functions);
 	return found;
 }

Or more concisely:

 bool isFunctionDefined(const FunctionId &funcId) {
 	bool found = false;
 	std::apply([&](const auto&... funcDefs) {
-		((funcDefs.id.value == funcId.value ? found = true : false), ...);
+		(((funcDefs.id.value == funcId.value) && (found = true)), ...);
 	}, functions_.functions);
 	return found;
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 13f803b and 609e1fb.

📒 Files selected for processing (8)
  • examples/main_consumer.cpp (1 hunks)
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (17 hunks)
  • include/bringauto/async_function_execution/Constants.hpp (1 hunks)
  • include/bringauto/async_function_execution/clients/AeronClient.hpp (4 hunks)
  • include/bringauto/async_function_execution/structures/Settings.hpp (2 hunks)
  • test/include/AsyncFunctionExecutorTests.hpp (2 hunks)
  • test/include/MockClient.hpp (6 hunks)
  • test/source/AsyncFunctionExecutorTests.cpp (5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 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:

  • test/include/AsyncFunctionExecutorTests.hpp
  • include/bringauto/async_function_execution/clients/AeronClient.hpp
  • examples/main_consumer.cpp
  • test/source/AsyncFunctionExecutorTests.cpp
  • include/bringauto/async_function_execution/Constants.hpp
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
📚 Learning: 2025-10-02T13:53:35.438Z
Learnt from: MarioIvancik
PR: bringauto/async-function-execution#3
File: include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp:172-185
Timestamp: 2025-10-02T13:53:35.438Z
Learning: In the async-function-execution project tests (test/include/AsyncFunctionExecutorTests.hpp), the MockClient's connect() method is a no-op that always returns 0, so checking return values in test setup is not required.

Applied to files:

  • test/include/AsyncFunctionExecutorTests.hpp
  • include/bringauto/async_function_execution/clients/AeronClient.hpp
  • test/include/MockClient.hpp
  • test/source/AsyncFunctionExecutorTests.cpp
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
📚 Learning: 2024-10-22T11:03:16.796Z
Learnt from: MarioIvancik
PR: bringauto/fleet-protocol-http-client-cxx#13
File: example/main.cpp:30-32
Timestamp: 2024-10-22T11:03:16.796Z
Learning: In `example/main.cpp`, the code serves as an example implementation and may not require detailed error messaging enhancements.

Applied to files:

  • examples/main_consumer.cpp
📚 Learning: 2024-10-22T11:03:33.918Z
Learnt from: MarioIvancik
PR: bringauto/fleet-protocol-http-client-cxx#13
File: example/main.cpp:29-37
Timestamp: 2024-10-22T11:03:33.918Z
Learning: In the C++ project, `example/main.cpp` is an example implementation and may not require refactoring or code optimization during code reviews.

Applied to files:

  • examples/main_consumer.cpp
🧬 Code graph analysis (5)
include/bringauto/async_function_execution/clients/AeronClient.hpp (3)
test/include/MockClient.hpp (6)
  • subscriptionIds (17-20)
  • subscriptionIds (17-17)
  • channelId (22-85)
  • channelId (22-22)
  • channelId (87-95)
  • channelId (87-87)
include/bringauto/async_function_execution/clients/ClientInterface.hpp (3)
  • subscriptionIds (23-23)
  • channelId (32-32)
  • channelId (41-41)
include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1)
  • timeout (64-64)
test/include/MockClient.hpp (3)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (4)
  • funcId (339-345)
  • funcId (339-339)
  • bytes (412-419)
  • bytes (412-412)
include/bringauto/async_function_execution/clients/AeronClient.hpp (5)
  • channelId (121-129)
  • channelId (121-121)
  • channelId (139-156)
  • channelId (139-139)
  • channelId (164-183)
include/bringauto/async_function_execution/clients/ClientInterface.hpp (2)
  • channelId (32-32)
  • channelId (41-41)
examples/main_consumer.cpp (2)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (4)
  • bytes (412-419)
  • bytes (412-412)
  • funcId (339-345)
  • funcId (339-339)
test/include/MockClient.hpp (10)
  • bytes (120-134)
  • bytes (120-120)
  • bytes (137-150)
  • bytes (137-137)
  • funcId (153-160)
  • funcId (153-153)
  • funcId (163-174)
  • funcId (163-163)
  • funcId (177-181)
  • funcId (177-177)
test/source/AsyncFunctionExecutorTests.cpp (1)
test/include/AsyncFunctionExecutorTests.hpp (1)
  • executorProducer (102-105)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (3)
test/include/MockClient.hpp (10)
  • funcId (153-160)
  • funcId (153-153)
  • funcId (163-174)
  • funcId (163-163)
  • funcId (177-181)
  • funcId (177-177)
  • bytes (120-134)
  • bytes (120-120)
  • bytes (137-150)
  • bytes (137-137)
include/bringauto/async_function_execution/TimeoutIdleStrategy.hpp (1)
  • timeout (64-64)
include/bringauto/async_function_execution/clients/AeronClient.hpp (1)
  • buffer (186-191)
🪛 Clang (14.0.6)
examples/main_consumer.cpp

[error] 1-1: 'bringauto/async_function_execution/AsyncFunctionExecutor.hpp' file not found

(clang-diagnostic-error)

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: 2

🧹 Nitpick comments (1)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (1)

124-133: Consider expanding enum documentation.

The inline comments are helpful, but each error case could benefit from more detailed documentation explaining:

  • When the error occurs
  • How to resolve it
  • Example scenarios

This would improve developer experience when handling these errors.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 570a0d3 and faa7238.

📒 Files selected for processing (1)
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (17 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-03T10:39:48.715Z
Learnt from: MarioIvancik
Repo: bringauto/async-function-execution PR: 3
File: include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp:214-244
Timestamp: 2025-11-03T10:39:48.715Z
Learning: In the bringauto/async-function-execution repository, exceptions thrown from AsyncFunctionExecutor::callFunc() (specifically from client_->sendMessage(), client_->waitForMessage(), or deserializeReturn()) are considered fatal errors indicating unrecoverable infrastructure failures. The program is expected to terminate in such cases, so exception safety cleanup (like resetting callInProgress_ flags) is intentionally not implemented.

Applied to files:

  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
📚 Learning: 2025-10-02T13:53:28.773Z
Learnt from: MarioIvancik
Repo: bringauto/async-function-execution PR: 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/async_function_execution/AsyncFunctionExecutor.hpp
📚 Learning: 2025-10-02T13:53:35.438Z
Learnt from: MarioIvancik
Repo: bringauto/async-function-execution PR: 3
File: include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp:172-185
Timestamp: 2025-10-02T13:53:35.438Z
Learning: In the async-function-execution project tests (test/include/AsyncFunctionExecutorTests.hpp), the MockClient's connect() method is a no-op that always returns 0, so checking return values in test setup is not required.

Applied to files:

  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
📚 Learning: 2024-10-22T11:03:16.796Z
Learnt from: MarioIvancik
Repo: bringauto/fleet-protocol-http-client-cxx PR: 13
File: example/main.cpp:30-32
Timestamp: 2024-10-22T11:03:16.796Z
Learning: In `example/main.cpp`, the code serves as an example implementation and may not require detailed error messaging enhancements.

Applied to files:

  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
🧬 Code graph analysis (1)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (2)
test/include/MockClient.hpp (10)
  • funcId (153-160)
  • funcId (153-153)
  • funcId (163-174)
  • funcId (163-163)
  • funcId (177-181)
  • funcId (177-177)
  • bytes (120-134)
  • bytes (120-120)
  • bytes (137-150)
  • bytes (137-137)
include/bringauto/async_function_execution/structures/Settings.hpp (4)
  • functionId (26-31)
  • functionId (26-26)
  • functionId (33-35)
  • functionId (33-33)
🔇 Additional comments (12)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (12)

20-24: LGTM! Typed configuration improves type safety.

The change from std::string_view (JSON-based) to structures::FunctionConfigs aligns with the PR's objective to use typed structures and eliminates runtime JSON parsing errors.


93-109: LGTM! Strong type constraints improve compile-time safety.

The is_function_definition trait and IsFunctionDefinition concept provide robust compile-time validation that FunctionList only accepts FunctionDefinition types.


116-120: LGTM! Explicit constructor prevents implicit conversions.

The IsFunctionDefinition constraint and explicit constructor are appropriate defensive measures.


214-244: LGTM! Good use of std::expected for error handling.

The function properly validates inputs, tracks call-in-progress state, and uses std::expected for clear error propagation. The per-function timeout configuration is a nice addition.

Note: As per learnings, exceptions from sendMessage(), waitForMessage(), or deserializeReturn() indicate fatal infrastructure failures, so the callInProgress_ flag is intentionally not reset in exception paths.

Based on learnings.


253-266: LGTM! Updated to use waitForAnyMessage().

The change from waitForMessage() to waitForAnyMessage() is appropriate for consumer-mode polling where the executor should receive any incoming function call.


287-287: LGTM! More idiomatic empty check.

Using argBytes.empty() is clearer than argBytes.size() < 1.


330-344: LGTM! Consistent channelOffset handling.

The method correctly applies channelOffset_ to the return channel ID, maintaining consistency with the producer-side expectations.


347-353: LGTM! Appropriate use of const reference.

Changing from auto&&... to const auto&... is correct since the lambda only reads the function definitions without modifying them.


356-365: Fold expression evaluation order is deterministic.

Line 359 uses a fold expression ((totalSize += 2 + sizeof(args)), ...) which evaluates left-to-right. This is well-defined in C++17 and later.

However, note that a past reviewer (koudis) raised a concern about "order of argument evaluation" being "dangerous" and potentially "random". If this refers to a different line or aspect of the code, please clarify.


401-417: LGTM! Proper std::expected usage for void and non-void returns.

The method correctly handles both void and non-void return types with std::expected, providing clear error propagation for function ID mismatches.


420-440: LGTM! Thread-safe call tracking with atomic_bool.

The use of std::atomic_bool in callInProgress_ (line 437) ensures thread-safe concurrent access to the in-progress flags. The added documentation for private members is also helpful.


10-10: No issues found. C++23 standard is properly configured.

The codebase is set to use C++23 (CMAKE_CXX_STANDARD is set to 23 in CMakeLists.txt), so the inclusion of <expected> is valid and properly supported.

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: 0

🧹 Nitpick comments (2)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (2)

163-167: Consider validating in reverse: iterate FunctionList to check configs.

Currently, the code iterates functionConfigs.getFunctionIds() and validates each is defined. Per review feedback, consider iterating the FunctionList instead and validating that required functions have configurations, which would make the validation direction match the primary source of truth (the FunctionList).

Apply this diff to iterate FunctionList:

-		for (const auto &funcId: settings_.functionConfigs.getFunctionIds()) {
-			if (!isFunctionDefined(FunctionId{funcId})) {
-				throw std::runtime_error("Warning: Function ID " + std::to_string(funcId) + " in configuration is not defined in FunctionList.");
-			}
-		}
+		std::apply([&](const auto&... funcDefs) {
+			// Validation logic here if needed for each function definition
+			((void)funcDefs, ...);
+		}, functions_.functions);

Note: Since FunctionConfigs::getConfig() returns a default FunctionConfig{} for missing entries, explicit validation may not be necessary unless you want to warn about orphaned config entries.


386-394: Consider explicit parentheses in bitwise operations for clarity.

The expressions size >> 8 & 0xFF at lines 388 and 394 are correct due to operator precedence (>> binds before &), but adding explicit parentheses improves readability:

-		buffer.push_back(static_cast<uint8_t>(size >> 8 & 0xFF));
+		buffer.push_back(static_cast<uint8_t>((size >> 8) & 0xFF));

Apply similarly at line 394.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between faa7238 and ebd4c65.

📒 Files selected for processing (3)
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (17 hunks)
  • test/include/AsyncFunctionExecutorTests.hpp (2 hunks)
  • test/include/MockClient.hpp (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/include/MockClient.hpp
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-10-02T13:53:35.438Z
Learnt from: MarioIvancik
Repo: bringauto/async-function-execution PR: 3
File: include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp:172-185
Timestamp: 2025-10-02T13:53:35.438Z
Learning: In the async-function-execution project tests (test/include/AsyncFunctionExecutorTests.hpp), the MockClient's connect() method is a no-op that always returns 0, so checking return values in test setup is not required.

Applied to files:

  • test/include/AsyncFunctionExecutorTests.hpp
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
📚 Learning: 2025-10-02T13:53:28.773Z
Learnt from: MarioIvancik
Repo: bringauto/async-function-execution PR: 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:

  • test/include/AsyncFunctionExecutorTests.hpp
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
📚 Learning: 2025-11-03T10:39:48.715Z
Learnt from: MarioIvancik
Repo: bringauto/async-function-execution PR: 3
File: include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp:214-244
Timestamp: 2025-11-03T10:39:48.715Z
Learning: In the bringauto/async-function-execution repository, exceptions thrown from AsyncFunctionExecutor::callFunc() (specifically from client_->sendMessage(), client_->waitForMessage(), or deserializeReturn()) are considered fatal errors indicating unrecoverable infrastructure failures. The program is expected to terminate in such cases, so exception safety cleanup (like resetting callInProgress_ flags) is intentionally not implemented.

Applied to files:

  • test/include/AsyncFunctionExecutorTests.hpp
  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
📚 Learning: 2024-10-22T11:03:16.796Z
Learnt from: MarioIvancik
Repo: bringauto/fleet-protocol-http-client-cxx PR: 13
File: example/main.cpp:30-32
Timestamp: 2024-10-22T11:03:16.796Z
Learning: In `example/main.cpp`, the code serves as an example implementation and may not require detailed error messaging enhancements.

Applied to files:

  • include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp
🧬 Code graph analysis (1)
include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (3)
test/include/MockClient.hpp (10)
  • funcId (153-160)
  • funcId (153-153)
  • funcId (163-174)
  • funcId (163-163)
  • funcId (177-181)
  • funcId (177-177)
  • bytes (120-134)
  • bytes (120-120)
  • bytes (137-150)
  • bytes (137-137)
include/bringauto/async_function_execution/structures/Settings.hpp (4)
  • functionId (26-31)
  • functionId (26-26)
  • functionId (33-35)
  • functionId (33-33)
include/bringauto/async_function_execution/clients/AeronClient.hpp (1)
  • buffer (186-191)
🔇 Additional comments (13)
test/include/AsyncFunctionExecutorTests.hpp (4)

15-15: Good practice: explicit constructor prevents implicit conversions.

Making the single-argument constructor explicit is correct and prevents unintended implicit conversions from std::string.


55-59: LGTM: FunctionWait definition.

The new FunctionWait definition with void return type and no arguments is correctly structured for testing timeout behavior.


74-81: Producer FunctionList correctly includes all configured functions.

The updated FunctionList includes both FunctionNoArgs and FunctionWait, consistent with the per-function configurations defined above.


85-105: Test setup correctly configured for asymmetric producer/consumer testing.

The consumer configuration excludes FunctionWait while the producer includes it, allowing tests to exercise cross-executor behavior. The channel offset of 42 intentionally tests offset handling.

include/bringauto/async_function_execution/AsyncFunctionExecutor.hpp (9)

10-10: LGTM: Headers and type updates support std::expected API.

The <expected> include and migration to structures::FunctionConfigs correctly support the new error-handling design.

Also applies to: 23-23


29-29: Well-documented public API structures.

The added documentation and explicit constructors clarify the API surface. Making FunctionDefinition members public enables cleaner client code.

Also applies to: 47-87


93-120: Robust compile-time constraints on FunctionList.

The IsFunctionDefinition concept and explicit constructor ensure type safety at compile time, preventing misuse of the FunctionList API.


124-133: Clear error taxonomy with CallError enum.

The enumeration provides well-documented, type-safe error codes for all failure modes in function call flow.


214-244: Robust error handling with std::expected and per-function timeouts.

The updated callFunc() provides clear error semantics via std::expected and correctly applies per-function timeout configurations with a fallback to defaultTimeout. The callInProgress_ flag prevents concurrent calls to the same function.


253-319: Improved safety in argument deserialization.

The bounds checking at lines 300 and 303 prevents buffer overruns, and the clear error messages aid debugging. The deserialization logic correctly mirrors the encoding in appendArg().


331-344: LGTM: sendReturnMessage() error signaling.

Returning int allows callers to detect send failures. The channel offset calculation correctly uses functionId.value + channelOffset_ + MESSAGE_RETURN_CHANNEL_OFFSET.


401-440: Solid implementation of std::expected-based deserialization and state tracking.

The deserializeReturn() correctly handles void specialization, serializable types, and trivially-copyable types. The callInProgress_ map using std::atomic_bool ensures thread-safe tracking of concurrent function calls.


181-201: No issues found - MESSAGE_RETURN_CHANNEL_OFFSET is properly defined and accessible.

The constant MESSAGE_RETURN_CHANNEL_OFFSET is defined in Constants.hpp (line 12) as constexpr unsigned int MESSAGE_RETURN_CHANNEL_OFFSET = 1000; and is included transitively in AsyncFunctionExecutor.hpp through the AeronClient.hpp header. All usages in the connect() method are correct and consistent.

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