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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ __pycache__/
.cproject
.pydevproject
.gdbinit
.clangd
.vscode/
**/.idea/*
!**/.idea/dictionaries
Expand Down
41 changes: 41 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ cmake_minimum_required(VERSION 3.12)
project(olga_scheduler_tests C CXX)
enable_testing()

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CTEST_OUTPUT_ON_FAILURE ON)

# Use -DNO_STATIC_ANALYSIS=1 to suppress static analysis. If not suppressed, the tools used here shall be available.
Expand Down Expand Up @@ -105,3 +106,43 @@ set_target_properties(olga_scheduler_c_demo PROPERTIES C_STANDARD 99 C_STANDARD_
target_include_directories(olga_scheduler_c_demo PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/olga_scheduler)
target_include_directories(olga_scheduler_c_demo SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib/cavl)
target_compile_options(olga_scheduler_c_demo PRIVATE -Wall -Wextra -Werror -pedantic)

add_executable(basic_example_c ${CMAKE_CURRENT_SOURCE_DIR}/examples/basic_example.c)
set_target_properties(basic_example_c PROPERTIES C_STANDARD 99 C_STANDARD_REQUIRED YES C_EXTENSIONS OFF)
target_include_directories(basic_example_c PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/olga_scheduler)
target_include_directories(basic_example_c SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib/cavl)
target_compile_definitions(basic_example_c PRIVATE _POSIX_C_SOURCE=200809L)
target_compile_options(basic_example_c PRIVATE -Wall -Wextra -Werror -pedantic)
add_custom_target(run_basic_example_c
COMMAND $<TARGET_FILE:basic_example_c>
DEPENDS basic_example_c
USES_TERMINAL
)

add_executable(basic_example_cpp ${CMAKE_CURRENT_SOURCE_DIR}/examples/basic_example.cpp)
set_target_properties(basic_example_cpp PROPERTIES CXX_STANDARD 20)
target_include_directories(basic_example_cpp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/olga_scheduler)
target_include_directories(basic_example_cpp SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib/cavl)
target_compile_options(basic_example_cpp PRIVATE -Wall -Wextra -Werror -pedantic)
add_custom_target(run_basic_example_cpp
COMMAND $<TARGET_FILE:basic_example_cpp>
DEPENDS basic_example_cpp
USES_TERMINAL
)

add_executable(task_trace ${CMAKE_CURRENT_SOURCE_DIR}/examples/task_trace.cpp)
set_target_properties(task_trace PROPERTIES CXX_STANDARD 20)
target_include_directories(task_trace PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/olga_scheduler)
target_include_directories(task_trace SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib/cavl)
target_compile_options(task_trace PRIVATE -Wall -Wextra -Werror -pedantic)
add_custom_target(run_task_trace
COMMAND $<TARGET_FILE:task_trace>
DEPENDS task_trace
USES_TERMINAL
)

add_custom_target(examples)
add_dependencies(examples basic_example_c basic_example_cpp)
add_test(NAME BuildExamples
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target examples
)
47 changes: 47 additions & 0 deletions examples/basic_example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "olga_scheduler.h"

#include <inttypes.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>

static int64_t get_microseconds(olga_t* sched)
{
(void)sched;
struct timespec ts;
(void)clock_gettime(CLOCK_MONOTONIC, &ts);
return ((int64_t)ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
}

static void handler(olga_t* sched, olga_event_t* event, int64_t now)
{
uint64_t* counter = (uint64_t*)event->user;
++*counter;
printf("counter=%" PRIu64 " now=%" PRId64 "\n", *counter, now);
// Keep events triggering exactly at 1 Hz.
olga_defer(sched, event->deadline + 1000000, event->user, handler, event);
}

int main(void)
{
olga_t sched;
olga_init(&sched, NULL, get_microseconds);

uint64_t counter = 0;
olga_event_t event = OLGA_EVENT_INIT;
olga_defer(&sched, sched.now(&sched) + 1000000, &counter, handler, &event);

for (;;) {
olga_spin_result_t spin_result = olga_spin(&sched);
(void)spin_result; // Optional performance information here.
const struct timespec delay = { .tv_sec = 0, .tv_nsec = 1000 * 1000 };
(void)nanosleep(&delay, NULL); // Do something else here: IO multiplexing,
// update scheduler stats, etc.
if (counter > 10) {
olga_cancel(&sched, &event);
puts("Event canceled");
break;
}
}
return 0;
}
34 changes: 34 additions & 0 deletions examples/basic_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "olga_scheduler.hpp"

#include <chrono>
#include <cstdint>
#include <iostream>
#include <thread>

namespace {

constexpr std::uint64_t CounterLimit = 10;

} // namespace

int main()
{
using namespace std::chrono_literals;

olga_scheduler::EventLoop<std::chrono::steady_clock> loop;
std::uint64_t counter = 0;

auto evt = loop.repeat(1s, [&](const auto& arg) {
++counter;
std::cout << "counter=" << counter << " now=" << arg.approx_now.time_since_epoch().count() << '\n';
if (counter > CounterLimit) {
arg.event.cancel();
}
});

while (!loop.isEmpty()) {
(void)loop.spin();
std::this_thread::sleep_for(1ms); // Do something else here.
}
return 0;
}
83 changes: 83 additions & 0 deletions examples/task_trace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#include "olga_scheduler.hpp"

#include <chrono>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <string_view>
#include <thread>

namespace {

using Clock = std::chrono::steady_clock;

constexpr std::uint64_t TaskRunCount = 6;
constexpr auto TaskPeriod = std::chrono::milliseconds{ 200 };
constexpr auto ShortWork = std::chrono::milliseconds{ 25 };
constexpr auto LongWork = std::chrono::milliseconds{ 50 };
constexpr int TaskColumn = 14;
constexpr int CountColumn = 12;
constexpr int DurationColumn = 14;

template <typename Duration>
auto toMicroseconds(const Duration duration)
{
return std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
}

template <typename Trace>
void printTrace(const Trace& trace)
{
std::cout << "\ntask_trace\n";
std::cout << std::left << std::setw(TaskColumn) << "task" << std::right << std::setw(CountColumn) << "executions"
<< std::setw(DurationColumn) << "total_us" << std::setw(DurationColumn) << "shortest_us"
<< std::setw(DurationColumn) << "longest_us" << std::setw(DurationColumn) << "average_us" << '\n';
std::cout << std::string_view{ "----------------------------------------------"
"--------------------------------------" }
<< '\n';
for (const auto& task : trace) {
const auto name = task.name.empty() ? std::string_view{ "<unnamed>" } : task.name;
std::cout << std::left << std::setw(TaskColumn) << name << std::right << std::setw(CountColumn)
<< task.execution_count << std::setw(DurationColumn) << toMicroseconds(task.total_duration)
<< std::setw(DurationColumn) << toMicroseconds(task.shortest_duration) << std::setw(DurationColumn)
<< toMicroseconds(task.longest_duration) << std::setw(DurationColumn)
<< toMicroseconds(task.average_duration) << '\n';
}
}

} // namespace

int main()
{
olga_scheduler::EventLoop<Clock, true, 3U> loop;

std::uint64_t sensor_cycles = 0;
std::uint64_t controller_cycles = 0;
std::uint64_t estimator_cycles = 0;

const auto make_task = [](const std::string_view name, const Clock::duration work, std::uint64_t& cycles) {
return [name, work, &cycles](const auto& arg) {
++cycles;
std::cout << "task=" << name << " cycle=" << cycles << " now=" << arg.approx_now.time_since_epoch().count()
<< '\n';
std::this_thread::sleep_for(work);
if (cycles >= TaskRunCount) {
arg.event.cancel();
}
};
};

auto sensor_task = loop.repeat("sensor", TaskPeriod, make_task("sensor", ShortWork, sensor_cycles));
auto controller_task = loop.repeat("controller", TaskPeriod, make_task("controller", ShortWork, controller_cycles));
auto estimator_task = loop.repeat("estimator", TaskPeriod, make_task("estimator", LongWork, estimator_cycles));

while (!loop.isEmpty()) {
const auto spin_result = loop.spin();
if (!loop.isEmpty()) {
std::this_thread::sleep_until(spin_result.next_deadline);
}
}

printTrace(loop.getTrace());
return 0;
}
Loading
Loading