Introduce coroutine support (util/coro.hpp)

Implement coroutine types `lazy` and `generator` in stx namespace.
Implement fs::list_dir_recursively.
This commit is contained in:
Nekotekina 2021-11-30 17:40:46 +03:00
parent d6420b8803
commit 6b40d69a8f
4 changed files with 409 additions and 1 deletions

View File

@ -9,6 +9,7 @@
#include <map>
#include "util/asm.hpp"
#include "util/coro.hpp"
using namespace std::literals::string_literals;
@ -1182,7 +1183,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
for (const char* data = static_cast<const char*>(buffer); count;)
{
const DWORD size = static_cast<DWORD>(std::min<u64>(count, DWORD{umax} & -4096));
const DWORD size = static_cast<DWORD>(std::min<u64>(count, DWORD{umax} & -4096));
DWORD nwritten = 0;
ensure(WriteFile(m_handle, data, size, &nwritten, nullptr)); // "file::write"
@ -2039,6 +2040,30 @@ bool fs::pending_file::commit(bool overwrite)
return false;
}
stx::generator<fs::dir_entry&> fs::list_dir_recursively(std::string path)
{
for (auto& entry : fs::dir(path))
{
if (entry.name == "." || entry.name == "..")
{
continue;
}
std::string new_path = path_append(path, entry.name);
if (entry.is_directory)
{
for (auto& nested : fs::list_dir_recursively(new_path))
{
co_yield nested;
}
}
entry.name = std::move(new_path);
co_yield entry;
}
}
template<>
void fmt_class_string<fs::seek_mode>::format(std::string& out, u64 arg)
{

View File

@ -804,4 +804,6 @@ namespace fs
}
file make_gather(std::vector<file>);
stx::generator<dir_entry&> list_dir_recursively(std::string path);
}

375
rpcs3/util/coro.hpp Normal file
View File

@ -0,0 +1,375 @@
#pragma once
#if __has_include(<coroutine>)
#if defined(__clang__) && !defined(__cpp_impl_coroutine)
#define __cpp_impl_coroutine 123
#endif
#include <coroutine>
#if defined(__clang__) && (__cpp_impl_coroutine == 123)
namespace std::experimental
{
using ::std::coroutine_traits;
using ::std::coroutine_handle;
}
#endif
#elif __has_include(<experimental/coroutine>)
#include <experimental/coroutine>
namespace std
{
using experimental::coroutine_traits;
using experimental::coroutine_handle;
using experimental::noop_coroutine_handle;
using experimental::suspend_always;
using experimental::suspend_never;
}
#endif
#include <iterator>
namespace stx
{
template <typename T>
struct lazy;
template <typename T>
struct generator;
namespace detail
{
struct lazy_promise_base
{
struct final_suspend_t
{
constexpr bool await_ready() const noexcept { return false; }
template <typename P>
std::coroutine_handle<> await_suspend(std::coroutine_handle<P> h) noexcept
{
return h.promise().m_handle;
}
void await_resume() noexcept {}
};
constexpr lazy_promise_base() noexcept = default;
lazy_promise_base(const lazy_promise_base&) = delete;
lazy_promise_base& operator=(const lazy_promise_base&) = delete;
std::suspend_always initial_suspend() { return {}; }
final_suspend_t final_suspend() noexcept { return {}; }
void unhandled_exception() {}
protected:
std::coroutine_handle<> m_handle{};
};
template <typename T>
struct lazy_promise final : lazy_promise_base
{
constexpr lazy_promise() noexcept = default;
lazy<T> get_return_object() noexcept;
template <typename U>
void return_value(U&& value)
{
m_value = std::forward<U>(value);
}
T& result() &
{
return m_value;
}
T&& result() &&
{
return m_value;
}
private:
T m_value{};
};
template <typename T>
struct lazy_promise<T&> final : lazy_promise_base
{
constexpr lazy_promise() noexcept = default;
lazy<T&> get_return_object() noexcept;
void return_value(T& value) noexcept
{
m_value = std::addressof(value);
}
T& result()
{
return *m_value;
}
private:
T* m_value{};
};
template <>
struct lazy_promise<void> final : lazy_promise_base
{
constexpr lazy_promise() noexcept = default;
lazy<void> get_return_object() noexcept;
void return_void() noexcept {}
void result() {}
};
}
template <typename T = void>
struct [[nodiscard]] lazy
{
using promise_type = detail::lazy_promise<T>;
using value_type = T;
struct awaitable_base
{
std::coroutine_handle<promise_type> coro;
bool await_ready() const noexcept
{
return !coro || coro.done();
}
std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) noexcept
{
coro.promise().m_handle = h;
return coro;
}
};
lazy(const lazy&) = delete;
lazy(lazy&& rhs) noexcept
: m_handle(rhs.m_handle)
{
rhs.m_handle = nullptr;
}
lazy& operator=(const lazy&) = delete;
~lazy()
{
if (m_handle)
{
m_handle.destroy();
}
}
bool is_ready() const noexcept
{
return !m_handle || m_handle.done();
}
auto operator co_await() const & noexcept
{
struct awaitable : awaitable_base
{
using awaitable_base::awaitable_base;
decltype(auto) await_resume()
{
return this->m_handle.promise().result();
}
};
return awaitable{m_handle};
}
auto operator co_await() const && noexcept
{
struct awaitable : awaitable_base
{
using awaitable_base::awaitable_base;
decltype(auto) await_resume()
{
return std::move(this->m_handle.promise()).result();
}
};
return awaitable{m_handle};
}
private:
friend promise_type;
explicit constexpr lazy(std::coroutine_handle<promise_type> h) noexcept
: m_handle(h)
{
}
std::coroutine_handle<promise_type> m_handle;
};
template <typename T>
inline lazy<T> detail::lazy_promise<T>::get_return_object() noexcept
{
return lazy<T>{std::coroutine_handle<lazy_promise>::from_promise(*this)};
}
template <typename T>
inline lazy<T&> detail::lazy_promise<T&>::get_return_object() noexcept
{
return lazy<T&>{std::coroutine_handle<lazy_promise>::from_promise(*this)};
}
inline lazy<void> detail::lazy_promise<void>::get_return_object() noexcept
{
return lazy<void>{std::coroutine_handle<lazy_promise>::from_promise(*this)};
}
namespace detail
{
template <typename T>
struct generator_promise
{
using value_type = std::remove_reference_t<T>;
using reference_type = std::conditional_t<std::is_reference_v<T>, T, T&>;
generator_promise() = default;
generator<T> get_return_object() noexcept;
constexpr std::suspend_always initial_suspend() const noexcept { return {}; }
constexpr std::suspend_always final_suspend() const noexcept { return {}; }
void unhandled_exception() {}
std::suspend_always yield_value(std::remove_reference_t<T>& value) noexcept
{
m_ptr = std::addressof(value);
return {};
}
std::suspend_always yield_value(std::remove_reference_t<T>&& value) noexcept
{
m_ptr = std::addressof(value);
return {};
}
void return_void() {}
reference_type value() const noexcept
{
return static_cast<reference_type>(*m_ptr);
}
template <typename U>
std::suspend_never await_transform(U&& value) = delete;
private:
value_type* m_ptr;
};
struct generator_end
{
};
template<typename T>
struct generator_iterator
{
using iterator_category = std::input_iterator_tag;
using value_type = typename generator_promise<T>::value_type;
using reference = typename generator_promise<T>::reference_type;
constexpr generator_iterator() noexcept = default;
explicit constexpr generator_iterator(std::coroutine_handle<generator_promise<T>> coro) noexcept
: m_coro(coro)
{
}
bool operator==(generator_end) const noexcept
{
return !m_coro || m_coro.done();
}
generator_iterator& operator++()
{
m_coro.resume();
return *this;
}
void operator++(int)
{
m_coro.resume();
}
reference operator*() const noexcept
{
return m_coro.promise().value();
}
value_type* operator->() const noexcept
{
return std::addressof(operator*());
}
private:
std::coroutine_handle<generator_promise<T>> m_coro;
};
}
template<typename T>
struct [[nodiscard]] generator
{
using promise_type = detail::generator_promise<T>;
using iterator = detail::generator_iterator<T>;
generator(const generator&) = delete;
generator(generator&& rhs) noexcept
: m_handle(rhs.m_handle)
{
rhs.m_handle = nullptr;
}
generator& operator=(const generator&) = delete;
~generator()
{
if (m_handle)
{
m_handle.destroy();
}
}
iterator begin()
{
if (m_handle)
{
m_handle.resume();
}
return iterator{m_handle};
}
detail::generator_end end() noexcept
{
return detail::generator_end{};
}
private:
friend promise_type;
explicit constexpr generator(std::coroutine_handle<promise_type> h) noexcept
: m_handle(h)
{
}
std::coroutine_handle<promise_type> m_handle;
};
template <typename T>
generator<T> detail::generator_promise<T>::get_return_object() noexcept
{
return generator<T>{std::coroutine_handle<generator_promise<T>>::from_promise(*this)};
}
}

View File

@ -131,6 +131,12 @@ namespace stx
{
template <typename T, bool Se, usz Align>
class se_t;
template <typename T>
struct lazy;
template <typename T>
struct generator;
}
using stx::se_t;