Skip to content

Conversation

MarioIvancik
Copy link
Member

@MarioIvancik MarioIvancik commented Oct 1, 2025

Summary by CodeRabbit

  • New Features

    • Per-function timeout configuration and timeout-aware messaging/idle behavior.
    • New runnable examples: driver, producer, consumer.
  • Breaking Changes

    • connect() now returns a status code and must be invoked before use.
    • Configuration moved from JSON string to typed function configurations.
    • Message-waiting API accepts a timeout parameter.
  • Documentation

    • Updated setup, build, and example run instructions.
  • Chores

    • Removed JSON dependency, streamlined build, bumped project version to 1.0.0.
  • Tests

    • Updated tests for new config and connection semantics.

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

coderabbitai bot commented Oct 1, 2025

Walkthrough

Introduces typed per-function timeout configs and FunctionList refinements; makes AsyncFunctionExecutor connect() return int; adds timeout-aware idle strategy and client wait APIs; updates AeronClient error handling; removes nlohmann_json; splits examples into producer/consumer/driver; and updates CMake/test wiring and docs.

Changes

Cohort / File(s) Summary
Top-level build
CMakeLists.txt, cmake/Dependencies.cmake, examples/CMakeLists.txt, test/CMakeLists.txt
Version bumped to 1.0.0; removed nlohmann_json BA_PACKAGE declaration and public link; switched tests to add_subdirectory and added target presence guards; split examples into multiple executables and added target guards.
Documentation & READMEs
README.md, examples/README.md, test/README.md
Documented functionConfigurations (typed map), noted requirement to call executorProducer.connect(), updated build instructions (removed Aeron CM options), and added example build/run steps.
Core API / Models
include/.../AsyncFunctionExecutor.hpp, include/.../structures/Settings.hpp, include/.../Constants.hpp
Replaced string JSON configs with typed structures::FunctionConfigs; added FunctionConfigs type; updated Config/Settings constructors; enhanced FunctionDefinition/Return/Arguments/FunctionList (explicit ctors, concepts, deduction guide); AsyncFunctionExecutor ctor accepts optional client and connect() now returns int; per-function timeout lookup implemented; new constants added.
Idle strategy
include/.../TimeoutIdleStrategy.hpp, source/.../TimeoutIdleStrategy.cpp
Moved IdleState private; replaced parameterless idle() with idle(workCount, timeout) and reset(); implemented timeout-aware spin/yield/park state machine and timeout return (-1).
Client layer
include/.../clients/ClientInterface.hpp, include/.../clients/AeronClient.hpp
waitForMessage gained a std::chrono::nanoseconds timeout param; AeronClient connect/waits wrapped with try/catch and per-channel timeout checks, returns -1 on failures/timeouts; sendMessage and waitForMessage include channel existence checks and proper returns on timeout.
Examples
examples/main_producer.cpp, examples/main_consumer.cpp, examples/main_driver.cpp, removed examples/main.cpp
Removed old monolithic example; added separate producer, consumer, and driver example programs demonstrating new connect(), per-function timeouts, polling, and Aeron driver usage.
Tests & test utilities
test/include/AsyncFunctionExecutorTests.hpp, test/include/MockClient.hpp, test/source/AsyncFunctionExecutorTests.cpp, test/integration_tests/*
Tests migrated from JSON-string configs to structures::FunctionConfigs; FunctionList initializer style updated; MockClient marked final and waitForMessage updated with timeout param; added ArgumentTooLarge test; integration tests check connect() return and updated call/use sites.
Aeron driver
include/.../AeronDriver.hpp, source/.../AeronDriver.cpp
run() made const; isRunning() marked [[nodiscard]]; implementation updated to use C++ signal header and nullptr in termination hook.

Sequence Diagram(s)

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

  rect rgb(250,250,255)
    note right of App: Initialization & connect
    App->>Exec: new AsyncFunctionExecutor(Config{..., functionConfigs}, FunctionList, [client])
    App->>Exec: connect()
    Exec->>Client: connect(...) 
    Client-->>Exec: int status
  end

  rect rgb(245,255,245)
    note right of App: Producer flow (per-fn timeout)
    App->>Exec: callFunc(FunctionDef fn, args...)
    Exec->>Client: sendMessage(callChannel, encodedArgs)
    Exec->>Client: waitForMessage(returnChannel, timeout = fn.timeout || default)
    alt message received
      Client-->>Exec: span(bytes)
      Exec-->>App: return decoded result
    else timeout / empty
      Client-->>Exec: empty span
      Exec-->>App: throws / error
    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 and continue
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • First release #2 — Overlapping code-level changes to AsyncFunctionExecutor, TimeoutIdleStrategy, ClientInterface/AeronClient, Constants, Settings/FunctionConfigs, examples, and CMake wiring; likely strongly related or complementary.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “First proposed release” is too generic and does not convey the specific version bump or the primary change in the PR, making it unclear for someone scanning the history. Consider renaming the pull request to explicitly mention the version number and main change, for example “Release v1.0.0” or “Bump project version to 1.0.0”.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • 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.

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.

1 participant