Skip to content

Commit

Permalink
Finishing touches for v0.1.0 release
Browse files Browse the repository at this point in the history
  • Loading branch information
Oipo committed Nov 5, 2022
1 parent af105a2 commit 5f628b3
Show file tree
Hide file tree
Showing 27 changed files with 165 additions and 95 deletions.
71 changes: 5 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,69 +44,9 @@ More examples can be found in the [examples directory](examples).
* Baremetal, might change if someone puts in the effort to modify Ichor to work with freestanding implementations of C++20
* Far out future plans for any RTOS that supports C++20 such as VxWorks Wind River, FreeRTOS

## Dependencies

### Ubuntu 20.04:

```
sudo apt install libisl-dev libmpfrc++-dev libmpc-dev libgmp-dev build-essential cmake g++
wget http://mirror.koddos.net/gcc/releases/gcc-11.3.0/gcc-11.3.0.tar.xz
tar xf gcc-11.3.0.tar.xz
mkdir gcc-build
cd gcc-build
../gcc-11.3.0/configure --prefix=/opt/gcc-11.3 --enable-languages=c,c++ --disable-multilib
make -j$(nproc)
sudo make install
```

Then with cmake, use
```
CXX=/opt/gcc-11.3.0/bin/g++ cmake $PATH_TO_ICHOR_SOURCE
```

### Ubuntu 22.04:

```
sudo apt install g++ build-essential cmake
```

#### Optional Features
Some features are behind feature flags and have their own dependencies.

If using etcd:
```
sudo apt install libgrpc++-dev libprotobuf-dev
```

If using the Boost.BEAST (recommended boost 1.70 or newer):

Ubuntu 20.04:
```
sudo apt install libboost1.71-all-dev libssl-dev
```

Ubuntu 22.04:
```
sudo apt install libboost1.74-all-dev libssl-dev
```

#### Windows

Tried with MSVC 19.33, but it seemed like coroutines and concepts are not fully implemented yet.

## Building

Linux:
```
git clone https://github.com/volt-software/Ichor.git
mkdir Ichor/build
cd Ichor/build
cmake ..
make -j$(nproc)
make test
../bin/ichor_minimal_example
(ctrl + c to quit)
```
For build instructions and required dependencies, please see [GettingStarted](docs/GettingStarted.md).

## Documentation

Expand All @@ -133,29 +73,28 @@ The framework provides several core features and optional services behind cmake
* Dependency Injection
* Service lifecycle management (sort of like OSGi-lite services)
* data race free communication between event loops
* Http server/client

Optional services:
* Websocket service through Boost.BEAST
* HTTP client and server services through Boost.BEAST
* Spdlog logging service
* TCP communication service
* RapidJson serialization services
* JSON serialization services examples
* Timer service
* Partial etcd service
* Partial etcd service (may be broken, unused for a while)

# Roadmap

* EDF scheduling / WCET measurements
* CMake stuff to include ichor library from external project
* expand/re-do etcd support, currently only simply put/get supported
* Pubsub interfaces
* Kafka? Pulsar? Ecal?
* Shell Commands
* Shell Commands / REPL
* Tracing interface
* Opentracing? Jaeger?
* Docker integration/compilation
* "Remote" services, where services are either in a different thread or a different machine
* Code generator for remote communication boilerplate
* ...

# Benchmarks
Expand Down
1 change: 1 addition & 0 deletions benchmarks/coroutine_benchmark/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

int main(int argc, char *argv[]) {
std::locale::global(std::locale("en_US.UTF-8"));
std::ios::sync_with_stdio(false);

{
auto start = std::chrono::steady_clock::now();
Expand Down
1 change: 1 addition & 0 deletions benchmarks/event_benchmark/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

int main(int argc, char *argv[]) {
std::locale::global(std::locale("en_US.UTF-8"));
std::ios::sync_with_stdio(false);

{
auto start = std::chrono::steady_clock::now();
Expand Down
1 change: 1 addition & 0 deletions benchmarks/serializer_benchmark/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

int main(int argc, char *argv[]) {
std::locale::global(std::locale("en_US.UTF-8"));
std::ios::sync_with_stdio(false);

{
auto start = std::chrono::steady_clock::now();
Expand Down
1 change: 1 addition & 0 deletions benchmarks/start_benchmark/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

int main(int argc, char *argv[]) {
std::locale::global(std::locale("en_US.UTF-8"));
std::ios::sync_with_stdio(false);

{
auto start = std::chrono::steady_clock::now();
Expand Down
1 change: 1 addition & 0 deletions benchmarks/start_stop_benchmark/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

int main(int argc, char *argv[]) {
std::locale::global(std::locale("en_US.UTF-8"));
std::ios::sync_with_stdio(false);

{
auto start = std::chrono::steady_clock::now();
Expand Down
14 changes: 9 additions & 5 deletions docs/Downsides.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Downsides

Any and all tech choices come with pros and cons. Here are some for Ichor:
Any and all tech choices come with pros and cons. Anyone who only tells you about the benefits of a piece of technology is withholding important information. Here are some for Ichor:

* Inability to have custom defined constructors
* Using co_await messes up lifetime management
* Inability to have custom defined constructors (could maybe be improved?)
* Using co_await messes up lifetime management. E.g. using a timer, using co_await inside the handler and then changing the timer after the await may result in UB.
* Using anything getManager() bases in the no-arguments service constructor segfaults, as it is not available at that point.
* Creating a service that has its own thread and making interaction between Ichor and that thread thread-safe is difficult.
* Debugging issues in (core) Ichor is painful. Going outside of the existing best practices for Ichor will lead to painful debugging.
* Creating a service that has its own thread and making interaction between Ichor and that thread thread-safe is difficult. It took a couple of iterations to get a safe http implementation.
* Debugging issues in Ichor itself is painful. Going outside of the existing best practices for Ichor will lead to painful debugging.
* Ichor relies on bleeding edge C++, which results in having to deal with compiler bugs.
* Ichor uses a lot of memory allocations. Mimalloc reduces the impact, but the most egregious example is Ichor's boost implementation: abstracting away http requests/responses requires a lot of memcpy/memset/moves of memory that would otherwise not be necessary.

Most of these can worked around, institutional knowledge can be built or can be attributed to downsides of C++ itself. Use this knowledge to make a decision yourself: is Ichor worth it for your use-case?
49 changes: 46 additions & 3 deletions docs/Fundamentals.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ Ichor is a combination of three components:
* Service Lifecycle Management (SLM)
* Event Queue (EQ)

Using the latest C++20 features:
* Coroutines
* Concepts

## Dependency Injection (DI)

Ichor's DI is run-time. There's some compile time stuff to determine which interfaces are requested, but resolving and registering happens at run-time.
The main feature Ichor aims for is run-time interception of dependency requests and creating a new dependency instance specifically for that request.
One of the use-cases is to easily setup factories separated per function domain. E.g. to create two workflows which share common code but separate data.
Another would be to dynamically load .so or .dll files which expose common Ichor dependencies, to be easily shared accross multiple projects.

### Requesting Dependencies

Expand All @@ -24,7 +29,7 @@ struct DependencyService final : public Service<DependencyService> {
}
~DependencyService() final = default;

void addDependencyInstance(ISomeDependency *, IService *) {}
void addDependencyInstance(ISomeDependency * /* Injected service */, IService * /* service interface for injected service, e.g. service id and properties */) {}
void removeDependencyInstance(ISomeDependency *, IService *) {}
void addDependencyInstance(ISomeOptionalDependency *, IService *) {}
void removeDependencyInstance(ISomeOptionalDependency *, IService *) {}
Expand All @@ -38,7 +43,7 @@ It is only when also taking Service Lifecyce Management and the thread safety gu
#### Boost.DI
Boost.DI is a purely compile-time DI framework. This enables it to have no run-time overhead, but doesn't allow creating a new instance per dependency request.
Boost.DI is a purely compile-time DI framework. This enables it to have no run-time overhead, but doesn't allow creating a new instance per dependency request. It requires all possible interfaces and how to instantiate them to be known at compile time.
#### Google Fruit
Expand Down Expand Up @@ -69,4 +74,42 @@ Ichor, at least on Linux, ensures that the highest priority thread trying to ins
### Real-time scheduling
When combined with a real-time kernel, each thread can be run with a real-time guarantee. Although Ichor provides the concept of priorities, these are discouraged in real-time usage. Most real-time tasks run in a linear fashion so that doesn't create issues.
When combined with a real-time kernel, each thread can be run with a real-time guarantee. Although Ichor provides the concept of priorities, these are discouraged in real-time usage. Most real-time tasks run in a linear fashion so that doesn't create issues.
### Supported Out-Of-The-Box
Ichor provides a multimap-based priority queue as well as an [sdevent](https://www.freedesktop.org/software/systemd/man/sd-event.html) implementation out of the box. Custom ones can be made to suit your needs.
The sdevent implementation is a showcase on how to implement Ichor on top of your existing event queue.
## C++20
### Coroutines
Ichor provides a co_yield and co_await capable generator for use in conjunction with its event queue integration. An example:
```c++
AsyncManualResetEvent evt;
dm.pushEvent<RunFunctionEvent>(0, [&](DependencyManager& mng) -> AsyncGenerator<void> {
co_await evt;
co_return;
});
// ... Sometime Later ...
dm.pushEvent<RunFunctionEvent>(0, [&](DependencyManager& mng) -> AsyncGenerator<void> {
evt.set();
co_return;
});
```

What happens under the hood is that a `RunFunctionEvent` gets inserted into the queue and executed. Upon encountering a `co_await`, a coroutine handle gets created and stored in Ichor.

Sometime later, another `RunFunctionEvent` is inserted and executed, which sets the Async event, immediately continuing the execution of the previous coroutine, after which its execution is finished.

Big thanks for Lewis Baker's [cppcoro](https://github.com/lewissbaker/cppcoro), which Ichor borrowed heavily from.

For more details see the `sendTestRequest` function in `UsingHttpService.h` in the [http example](../examples/http_example)

### Concepts

For registering dependencies, trackers, http routes and more, Ichor uses templated with concepts to ensure the right function signatures are present. These then get stored in Ichor using type-erasure through `std::function`.
Loading

0 comments on commit 5f628b3

Please sign in to comment.