Open vikman90 opened 1 week ago
The following OS have been tested and they either have a package for gcc10 or gcc10 has been built successfully.
I've conducted a trial to model a module pool in C++, aiming to create a class capable of managing references to all modules, starting them, and facilitating communication.
To achieve this, I explored two approaches:
IRunnable
interface where modules inherit and implement it.Runnable
concept, with the pool restricting parameters to this concept.Initially, conceptual constraints seemed preferable over inheritance to ensure each module remains independent of the "Module" definition. However, I noted that using a std::vector<std::any>
container — akin to the classic void*[]
in C — introduces excessive generality. This approach necessitates runtime type inference for each module access operation, thereby:
Moreover, relying on concepts does not entirely decouple modules from the Agent component, as they still need to interact via the Pool if inter-module communication is desired.
Inheritance | Concepts | |
---|---|---|
Pros | Simpler container setup. Agent independence from individual module definitions. | Module definition detached from Agent during instantiation. |
Cons | Each module inherits Module , causing vtable overhead. Dependency of Agent on each module. |
Agent dependency on each module persists. |
If my analysis holds true, IMHO, I'm inclined towards using inheritance.
The process to compile gcc10 on Fedora 40 was not straight forward, gcc 10.5 worked but not previous versions. It was necessary to compile GCC with options --enable-version-specific-runtime-libs
. Then the example code with coroutines also needed the additional option -static-libgcc
as the linker stage failed to find libgcc.
GCC14 is available out of the box nonetheless.
I have further developed a new proposal for the approach using concepts: I introduced wrappers for each module. The addModule()
function, which creates the wrappers, is a template function that uses concepts. This way, we achieve decoupling each module class from the module definition.
However, in this proposal, each module must receive a Configuration
object to establish its configuration. This object must be created by some configuration parser.
Below, I present the two updated approaches with exactly the same behavior:
In conclusion, here is the updated table of pros and cons:
Inheritance | Wrapper (concepts) | |
---|---|---|
Pros | The agent can transparently receive modules. | Modules are developed independently from the module pool. |
Cons | Dependency on the base class. Slight runtime overhead due to vtable usage. | The pool must maintain each module. Slight compilation overhead due to templates, and increased binary size. |
Tremendous thanks to @gdiazlo and @dwordcito for their collaboration in this research.
After investigating the implementation restriction of supporting multiple communications without multithreading, I explored the use of coroutines. Coroutines allow for non-blocking operations by suspending and resuming execution, which can efficiently manage asynchronous tasks. However, achieving true multitasking and supporting multiple concurrent connections may still require an underlying mechanism, such as an event loop or a thread pool, to effectively handle these connections concurrently.
Here's a test using cppcoro
(https://github.com/lewissbaker/cppcoro): https://github.com/wazuh/wazuh-agent/commit/726a2997840a88eb32fdba752a17650153e39d3a which implements a task pool using coroutines and threads.
Parent issue:
Description
Following the completion of the spike on agent-manager communication, the next step is to implement a functional client for this new protocol. This client will establish a fully functional communication and handshake with the server, transmit queue information, and leave the interface open for the addition of other modules.
Functional requirements
200
code from the server. a. Stateful. b. Stateless.Implementation restrictions
ConfigParser
.Plan
Subtasks