Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
f74f1f7
Add expect overload for std::optional
RuthgerD May 17, 2025
01e99be
Make readClientRequest fallible + simple read/write wrappers
RuthgerD May 17, 2025
aa35e4a
wip: coroutines
RuthgerD Jun 18, 2025
130f3bf
non working stop
RuthgerD Sep 27, 2025
1a559c1
Lets start stacking commits
RuthgerD Sep 28, 2025
58ec752
Almost got a good pattern
RuthgerD Oct 3, 2025
32de786
More progress yay, just need Task to work now
RuthgerD Oct 4, 2025
b479449
Add basic spawn code, needs more
RuthgerD Oct 6, 2025
7261398
slowly getting there..
RuthgerD Oct 9, 2025
67f1de7
Simplify
RuthgerD Mar 6, 2026
63c1c01
Task spawning does not need to be awaited now
RuthgerD Mar 6, 2026
74e3609
Implement block_on
RuthgerD Mar 6, 2026
4e9a4da
Add runtime
RuthgerD Mar 6, 2026
2c5c782
Fix runtime
RuthgerD Mar 6, 2026
568c8f6
Fix block_on to not use task
RuthgerD Mar 6, 2026
e26fc4e
Catch stopped exception in run()
RuthgerD Mar 6, 2026
e54381b
Add void support
RuthgerD Mar 7, 2026
4e40369
final
RuthgerD Mar 7, 2026
b0df815
Add asendmsg
RuthgerD Mar 10, 2026
dcb7d8f
Don't make PolledFd::create async
RuthgerD Mar 10, 2026
14322f9
clang tidy/format
RuthgerD Mar 10, 2026
1d7d952
Remove todos
RuthgerD Mar 10, 2026
6a4c8b1
Update to clang 21
RuthgerD Mar 10, 2026
2ec5204
gcc :(
RuthgerD Mar 10, 2026
53770b4
Pin clang-format-21?
RuthgerD Mar 10, 2026
30fa5f9
try again
RuthgerD Mar 10, 2026
4ff33c3
Is 22 available?
RuthgerD Mar 10, 2026
047452d
Headers
RuthgerD Mar 10, 2026
aa65de0
don't use move_only_function
RuthgerD Mar 10, 2026
a14f965
Move back to llvm 21
RuthgerD Mar 10, 2026
134fddd
Fix deadlock and yield
RuthgerD Mar 13, 2026
7d9a509
Review comments
RuthgerD Mar 15, 2026
8c80d6b
Remove exposition only yield
RuthgerD Mar 15, 2026
5f4fabd
Copyright + EOF
RuthgerD Mar 15, 2026
89efa49
Revert back to cxx17 on public interface
RuthgerD Mar 15, 2026
41b6d45
More linting
RuthgerD Mar 15, 2026
07b4794
Guard against interruptions
RuthgerD Mar 15, 2026
0547c0c
More interruption guards
RuthgerD Mar 15, 2026
d1550e4
Fix cmake
RuthgerD Mar 15, 2026
5bcd5cc
Remove void comment
RuthgerD Mar 15, 2026
073f136
Cleanup registration callback
RuthgerD Mar 15, 2026
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
10 changes: 5 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ jobs:
ld: MOLD
ldflags:
- name: Clang/LLVM
cc: clang-20
cxx: clang++-20
cc: clang-21
cxx: clang++-21
cxxflags: -stdlib=libc++
ld: LLD
ldflags: -lllvmlibc
Expand All @@ -28,8 +28,8 @@ jobs:
if: matrix.toolchain.name == 'Clang/LLVM'
run: |
sudo wget -O /etc/apt/trusted.gpg.d/apt.llvm.org.asc https://apt.llvm.org/llvm-snapshot.gpg.key
sudo add-apt-repository -y "deb http://apt.llvm.org/$(lsb_release -sc)/ llvm-toolchain-$(lsb_release -sc)-20 main"
sudo apt-get update && sudo apt-get install -y --no-install-recommends clang-20 lld-20 libllvmlibc-20-dev libc++-20-dev libc++abi-20-dev clang-tidy-20
sudo add-apt-repository -y "deb http://apt.llvm.org/$(lsb_release -sc)/ llvm-toolchain-$(lsb_release -sc)-21 main"
sudo apt-get update && sudo apt-get install -y --no-install-recommends clang-21 lld-21 libllvmlibc-21-dev libc++-21-dev libc++abi-21-dev clang-tidy-21
- name: Check deps
run: |
cmake --version
Expand All @@ -56,5 +56,5 @@ jobs:
- name: Lint
if: matrix.toolchain.name == 'Clang/LLVM'
env:
CLANG_TIDY: clang-tidy-20
CLANG_TIDY: clang-tidy-21
run: git clang-tidy
2 changes: 1 addition & 1 deletion .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Fetch ClangFormat
run: |
sudo wget -O /etc/apt/trusted.gpg.d/apt.llvm.org.asc https://apt.llvm.org/llvm-snapshot.gpg.key
sudo add-apt-repository -y "deb http://apt.llvm.org/$(lsb_release -sc)/ llvm-toolchain-$(lsb_release -sc) main"
sudo add-apt-repository -y "deb http://apt.llvm.org/$(lsb_release -sc)/ llvm-toolchain-$(lsb_release -sc)-21 main"
sudo apt-get update && sudo apt-get install -y --no-install-recommends clang-format
- run: git config include.path ../.gitconfig
- run: git clang-format
13 changes: 12 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,27 @@ target_sources (
src/io/addr.hxx
src/io/addr.cxx
src/io/endian.hxx
src/io/polled_fd.hxx
src/io/reactor.hxx
src/io/reactor.cxx
src/io/context.hxx
src/co/scheduler.hxx
src/co/coroutine.hxx
src/co/task.hxx
src/co/received.hxx
src/fastipc.cxx
src/visitor.hxx
src/channel.hxx
)

if (NOT DEFINED CMAKE_CXX_CLANG_TIDY OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
target_precompile_headers (fastipc PUBLIC include/fastipc.hxx)
endif ()

target_compile_features (fastipc INTERFACE cxx_std_17)
target_compile_features (fastipc PRIVATE cxx_std_23)
target_compile_options (fastipc PRIVATE ${FASTIPC_COMPILE_OPTIONS} -Wno-zero-length-array)
target_include_directories (fastipc PRIVATE src)

add_library (tower OBJECT)
target_include_directories (tower PUBLIC src)
Expand All @@ -48,7 +59,7 @@ target_link_libraries (tower PRIVATE fastipc)
add_executable (fastipcd)
target_sources (fastipcd PRIVATE src/main.cxx)
target_compile_options (fastipcd PRIVATE ${FASTIPC_COMPILE_OPTIONS})
target_link_libraries (fastipcd PRIVATE tower)
target_link_libraries (fastipcd PRIVATE tower fastipc)

enable_testing ()
add_subdirectory (test)
306 changes: 306 additions & 0 deletions src/co/coroutine.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
/*
* coroutine.hxx
* Copyright 2025 ItJustWorksTM
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#pragma once

#include <coroutine>
#include <exception>
#include <type_traits>
#include <utility>
#include <variant>
#include "co/received.hxx"

namespace fastipc::co {

template <class T>
class Receiver {
public:
Receiver() = default;

Receiver(Receiver&&) noexcept = default;
Receiver& operator=(Receiver&&) noexcept = default;

Receiver(const Receiver&) noexcept = default;
Receiver& operator=(const Receiver&) noexcept = default;

virtual ~Receiver() = default;

virtual void set_value(T value) = 0;
virtual void set_exception(std::exception_ptr exc) = 0;
};

template <>
class Receiver<void> {
public:
Receiver() = default;

Receiver(Receiver&&) noexcept = default;
Receiver& operator=(Receiver&&) noexcept = default;

Receiver(const Receiver&) noexcept = default;
Receiver& operator=(const Receiver&) noexcept = default;

virtual ~Receiver() = default;

virtual void set_value() = 0;
virtual void set_exception(std::exception_ptr exc) = 0;
};

template <class A, class P>
struct AwaitedBy {
A value;
};

template <class T>
class PromiseState;

template <class T>
class [[nodiscard]] Promise final {
public:
explicit Promise(std::coroutine_handle<PromiseState<T>> handle) : m_handle{std::move(handle)} {}

Promise(Promise&) noexcept = delete;
Promise& operator=(Promise&) noexcept = delete;

Promise(Promise&& it) noexcept : m_handle{std::exchange(it.m_handle, {})} {}
Promise& operator=(Promise&& rhs) noexcept {
auto other = Promise{std::move(rhs)};

std::swap(m_handle, other.m_handle);

return *this;
}

~Promise() noexcept {
if (m_handle) {
m_handle.destroy();
}
}

[[nodiscard]] std::coroutine_handle<PromiseState<T>> handle() const { return m_handle; }

private:
std::coroutine_handle<PromiseState<T>> m_handle;
};

template <class T>
class PromiseState final {
public:
Promise<T> get_return_object() { return Promise<T>{std::coroutine_handle<PromiseState>::from_promise(*this)}; }

std::suspend_always initial_suspend() noexcept { return {}; }

struct FinalSuspend final {
bool await_ready() noexcept { return false; }
void await_suspend(std::coroutine_handle<PromiseState> h) noexcept { h.promise().complete(); }
void await_resume() noexcept {}
};

FinalSuspend final_suspend() noexcept { return {}; }

void return_value(T value) { received.set_value(std::move(value)); }
void unhandled_exception() { received.set_exception(std::current_exception()); }

template <class A>
AwaitedBy<A, PromiseState> await_transform(A&& awaitable) {
return {std::forward<A>(awaitable)};
}

Receiver<T>* receiver{};

private:
void complete() { std::move(received).forward(*receiver); }

Received<T> received;
};

template <>
class PromiseState<void> final {
public:
Promise<void> get_return_object() {
return Promise<void>{std::coroutine_handle<PromiseState>::from_promise(*this)};
}

std::suspend_always initial_suspend() noexcept { return {}; }

struct FinalSuspend final {
bool await_ready() noexcept { return false; }
void await_suspend(std::coroutine_handle<PromiseState> h) noexcept { h.promise().complete(); }
void await_resume() noexcept {}
};

FinalSuspend final_suspend() noexcept { return {}; }

void return_void() { received.set_value(); }
void unhandled_exception() { received.set_exception(std::current_exception()); }

template <class A>
AwaitedBy<A, PromiseState> await_transform(A&& awaitable) {
return {std::forward<A>(awaitable)};
}

Receiver<void>* receiver{};

private:
void complete() { std::move(received).forward(*receiver); }

Received<void> received;
};

template <class T, class P>
class AwaiterReceiver final {
public:
AwaiterReceiver(Received<T>& received, std::coroutine_handle<P> awaiter)
: m_received{&received}, m_awaiter{awaiter} {}

void set_value(T value) {
m_received->set_value(std::move(value));

m_awaiter.resume();
}

void set_exception(std::exception_ptr ptr) {
m_received->set_exception(std::move(ptr));

m_awaiter.resume();
}

private:
Received<T>* m_received;
std::coroutine_handle<P> m_awaiter;
};

template <class P>
class AwaiterReceiver<void, P> final {
public:
AwaiterReceiver(Received<void>& received, std::coroutine_handle<P> awaiter)
: m_received{&received}, m_awaiter{awaiter} {}

void set_value() {
m_received->set_value();

m_awaiter.resume();
}

void set_exception(std::exception_ptr ptr) {
m_received->set_exception(std::move(ptr));

m_awaiter.resume();
}

private:
Received<void>* m_received;
std::coroutine_handle<P> m_awaiter;
};

template <class S, class P>
class SenderAwaiter final {

public:
using sender_type = S;
using value_type = sender_type::value_type;

explicit SenderAwaiter(sender_type sender) : state{std::move(sender)} {}

bool await_ready() noexcept { return false; }

std::coroutine_handle<> await_suspend(std::coroutine_handle<P> cont) {
auto& operation_state = state.template emplace<operation_state_type>(
std::get<sender_type>(std::move(state)).connect(AwaiterReceiver<value_type, P>{this->received, cont}));

operation_state.start();

return std::noop_coroutine();
}

[[nodiscard]] value_type await_resume() { return std::move(received).consume(); }

private:
using operation_state_type = decltype(std::declval<S>().connect(std::declval<AwaiterReceiver<value_type, P>>()));

std::variant<std::monostate, sender_type, operation_state_type> state;
Received<value_type> received;
};

// todo sender concept
template <class A, class P>
SenderAwaiter<A, P> operator co_await(AwaitedBy<A, P>&& awaited_by) {
return SenderAwaiter<A, P>{std::move(awaited_by).value};
}

template <class T, class R>
class PromiseReceiver final : public Receiver<T> {

public:
explicit PromiseReceiver(R receiver) : Receiver<T>{}, m_receiver{std::move(receiver)} {}

void set_value(T value) override { m_receiver.set_value(std::move(value)); }
void set_exception(std::exception_ptr exc) override { m_receiver.set_exception(exc); }

private:
R m_receiver;
};

template <class R>
class PromiseReceiver<void, R> final : public Receiver<void> {

public:
explicit PromiseReceiver(R receiver) : Receiver<void>{}, m_receiver{std::move(receiver)} {}

void set_value() override { m_receiver.set_value(); }
void set_exception(std::exception_ptr exc) override { m_receiver.set_exception(exc); }

private:
R m_receiver;
};

template <class T>
class [[nodiscard]] Co final {

public:
using promise_type = PromiseState<T>;
using value_type = T;

explicit(false) Co(Promise<T> promise) : m_promise{std::move(promise)} {}

template <class R>
auto connect(R&& receiver) && {
class OperationState {

public:
OperationState(Promise<T> promise, R receiver)
: m_receiver{std::move(receiver)}, m_promise{std::move(promise)} {}

void start() {
m_promise.handle().promise().receiver = &m_receiver;
m_promise.handle().resume();
}

private:
PromiseReceiver<T, R> m_receiver;
Promise<T> m_promise;
};

return OperationState{std::move(*this).m_promise, std::forward<R>(receiver)};
}

private:
Promise<T> m_promise;
};

} // namespace fastipc::co
Loading
Loading