diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index a94312ad..79ebc98a 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -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", diff --git a/CREDITS.md b/CREDITS.md index 0d2a9d29..44d8790f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -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) diff --git a/Makefile b/Makefile index 2fb1332c..2e370fd8 100644 --- a/Makefile +++ b/Makefile @@ -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 $<) diff --git a/include_std/coroutine b/include_std/coroutine index 01185507..8fdbc074 100644 --- a/include_std/coroutine +++ b/include_std/coroutine @@ -17,5 +17,173 @@ namespace std { - /* FIXME: Implement */ +#if __cpp_impl_coroutine + namespace detail + { + template + struct coroutine_traits_base + { + }; + + template + requires requires { typename R::promise_type; } + struct coroutine_traits_base + { + using promise_type = R::promise_type; + }; + } + + template + struct coroutine_traits : detail::coroutine_traits_base + { + }; + + template + struct coroutine_handle; + + struct noop_coroutine_promise; + template <> + struct coroutine_handle; + using noop_coroutine_handle = coroutine_handle; + noop_coroutine_handle noop_coroutine() noexcept; + + template + struct hash; + template + struct hash>; + + template <> + struct coroutine_handle + { + 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 + 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(ptr); } + }; + + struct noop_coroutine_promise + { + }; + + template <> + struct coroutine_handle + { + 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 } diff --git a/kernel_thread.cpp b/kernel_thread.cpp index 186c690e..3b75f132 100644 --- a/kernel_thread.cpp +++ b/kernel_thread.cpp @@ -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", diff --git a/tests/stl/coroutines.cpp b/tests/stl/coroutines.cpp new file mode 100644 index 00000000..d052f80b --- /dev/null +++ b/tests/stl/coroutines.cpp @@ -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 . +*/ + +#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 + +/* https://gist.github.com/Qix-/caa277fbf1a4e6ca55a27f2242df3b9a */ + +class resumable +{ +public: + struct promise_type; + using coro_handle = std::coroutine_handle; + + 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; + + 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::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 handle; + explicit SyscallTask(std::coroutine_handle 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 diff --git a/tests/t.h b/tests/t.h index 4b9e7189..dafb7a33 100644 --- a/tests/t.h +++ b/tests/t.h @@ -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__