Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 4 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.25 FATAL_ERROR)
PROJECT(async-function-execution C CXX)

SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
SET(CMAKE_CXX_STANDARD 20)
SET(CMAKE_CXX_STANDARD 23)

SET(ASYNC_FUNCTION_EXECUTION_VERSION 0.1.0)
SET(ASYNC_FUNCTION_EXECUTION_VERSION 1.0.0)

FIND_PACKAGE(CMLIB
COMPONENTS CMDEF CMUTIL
Expand All @@ -14,7 +14,6 @@ FIND_PACKAGE(CMLIB
INCLUDE(GNUInstallDirs)

SET(ASYNC_FUNCTION_EXECUTION_TARGET_NAME async-function-execution)
SET(ASYNC_FUNCTION_EXECUTION_ALIAS_NAME ${PROJECT_NAME}::async-function-execution)

OPTION(BRINGAUTO_SAMPLES OFF)
OPTION(BRINGAUTO_PACKAGE "Package creation" OFF)
Expand All @@ -38,7 +37,6 @@ IF (BRINGAUTO_PACKAGE)
ENDIF ()

FIND_PACKAGE(aeron 1.48.6 REQUIRED)
FIND_PACKAGE(nlohmann_json 3.2.0 REQUIRED)

FILE(GLOB_RECURSE source_files ${CMAKE_CURRENT_LIST_DIR}/source/*)

Expand All @@ -51,19 +49,15 @@ CMDEF_ADD_LIBRARY(
VERSION ${ASYNC_FUNCTION_EXECUTION_VERSION}
)

# I guess this is not needed when we use package?
#ADD_LIBRARY(${ASYNC_FUNCTION_EXECUTION_ALIAS_NAME} ALIAS "${ASYNC_FUNCTION_EXECUTION_TARGET_NAME}-shared")

TARGET_LINK_LIBRARIES(${ASYNC_FUNCTION_EXECUTION_TARGET_NAME}-shared PUBLIC
aeron::aeron
aeron::aeron_client
aeron::aeron_driver
nlohmann_json::nlohmann_json
)

IF(BRINGAUTO_TESTS)
ENABLE_TESTING()
INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt)
ADD_SUBDIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/test)
INCLUDE(CTest)
ENDIF(BRINGAUTO_TESTS)

Expand All @@ -85,5 +79,5 @@ IF (BRINGAUTO_PACKAGE)
ENDIF ()

IF (BRINGAUTO_SAMPLES)
ADD_SUBDIRECTORY(${CMAKE_CURRENT_LIST_DIR}/examples/)
ADD_SUBDIRECTORY(${CMAKE_CURRENT_LIST_DIR}/examples)
ENDIF ()
46 changes: 30 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,38 @@ AsyncFunctionExecutor executorProducer {
Config {
.isProducer = true, // decides the mode of the executor
.defaultTimeout = std::chrono::seconds(1) // polling timeout (should only be used when producer)
.functionConfigurations = structures::FunctionConfigs { {
{ 1, { std::chrono::seconds(2) }}
} }
},
FunctionList { std::tuple{ // list of all functions
FunctionList { // list of all functions
FunctionAdd
} }
}
};
```

#### Post initialization

Before using any functions, connection needs to be established using the connect function:

```cpp
// Returns -1 on a failed connection
int rc = executorProducer.connect();
```

#### functionConfigurations

The functionConfigurations parameter accepts an unordered map representing per function configurations. Syntax:

```cpp
{
{ <function-id>, { <timeout> } }
}
```

Supported parameters:
- timeout: replaces the default timeout value for that function (in nanoseconds)

### Producer

Producer is the side calling functions and waiting for a response from the consumer. If timeout is provided in config, the function will throw if it doesn't execute in time. Example of function calling:
Expand Down Expand Up @@ -79,27 +104,16 @@ If a producer expects a return value where returned bytes are used directly, the

## Requirements

- [cmlib](https://github.com/cmakelib/cmakelib)
- [aeron](https://github.com/aeron-io/aeron)
- [cmlib](https://github.com/cmakelib/cmakelib)
- the CMLIB_DIR env value has to be set

### Aeron setup

Build and install aeron to any folder.

```bash
git clone https://github.com/aeron-io/aeron.git
cd aeron
git checkout 1.48.5
./cppbuild/cppbuild
cd cppbuild/Release
cmake --install . --prefix <absolute-path-to-install-folder>
```

## Build

```bash
mkdir -p _build && cd _build
cmake ../ -DCMLIB_DIR=<absolute-path-cmakelib> -DCMAKE_PREFIX_PATH=<path-to-aeron-install>
cmake ../
make
```

Expand Down
1 change: 0 additions & 1 deletion cmake/Dependencies.cmake
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
SET(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH FALSE)

BA_PACKAGE_LIBRARY(nlohmann-json v3.10.5 NO_DEBUG ON)
BA_PACKAGE_LIBRARY(aeron v1.48.6)

IF (BRINGAUTO_TESTS)
Expand Down
14 changes: 12 additions & 2 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.25 FATAL_ERROR)

ADD_EXECUTABLE(example main.cpp)
SET(CMAKE_CXX_STANDARD 23)

TARGET_LINK_LIBRARIES(example PUBLIC async-function-execution-shared)
IF(NOT TARGET async-function-execution-shared)
MESSAGE(FATAL_ERROR "The async-function-execution-shared target was not found. Please build the example as part of the async-function-execution-shared project.")
ENDIF()

ADD_EXECUTABLE(example_producer main_producer.cpp)
ADD_EXECUTABLE(example_consumer main_consumer.cpp)
ADD_EXECUTABLE(example_driver main_driver.cpp)

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)
22 changes: 22 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Async function execution example

This folder contains a simple example of the usage of this project. 3 executables will be built: the aeron driver, the producer and the consumer.

## Build

Examples are built as part of the main project. Don't use the CMakeLists file in the examples folder.

```bash
mkdir -p _build_example && cd _build_example
cmake ../ -DBRINGAUTO_SAMPLES=ON
make
```

## Run

```bash
# Run the executables in this order
./example_driver
./example_consumer
./example_producer
```
41 changes: 0 additions & 41 deletions examples/main.cpp

This file was deleted.

82 changes: 82 additions & 0 deletions examples/main_consumer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include <bringauto/async_function_execution/AsyncFunctionExecutor.hpp>
#include <bringauto/async_function_execution/AeronDriver.hpp>



using namespace bringauto::async_function_execution;

struct SerializableString final {
std::string value {};
SerializableString() = default;
explicit SerializableString(std::string str) : value(std::move(str)) {}

std::span<const uint8_t> serialize() const {
return std::span {reinterpret_cast<const uint8_t *>(value.data()), value.size()};
}
void deserialize(std::span<const uint8_t> bytes) {
value = std::string {reinterpret_cast<const char *>(bytes.data()), bytes.size()};
}
};

FunctionDefinition ExampleFunc1 {
FunctionId { 1 },
Return { SerializableString {} },
Arguments { int {}, SerializableString {}, float {} }
};

FunctionDefinition ExampleFunc2 {
FunctionId { 2 },
Return { SerializableString {} },
Arguments { int {}, SerializableString {} }
};

FunctionDefinition ExampleFunc3 {
FunctionId { 3 },
Return { SerializableString {} },
Arguments { int {} }
};


int main() {
AsyncFunctionExecutor executor {
Config {
.isProducer = false,
},
FunctionList { ExampleFunc1, ExampleFunc2, ExampleFunc3 },
};

if (executor.connect() != 0) {
std::cerr << "Consumer: Failed to connect to executor" << std::endl;
return 1;
}

while (true) {
auto [funcId, argBytes] = executor.pollFunction();

switch (funcId.value) {
case 1: {
auto [arg1, arg2, arg3] = executor.getFunctionArgs(ExampleFunc1, argBytes);
std::cout << "Consumer: Received Function 1 call with args (" << arg1 << ", " << arg2.value << ", " << arg3 << ")." << std::endl;
executor.sendReturnMessage(funcId, SerializableString{"Func 1 return value"});
break;
}
case 2: {
auto [arg1, arg2] = executor.getFunctionArgs(ExampleFunc2, argBytes);
std::cout << "Consumer: Received Function 2 call with args (" << arg1 << ", " << arg2.value << ")." << std::endl;
executor.sendReturnMessage(funcId, SerializableString{"Func 2 return value"});
break;
}
case 3: {
auto [arg1] = executor.getFunctionArgs(ExampleFunc3, argBytes);
std::cout << "Consumer: Received Function 3 call with args (" << arg1 << ")" << std::endl;
executor.sendReturnMessage(funcId, SerializableString{"Func 3 return value"});
break;
}
default:
std::cerr << "Consumer: Unknown function ID received: " << static_cast<int>(funcId.value) << std::endl;
throw std::runtime_error("Unknown function ID");
}
}

return 0;
}
11 changes: 11 additions & 0 deletions examples/main_driver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <bringauto/async_function_execution/AeronDriver.hpp>



using namespace bringauto::async_function_execution;

int main() {
AeronDriver driver;
driver.run();
return 0;
}
59 changes: 59 additions & 0 deletions examples/main_producer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <bringauto/async_function_execution/AsyncFunctionExecutor.hpp>


using namespace bringauto::async_function_execution;

struct SerializableString final {
std::string value {};
SerializableString() = default;
explicit SerializableString(std::string str) : value(std::move(str)) {}

std::span<const uint8_t> serialize() const {
return std::span {reinterpret_cast<const uint8_t *>(value.data()), value.size()};
}
void deserialize(std::span<const uint8_t> bytes) {
value = std::string {reinterpret_cast<const char *>(bytes.data()), bytes.size()};
}
};

FunctionDefinition ExampleFunc1 {
FunctionId { 1 },
Return { SerializableString {} },
Arguments { int {}, SerializableString {}, float {} }
};

FunctionDefinition ExampleFunc2 {
FunctionId { 2 },
Return { SerializableString {} },
Arguments { int {}, SerializableString {} }
};

FunctionDefinition ExampleFunc3 {
FunctionId { 3 },
Return { SerializableString {} },
Arguments { int {} }
};


int main() {
AsyncFunctionExecutor executor {
Config {
.isProducer = true,
.defaultTimeout = std::chrono::seconds(1)
},
FunctionList { ExampleFunc1, ExampleFunc2, ExampleFunc3 },
};

if (executor.connect() != 0) {
std::cerr << "Producer: Failed to connect to executor" << std::endl;
return 1;
}

auto result1 = executor.callFunc(ExampleFunc1, 42, "Hello", 3.14f).value();
std::cout << result1.value << std::endl;
auto result2 = executor.callFunc(ExampleFunc2, 100, "World").value();
std::cout << result2.value << std::endl;
auto result3 = executor.callFunc(ExampleFunc3, 123).value();
std::cout << result3.value << std::endl;
return 0;
}
4 changes: 2 additions & 2 deletions include/bringauto/async_function_execution/AeronDriver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ class AeronDriver {
* @brief Starts the Aeron Driver.
* This method will block until the driver is stopped or an error occurs.
*/
void run();
void run() const;

/**
* @brief Checks if the Aeron Driver is running.
* @return true if the driver is running, false otherwise.
*/
bool isRunning() const;
[[nodiscard]] bool isRunning() const;

/**
* @brief Stops the Aeron Driver.
Expand Down
Loading