Skip to content
Merged
12 changes: 3 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ PROJECT(async-function-execution C CXX)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
SET(CMAKE_CXX_STANDARD 20)

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 ()
45 changes: 29 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,37 @@ 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::FuntionConfigs { {
{ 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
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 +103,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
12 changes: 10 additions & 2 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.25 FATAL_ERROR)

ADD_EXECUTABLE(example main.cpp)
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()

TARGET_LINK_LIBRARIES(example PUBLIC async-function-execution-shared)
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 CMakeList in the test 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.

80 changes: 80 additions & 0 deletions examples/main_consumer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#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;
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 },
};

executor.connect();

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, "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, "Func 2 return value");
break;
}
case 3: {
auto [arg1] = executor.getFunctionArgs(ExampleFunc3, argBytes);
SerializableString returnValue { "Result for input " + std::to_string(arg1) };
std::cout << "Consumer: Received Function 3 call with args (" << arg1 << ")" << std::endl;
executor.sendReturnMessage(funcId, "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;
}
56 changes: 56 additions & 0 deletions examples/main_producer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include <bringauto/async_function_execution/AsyncFunctionExecutor.hpp>


using namespace bringauto::async_function_execution;

struct SerializableString final {
std::string value {};
SerializableString() = default;
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 },
};

executor.connect();

auto result1 = executor.callFunc(ExampleFunc1, 42, "Hello", 3.14f);
std::cout << result1.value << std::endl;
auto result2 = executor.callFunc(ExampleFunc2, 100, "World");
std::cout << result2.value << std::endl;
auto result3 = executor.callFunc(ExampleFunc3, 123);
std::cout << result3.value << std::endl;
return 0;
}
Loading