stl: Implement coroutines

This commit is contained in:
EnderIce2 2024-11-15 01:54:21 +02:00
parent 1ab4ace23c
commit 4bc4e9b76f
Signed by untrusted user who does not match committer: enderice2
GPG Key ID: EACC3AD603BAB4DD
7 changed files with 333 additions and 2 deletions

View File

@ -32,6 +32,7 @@
"-mcmodel=kernel",
"-fno-builtin",
"-m64",
"-fcoroutines",
// Warnings
"-Wall",
"-Wextra",
@ -103,6 +104,7 @@
"-msoft-float",
"-fno-builtin",
"-m32",
"-fcoroutines",
// Warnings
"-Wall",
"-Wextra",
@ -167,6 +169,7 @@
"-msoft-float",
"-fPIC",
"-Wstack-protector",
"-fcoroutines",
// Warnings
"-Wall",
"-Wextra",

View File

@ -12,6 +12,9 @@ License information can be found in the [LICENSES.md](LICENSES.md) file.
- [ToaruOS](https://github.com/klange/toaruos)
- [Various SIMD functions](https://wiki.osdev.org/User:01000101/optlib/)
## C++ STL
- [cppreference.com](https://cppreference.com)
## Virtual terminal
- [vtconsole](https://github.com/sleepy-monax/vtconsole)

View File

@ -147,7 +147,7 @@ $(KERNEL_FILENAME): $(OBJ)
# https://gcc.gnu.org/projects/cxx-status.html
%.o: %.cpp $(HEADERS)
$(info Compiling $<)
$(CPP) $(CFLAGS) $(CFLAG_STACK_PROTECTOR) $(WARNCFLAG) -std=c++20 -c $< -o $@ -fno-rtti
$(CPP) $(CFLAGS) -fcoroutines $(CFLAG_STACK_PROTECTOR) $(WARNCFLAG) -std=c++20 -c $< -o $@ -fno-rtti
%.o: %.S
$(info Compiling $<)

View File

@ -17,5 +17,173 @@
namespace std
{
/* FIXME: Implement */
#if __cpp_impl_coroutine
namespace detail
{
template <class, class...>
struct coroutine_traits_base
{
};
template <class R, class... Args>
requires requires { typename R::promise_type; }
struct coroutine_traits_base<R, Args...>
{
using promise_type = R::promise_type;
};
}
template <class R, class... Args>
struct coroutine_traits : detail::coroutine_traits_base<R, Args...>
{
};
template <class Promise = void>
struct coroutine_handle;
struct noop_coroutine_promise;
template <>
struct coroutine_handle<noop_coroutine_promise>;
using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;
noop_coroutine_handle noop_coroutine() noexcept;
template <class T>
struct hash;
template <class P>
struct hash<coroutine_handle<P>>;
template <>
struct coroutine_handle<void>
{
private:
void *ptr;
public:
constexpr coroutine_handle() noexcept : ptr(nullptr) {}
constexpr coroutine_handle(nullptr_t __nul) noexcept : ptr(__nul) {}
coroutine_handle &operator=(nullptr_t) noexcept
{
ptr = nullptr;
return *this;
}
constexpr void *address() const noexcept { return ptr; }
static constexpr coroutine_handle from_address(void *addr)
{
coroutine_handle temp;
temp.ptr = addr;
return temp;
}
constexpr explicit operator bool() const noexcept { return bool(ptr); }
bool done() const { return __builtin_coro_done(ptr); }
void operator()() const { resume(); }
void resume() const { __builtin_coro_resume(ptr); }
void destroy() const { __builtin_coro_destroy(ptr); }
};
constexpr bool operator==(coroutine_handle<> x, coroutine_handle<> y) noexcept
{
return x.address() == y.address();
}
// constexpr strong_ordering operator<=>(coroutine_handle<> x, coroutine_handle<> y) noexcept;
template <class Promise>
struct coroutine_handle
{
private:
void *ptr;
public:
constexpr coroutine_handle() noexcept : ptr(nullptr) {}
constexpr coroutine_handle(nullptr_t) noexcept : ptr(nullptr) {}
static coroutine_handle from_promise(Promise &promise)
{
coroutine_handle temp;
temp.ptr = __builtin_coro_promise((char *)&promise, __alignof(Promise), true);
return temp;
}
coroutine_handle &operator=(nullptr_t) noexcept
{
ptr = nullptr;
return *this;
}
constexpr void *address() const noexcept { return ptr; }
static constexpr coroutine_handle from_address(void *addr)
{
coroutine_handle temp;
temp.ptr = addr;
return temp;
}
constexpr operator coroutine_handle<>() const noexcept
{
return coroutine_handle<>::from_address(address());
}
constexpr explicit operator bool() const noexcept { return bool(ptr); }
bool done() const { return __builtin_coro_done(ptr); }
void operator()() const { resume(); }
void resume() const { __builtin_coro_resume(ptr); }
void destroy() const { __builtin_coro_destroy(ptr); }
Promise &promise() const { return *static_cast<Promise *>(ptr); }
};
struct noop_coroutine_promise
{
};
template <>
struct coroutine_handle<noop_coroutine_promise>
{
private:
static struct __chframe
{
static void __stub_resume() {}
void (*__a)() = __stub_resume;
void (*__b)() = __stub_resume;
struct noop_coroutine_promise __c;
} __frame;
void *ptr = &__frame;
explicit coroutine_handle() noexcept = default;
friend coroutine_handle noop_coroutine() noexcept;
public:
constexpr operator coroutine_handle<>() const noexcept { return coroutine_handle<>::from_address(address()); }
constexpr explicit operator bool() const noexcept { return true; }
constexpr bool done() const noexcept { return false; }
constexpr void operator()() const noexcept {}
constexpr void resume() const noexcept {}
constexpr void destroy() const noexcept {}
noop_coroutine_promise &promise() const noexcept { return __frame.__c; }
constexpr void *address() const noexcept { return ptr; }
};
inline noop_coroutine_handle::__chframe noop_coroutine_handle::__frame{};
noop_coroutine_handle noop_coroutine() noexcept { return noop_coroutine_handle(); }
struct suspend_never
{
constexpr bool await_ready() const noexcept { return true; }
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};
struct suspend_always
{
constexpr bool await_ready() const noexcept { return false; }
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};
#else
#error "kernel requires -fcoroutines"
#endif
}

View File

@ -65,6 +65,7 @@ void KernelMainThread()
TaskManager->CreateThread(thisProcess, Tasking::IP(TaskMgr));
TaskManager->CreateThread(thisProcess, Tasking::IP(TaskHeartbeat));
TreeFS(fs->GetRoot(0), 0);
coroutineTest();
#endif
KPrint("Kernel compiled using GCC %d.%d.%d as of %s %s with Standard C++ %dL",

155
tests/stl/coroutines.cpp Normal file
View File

@ -0,0 +1,155 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifdef DEBUG
/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109867 */
#pragma GCC diagnostic ignored "-Wswitch-default"
#include "../t.h"
#include "../kernel.h"
#include <coroutine>
/* https://gist.github.com/Qix-/caa277fbf1a4e6ca55a27f2242df3b9a */
class resumable
{
public:
struct promise_type;
using coro_handle = std::coroutine_handle<promise_type>;
resumable(coro_handle handle) : co_handle(handle) { assert(handle); }
resumable(resumable &) = delete;
resumable(resumable &&) = delete;
~resumable() { co_handle.destroy(); }
bool resume()
{
if (!co_handle.done())
co_handle.resume();
return !co_handle.done();
}
private:
coro_handle co_handle;
};
struct resumable::promise_type
{
using coro_handle = std::coroutine_handle<promise_type>;
auto get_return_object() { return coro_handle::from_promise(*this); }
auto initial_suspend() { return std::suspend_always(); }
auto final_suspend() noexcept { return std::suspend_always(); }
void return_void() {}
void unhandled_exception() { assert(!"std::terminate();"); }
};
resumable foo()
{
debug("await start");
co_await std::suspend_always();
debug("done");
}
/* ===================================================================== */
class SyscallAwaitable
{
public:
bool await_ready() const noexcept
{
debug("Syscall await_ready() called");
return false; /* false - coroutine is not ready, so suspend */
}
void await_suspend(std::coroutine_handle<> handle) const noexcept
{
debug("Syscall suspended, waiting for data...");
}
int await_resume() const noexcept
{
debug("Syscall resumed and returned 42");
return 42;
}
};
class SyscallTask
{
public:
struct promise_type
{
SyscallTask get_return_object()
{
return SyscallTask{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend()
{
debug("Syscall started");
return {};
}
std::suspend_never final_suspend() noexcept
{
debug("Syscall finished");
return {};
}
void return_void()
{
debug("Syscall finished");
}
void unhandled_exception()
{
assert("std::terminate();");
}
};
std::coroutine_handle<promise_type> handle;
explicit SyscallTask(std::coroutine_handle<promise_type> h) : handle(h) {}
~SyscallTask()
{
if (handle)
handle.destroy();
}
};
SyscallTask perform_syscall()
{
debug("Initializing syscall I/O...");
int data = co_await SyscallAwaitable{};
debug("Syscall finished: %d", data);
}
void coroutineTest()
{
/* Example of syscall using coroutine */
auto task = perform_syscall();
task.handle.resume();
/* Example of coroutine */
auto p = foo();
while (p.resume())
;
}
#endif // DEBUG

View File

@ -30,6 +30,7 @@ void TaskMgr();
void TreeFS(FileNode *node, int Depth);
void TaskHeartbeat();
void StressKernel();
void coroutineTest();
#endif // DEBUG
#endif // !__FENNIX_KERNEL_non_constructor_tests_H__