Skip to content
Merged
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: 0 additions & 1 deletion .clangd
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
CompileFlags:
CompilationDatabase: .
BuiltinHeaders: QueryDriver

12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,19 @@ concurrency:
cancel-in-progress: true

jobs:
pre-commit:
name: 🧹 Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: sudo apt-get install -y doxygen
- uses: pre-commit/action@v3.0.1

package_and_upload_all_check:
name: 🔎 Package Validation
uses: libhal/ci/.github/workflows/package_and_upload_all.yml@5.x.y
with:
modules_support_needed: true
Expand Down
35 changes: 35 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
repos:
# General file fixes
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: end-of-file-fixer
- id: trailing-whitespace
- id: mixed-line-ending
args: [--fix=lf]

# Spell checking
- repo: https://github.com/streetsidesoftware/cspell-cli
rev: v8.6.0
hooks:
- id: cspell
files: \.(cpp|cppm|hpp|h|md)$

# C++ formatting with clang-format
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v21.1.8
hooks:
- id: clang-format
types_or: [c, c++]
files: \.(cpp|cppm|hpp|h|c)$

# Doxygen documentation checker
- repo: local
hooks:
- id: doxygen-check
name: doxygen-check
entry: doxygen
args: [docs/doxygen.conf]
language: system
pass_filenames: false
files: \.(cpp|cppm|hpp|h)$
21 changes: 21 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,30 @@ set(BUILD_UNIT_TESTS ON)
set(RUN_UNIT_TESTS ON)
set(BUILD_BENCHMARKS ON)
set(RUN_BENCHMARKS OFF)
set(USE_CLANG_TIDY ON)

project(async_context LANGUAGES CXX)

# ==============================================================================
# Find clang-tidy
# ==============================================================================

if(CMAKE_CROSSCOMPILING)
message(STATUS "Cross compiling, skipping clang-tidy")
else()
if(USE_CLANG_TIDY)
find_program(CLANG_TIDY_EXE NAMES clang-tidy)
if(CLANG_TIDY_EXE)
message(STATUS "Clang-tidy found: ${CLANG_TIDY_EXE}")
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE};--config-file=${CMAKE_CURRENT_SOURCE_DIR}/.clang-tidy;--fix")
else()
message(STATUS "Clang-tidy not found - continuing without clang-tidy")
endif()
else()
message(WARNING "Clang-tidy checks disabled")
endif()
endif()

# ==============================================================================
# Library
# ==============================================================================
Expand Down
6 changes: 4 additions & 2 deletions benchmarks/benchmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@
import async_context;

// Quick Bench: https://quick-bench.com/
// Compiler flags: -std=c++23 -O3 -DNDEBUG
// Compiler flags: -std=c++23 -O3 -D NDEBUG

// ============================================================================
// BENCHMARKS
// ============================================================================

// NOLINTBEGIN(clang-analyzer-deadcode.DeadStores)

// ----------------------------------------------------------------------------
// 1. BASELINE: Direct returns, 3 levels deep
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -477,4 +479,4 @@ static void bm_future_void_coroutine_context_resume(benchmark::State& state)
BENCHMARK(bm_future_void_coroutine_context_resume);

BENCHMARK_MAIN();
// NOLINTEND(readability-identifier-naming)
// NOLINTEND(clang-analyzer-deadcode.DeadStores)
2 changes: 1 addition & 1 deletion conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class async_context_conan(ConanFile):
description = ("Implementation of C++20 coroutines targeting embedded system by eliminating the usage of the global heap and providing a 'context' which contains a coroutine stack frame and other useful utilities for scheduling.")
topics = ("async", "coroutines", "stack", "scheduling", "scheduler")
settings = "compiler", "build_type", "os", "arch"
exports_sources = "modules/*", "benchmarks/*", "tests/*", "CMakeLists.txt", "LICENSE"
exports_sources = "modules/*", "benchmarks/*", "tests/*", "CMakeLists.txt", "LICENSE", ".clang-tidy"
package_type = "static-library"
shared = False

Expand Down
61 changes: 61 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"version": "0.2",
"language": "en",
"words": [
"cppm",
"noexcept",
"nullptr",
"constexpr",
"decltype",
"typename",
"templated",
"allocator",
"deallocate",
"deallocator",
"deallocating",
"realloc",
"inout",
"libhal",
"RAII",
"dtor",
"ctor",
"ctors",
"dtors",
"intptr",
"uintptr",
"ptrdiff",
"ifdef",
"ifndef",
"endif",
"elif",
"pragma",
"nodiscard",
"nothrow",
"initializer",
"uninitialised",
"uninitialized",
"Khalil",
"Estell",
"NOLINTNEXTLINE",
"bugprone",
"println",
"cassert",
"coro",
"chrono",
"EABI",
"NDEBUG",
"malloc",
"uptr"
],
"ignorePaths": [
"build/",
"*.json",
"*.bmi",
"compile_commands.json"
],
"enableFiletypes": [
"cpp",
"c",
"markdown"
]
}
22 changes: 22 additions & 0 deletions docs/doxygen.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Adapted from https://github.com/xtensor-stack/xtensor/blob/master/docs/Doxyfile
PROJECT_NAME = "async_context"
INPUT = ../modules
FILE_PATTERNS = *.cppm *.hpp
EXTRACT_STATIC = YES
EXCLUDE_PATTERNS = */third_party/*
HAVE_DOT = YES
OUTPUT_DIRECTORY = build
XML_OUTPUT = xml
GENERATE_LATEX = NO
GENERATE_MAN = NO
GENERATE_RTF = NO
CASE_SENSE_NAMES = NO
GENERATE_HTML = NO
GENERATE_XML = YES
RECURSIVE = YES
QUIET = YES
PREDEFINED = IN_DOXYGEN
EXCLUDE_SYMBOLS = detail
GENERATE_TREEVIEW = YES
SOURCE_BROWSER = YES
WARN_IF_UNDOCUMENTED = YES
17 changes: 13 additions & 4 deletions modules/async_context.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -1591,7 +1591,7 @@ struct final_awaiter
* continuation of the calling coroutine.
*
* @param p_completing_coroutine The coroutine handle that is completing
* @return The coroutine handle to resume next, which is a symmetryic transfer
* @return The coroutine handle to resume next, which is a symmetric transfer
* from this completing coroutine to its continuation (what called it
* originally).
*/
Expand Down Expand Up @@ -1643,15 +1643,19 @@ struct promise_return_base
void return_value(U&& p_value) noexcept
requires std::is_constructible_v<T, U&&>
{
// set future to its awaited T value
// NOLINTBEGIN(clang-analyzer-core.CallAndMessage): clang-tidy incorrectly
// assumes this pointer is uninitialized. The promise is constructed from
// the future returned by `get_return_object()`, which properly initializes
// this promise.
m_future_state->template emplace<T>(std::forward<U>(p_value));
// NOLINTEND(clang-analyzer-core.CallAndMessage)
}

/**
* @brief Pointer to the future state that should be set at future<T>
* construction.
*/
future_state<T>* m_future_state;
future_state<T>* m_future_state = nullptr;
};

/**
Expand All @@ -1667,14 +1671,19 @@ struct promise_return_base<void>
*/
void return_void() noexcept
{
// NOLINTBEGIN(clang-analyzer-core.CallAndMessage): clang-tidy incorrectly
// assumes this pointer is uninitialized. The promise is constructed from
// the future returned by `get_return_object()`, which properly initializes
// this promise.
*m_future_state = std::monostate{};
// NOLINTEND(clang-analyzer-core.CallAndMessage)
}

/**
* @brief Pointer to the future state that should be set at future<void>
* construction.
*/
future_state<void>* m_future_state;
future_state<void>* m_future_state = nullptr;
};

/**
Expand Down
30 changes: 15 additions & 15 deletions tests/cancel.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,22 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
};

{
expect(that % count == counter_pair{ 0, 0 });
expect(that % count == counter_pair{ .constructed = 0, .destructed = 0 });
expect(that % ends_reached == 0);

auto future = c(ctx);

expect(that % count == counter_pair{ 0, 0 });
expect(that % count == counter_pair{ .constructed = 0, .destructed = 0 });
expect(that % ends_reached == 0);

future.resume();

expect(that % count == counter_pair{ 3, 0 });
expect(that % count == counter_pair{ .constructed = 3, .destructed = 0 });
expect(that % ends_reached == 0);
expect(that % 0 < ctx.memory_used());
} // destroy future

expect(that % count == counter_pair{ 3, 3 });
expect(that % count == counter_pair{ .constructed = 3, .destructed = 3 });
expect(that % ends_reached == 0);
expect(that % 0 == ctx.memory_used());
};
Expand Down Expand Up @@ -104,23 +104,23 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
co_return;
};

expect(that % count == counter_pair{ 0, 0 });
expect(that % count == counter_pair{ .constructed = 0, .destructed = 0 });
expect(that % ends_reached == 0);

auto future = c(ctx);

expect(that % count == counter_pair{ 0, 0 });
expect(that % count == counter_pair{ .constructed = 0, .destructed = 0 });
expect(that % ends_reached == 0);

future.resume();

expect(that % count == counter_pair{ 3, 0 });
expect(that % count == counter_pair{ .constructed = 3, .destructed = 0 });
expect(that % ends_reached == 0);
expect(that % 0 < ctx.memory_used());

future.cancel();

expect(that % count == counter_pair{ 3, 3 });
expect(that % count == counter_pair{ .constructed = 3, .destructed = 3 });
expect(that % ends_reached == 0);
expect(that % 0 == ctx.memory_used());
};
Expand Down Expand Up @@ -164,25 +164,25 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
co_return;
};

expect(that % count == counter_pair{ 0, 0 });
expect(that % count == counter_pair{ .constructed = 0, .destructed = 0 });
expect(that % ends_reached == 0);

auto future = c(ctx);

expect(that % count == counter_pair{ 0, 0 });
expect(that % count == counter_pair{ .constructed = 0, .destructed = 0 });
expect(that % ends_reached == 0);

future.resume();

expect(that % count == counter_pair{ 3, 0 });
expect(that % count == counter_pair{ .constructed = 3, .destructed = 0 });
expect(that % ends_reached == 0);
expect(that % 0 < ctx.memory_used());
expect(that % false == future.has_value());
expect(that % false == future.done());

ctx.cancel();

expect(that % count == counter_pair{ 3, 3 });
expect(that % count == counter_pair{ .constructed = 3, .destructed = 3 });
expect(that % ends_reached == 0);
expect(that % 0 == ctx.memory_used());
expect(that % false == future.has_value());
Expand Down Expand Up @@ -232,12 +232,12 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
co_return;
};

expect(that % count == counter_pair{ 0, 0 });
expect(that % count == counter_pair{ .constructed = 0, .destructed = 0 });
expect(that % ends_reached == 0);

auto future = c(ctx);

expect(that % count == counter_pair{ 0, 0 });
expect(that % count == counter_pair{ .constructed = 0, .destructed = 0 });
expect(that % ends_reached == 0);

std::println("Resume until future reaches suspension @ coroutine A");
Expand All @@ -247,7 +247,7 @@ boost::ut::suite<"cancellation_tests"> cancellation_tests = []() {
<< "runtime_error exception was not thrown!";
expect(that % future.done());
expect(that % not future.has_value());
expect(that % count == counter_pair{ 3, 3 });
expect(that % count == counter_pair{ .constructed = 3, .destructed = 3 });
expect(that % ends_reached == 0);
expect(that % 0 == ctx.memory_used());
};
Expand Down
2 changes: 2 additions & 0 deletions tests/util.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export module test_utils;

import async_context;

// NOLINTBEGIN(bugprone-exception-escape)
export namespace async {
std::ostream& operator<<(std::ostream& out, blocked_by b)
{
Expand Down Expand Up @@ -148,3 +149,4 @@ export {
std::string_view m_label;
};
}
// NOLINTEND(bugprone-exception-escape)