diff --git a/include/fast_io_hosted/platforms/nt/nt_path.h b/include/fast_io_hosted/platforms/nt/nt_path.h index e9103ea9..0cecfcbc 100644 --- a/include/fast_io_hosted/platforms/nt/nt_path.h +++ b/include/fast_io_hosted/platforms/nt/nt_path.h @@ -99,22 +99,18 @@ inline auto nt_call_invoke_with_directory_handle_impl(void *directory, char_type = char16_t const *; if constexpr (::std::same_as) { - using char16_may_alias_ptr -#if __has_cpp_attribute(__gnu__::__may_alias__) - [[__gnu__::__may_alias__]] -#endif - = char16_t *; ::std::uint_least16_t const bytes(strlen_to_nt_filename_bytes(filename_len)); // Since this involves escaping Win32-style paths to NT, all forward slashes '/' are converted to backslashes '\\'. auto const char16_t_size{bytes / sizeof(char16_t)}; - rtl_alloc_guard rtl_guard{rtl_alloc_guard::allocator::allocate(char16_t_size)}; + rtl_alloc_guard rtl_guard{rtl_alloc_guard::allocator::allocate(char16_t_size + 1u)}; ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(filename), bytes, reinterpret_cast<::std::byte *>(rtl_guard.ptr)); - for (auto curr{rtl_guard.ptr}; curr != rtl_guard.ptr + char16_t_size; ++curr) + auto curr{rtl_guard.ptr}; + for (; curr != rtl_guard.ptr + char16_t_size; ++curr) { if (*curr == u'/') { @@ -122,8 +118,12 @@ inline auto nt_call_invoke_with_directory_handle_impl(void *directory, char_type } } + *curr = u'\0'; + win32::nt::unicode_string relative_path{ - .Length = bytes, .MaximumLength = bytes, .Buffer = rtl_guard.ptr}; + .Length = bytes, + .MaximumLength = static_cast<::std::uint_least16_t>(bytes + sizeof(char16_t)), + .Buffer = rtl_guard.ptr}; return callback(directory, __builtin_addressof(relative_path)); } else if constexpr (sizeof(char_type) == sizeof(char16_t)) diff --git a/include/fast_io_hosted/platforms/win32_network/socket_file.h b/include/fast_io_hosted/platforms/win32_network/socket_file.h index c068ac5e..998c6610 100644 --- a/include/fast_io_hosted/platforms/win32_network/socket_file.h +++ b/include/fast_io_hosted/platforms/win32_network/socket_file.h @@ -46,7 +46,7 @@ struct win32_socket_event_guard_t curr_handle = {}; return temp; } - + inline constexpr native_handle_type native_handle() const noexcept { return curr_handle; diff --git a/include/fast_io_hosted/process/process/arg_env.h b/include/fast_io_hosted/process/process/arg_env.h index 8d69663b..1023e0eb 100644 --- a/include/fast_io_hosted/process/process/arg_env.h +++ b/include/fast_io_hosted/process/process/arg_env.h @@ -50,7 +50,6 @@ inline constexpr void append_win32_quoted_arg_common( if (!needs_quote) { - auto const sz{static_cast<::std::size_t>(last - first)}; for (it = first; it != last; ++it) { str.push_back(*it); @@ -59,7 +58,6 @@ inline constexpr void append_win32_quoted_arg_common( return; } - auto const sz{static_cast<::std::size_t>(last - first)}; str.push_back(::fast_io::char_literal_v); for (it = first; it != last; ++it) { diff --git a/include/fast_io_hosted/threads/thread/dos.h b/include/fast_io_hosted/threads/thread/dos.h index e9c24962..3639b984 100644 --- a/include/fast_io_hosted/threads/thread/dos.h +++ b/include/fast_io_hosted/threads/thread/dos.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include diff --git a/include/fast_io_hosted/threads/thread/impl.h b/include/fast_io_hosted/threads/thread/impl.h index 67b20ab8..d55c3c54 100644 --- a/include/fast_io_hosted/threads/thread/impl.h +++ b/include/fast_io_hosted/threads/thread/impl.h @@ -7,7 +7,8 @@ #endif #elif defined(__MSDOS__) || defined(__DJGPP__) #include "dos.h" -#elif !defined(__SINGLE_THREAD__) && !defined(__NEWLIB__) && \ - !defined(__MSDOS__) && !defined(__wasi__) && __has_include() +#elif defined(__wasi__) +#include "wasi.h" +#elif !defined(__SINGLE_THREAD__) && !defined(__NEWLIB__) && !defined(__MSDOS__) && __has_include() #include "pthread.h" #endif diff --git a/include/fast_io_hosted/threads/thread/wasi.h b/include/fast_io_hosted/threads/thread/wasi.h new file mode 100644 index 00000000..2af0274c --- /dev/null +++ b/include/fast_io_hosted/threads/thread/wasi.h @@ -0,0 +1,411 @@ +#pragma once + +#if defined(__wasi__) + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace fast_io +{ + +namespace wasi +{ + +namespace details +{ + +using wasi_thread_id = ::std::uint_least64_t; + +inline ::std::atomic next_thread_id{1u}; + +inline thread_local wasi_thread_id current_thread_id{}; + +inline +#if __cpp_constexpr >= 202207L + constexpr +#endif +wasi_thread_id get_or_assign_thread_id() noexcept +{ + auto id{current_thread_id}; + if (id == 0) + { + id = next_thread_id.fetch_add(1u, ::std::memory_order_relaxed); + current_thread_id = id; + } + return id; +} + +inline void assign_thread_id_for_current(wasi_thread_id id) noexcept +{ + current_thread_id = id; +} + +inline void sleep_for_ns(__wasi_timestamp_t ns) noexcept +{ + if (ns == 0) + { + return; + } + + __wasi_timestamp_t start{}; + if (__wasi_clock_time_get(__WASI_CLOCKID_MONOTONIC, 0, __builtin_addressof(start)) != __WASI_ERRNO_SUCCESS) + { + return; + } + + for (;;) + { + __wasi_timestamp_t now{}; + if (__wasi_clock_time_get(__WASI_CLOCKID_MONOTONIC, 0, __builtin_addressof(now)) != __WASI_ERRNO_SUCCESS) + { + return; + } + if (now - start >= ns) + { + break; + } + (void)__wasi_sched_yield(); + } +} + +#if defined(__wasi_thread__) + +struct wasi_thread_control_block +{ + wasi_thread_id id{}; + void *data{}; + void (*run)(void *) noexcept{}; + void (*destroy)(void *) noexcept{}; + ::std::atomic<::std::uint_least32_t> refcount{2u}; + ::std::atomic done{false}; +}; + +inline void release_ref(wasi_thread_control_block *cb) noexcept +{ + if (cb == nullptr) + { + return; + } + if (cb->refcount.fetch_sub(1u, ::std::memory_order_acq_rel) == 1u) + { + cb->destroy(cb->data); + using alloc_cb = ::fast_io::native_typed_global_allocator; + alloc_cb::deallocate_n(cb, 1u); + } +} + +template +inline void run_impl(void *p) noexcept +{ + auto *tup{reinterpret_cast(p)}; +#ifdef FAST_IO_CPP_EXCEPTIONS + try +#endif + { + ::std::apply([](auto &func, auto &...args) { ::std::invoke(func, args...); }, *tup); + } +#ifdef FAST_IO_CPP_EXCEPTIONS + catch (...) + { + ::fast_io::fast_terminate(); + } +#endif +} + +template +inline void destroy_impl(void *p) noexcept +{ + using alloc = ::fast_io::native_typed_global_allocator; + auto *tup{reinterpret_cast(p)}; + ::std::destroy_at(tup); + alloc::deallocate_n(tup, 1u); +} + +template +inline wasi_thread_control_block *make_control_block(Func &&func, Args &&...args) +{ + using tuple_type = ::fast_io::containers::tuple<::std::decay_t, ::std::decay_t...>; + using alloc_tuple = ::fast_io::native_typed_global_allocator; + using alloc_cb = ::fast_io::native_typed_global_allocator; + + auto tup{alloc_tuple::allocate(1u)}; +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-braces" +#endif + ::new (tup) tuple_type{::std::forward(func), ::std::forward(args)...}; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + auto cb{alloc_cb::allocate(1u)}; + ::new (cb) wasi_thread_control_block{next_thread_id.fetch_add(1u, ::std::memory_order_relaxed), tup, + &run_impl, &destroy_impl, 2u, false}; + return cb; +} + +inline void wasi_thread_start_impl(void *start_arg) noexcept +{ + auto *cb{reinterpret_cast(start_arg)}; + assign_thread_id_for_current(cb->id); + cb->run(cb->data); + cb->done.store(true, ::std::memory_order_release); + release_ref(cb); +} + +extern "C" inline void wasi_thread_start(void *start_arg) noexcept + __attribute__((export_name("wasi_thread_start"))); + +extern "C" inline void wasi_thread_start(void *start_arg) noexcept +{ + ::fast_io::wasi::details::wasi_thread_start_impl(start_arg); +} + +#endif + +} // namespace details + +namespace this_thread +{ + +using id = ::fast_io::wasi::details::wasi_thread_id; + +inline +#if __cpp_constexpr >= 202207L + constexpr +#endif +id get_id() noexcept +{ + return ::fast_io::wasi::details::get_or_assign_thread_id(); +} + +template +inline +#if __cpp_constexpr >= 202207L + constexpr +#endif +void sleep_for(::std::chrono::duration const &sleep_duration) noexcept +{ + auto const ns64{::std::chrono::duration_cast<::std::chrono::nanoseconds>(sleep_duration).count()}; + if (ns64 <= 0) + { + return; + } + ::fast_io::wasi::details::sleep_for_ns(static_cast<__wasi_timestamp_t>(ns64)); +} + +template +inline +#if __cpp_constexpr >= 202207L + constexpr +#endif +void sleep_until(::std::chrono::time_point const &expect_time) noexcept +{ + auto const now{Clock::now()}; + if (now < expect_time) + { + ::fast_io::wasi::this_thread::sleep_for(expect_time - now); + } +} + +template <::std::int_least64_t off_to_epoch> +inline +#if __cpp_constexpr >= 202207L + constexpr +#endif +void sleep_for(::fast_io::basic_timestamp const &sleep_duration) noexcept +{ + if (sleep_duration.seconds < 0) + { + return; + } + + constexpr ::std::uint_least64_t mul_factor{::fast_io::uint_least64_subseconds_per_second / 1000000000u}; + + auto const ns64{ + static_cast<::std::uint_least64_t>(static_cast<::std::uint_least64_t>(sleep_duration.seconds) * 1000000000u + + sleep_duration.subseconds / mul_factor)}; + + if (ns64 == 0) + { + return; + } + + ::fast_io::wasi::details::sleep_for_ns(static_cast<__wasi_timestamp_t>(ns64)); +} + +template <::std::int_least64_t off_to_epoch> +inline +#if __cpp_constexpr >= 202207L + constexpr +#endif +void sleep_until(::fast_io::basic_timestamp const &expect_time) noexcept +{ + if (expect_time.seconds < 0) + { + return; + } + + auto const unix_expect{static_cast<::fast_io::unix_timestamp>(expect_time)}; + auto const now{::fast_io::posix_clock_gettime(::fast_io::posix_clock_id::realtime)}; + if (!(now < unix_expect)) + { + return; + } + + auto const delta{unix_expect - now}; + ::fast_io::wasi::this_thread::sleep_for(delta); +} + +} // namespace this_thread + +#if defined(__wasi_thread__) + +class wasi_thread +{ +public: + using id = ::fast_io::wasi::details::wasi_thread_id; + using native_handle_type = ::fast_io::wasi::details::wasi_thread_control_block *; + +private: + native_handle_type cb_{}; + id id_{}; + bool joinable_{false}; + +public: + inline constexpr wasi_thread() noexcept = default; + + template + requires(::std::invocable) + inline wasi_thread(Func &&func, Args &&...args) + { + auto *cb{::fast_io::wasi::details::make_control_block(::std::forward(func), + ::std::forward(args)...)}; + auto ec{__wasi_thread_spawn(cb)}; + if (ec != 0) + { + ::fast_io::wasi::details::release_ref(cb); + ::fast_io::wasi::details::release_ref(cb); + ::fast_io::throw_posix_error(ec); + } + cb_ = cb; + id_ = cb->id; + joinable_ = true; + } + + inline constexpr wasi_thread(wasi_thread const &) noexcept = delete; + + inline constexpr wasi_thread(wasi_thread &&other) noexcept : cb_{other.cb_}, id_{other.id_}, joinable_{other.joinable_} + { + other.cb_ = nullptr; + other.id_ = {}; + other.joinable_ = false; + } + + inline ~wasi_thread() noexcept + { + if (this->joinable()) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + } + + inline constexpr wasi_thread &operator=(wasi_thread const &) noexcept = delete; + + inline constexpr wasi_thread &operator=(wasi_thread &&other) noexcept + { + if (this == __builtin_addressof(other)) [[unlikely]] + { + return *this; + } + this->swap(other); + return *this; + } + + inline constexpr bool joinable() const noexcept + { + return this->joinable_; + } + + inline void join() + { + if (!this->joinable()) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + for (;;) + { + if (cb_->done.load(::std::memory_order_acquire)) + { + break; + } + (void)__wasi_sched_yield(); + } + this->joinable_ = false; + ::fast_io::wasi::details::release_ref(cb_); + cb_ = nullptr; + } + + inline void detach() + { + if (!this->joinable()) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->joinable_ = false; + ::fast_io::wasi::details::release_ref(cb_); + cb_ = nullptr; + } + + inline constexpr void swap(wasi_thread &other) noexcept + { + ::std::ranges::swap(cb_, other.cb_); + ::std::ranges::swap(id_, other.id_); + ::std::ranges::swap(joinable_, other.joinable_); + } + + [[nodiscard]] + inline constexpr auto get_id() const noexcept + { + return this->id_; + } + + [[nodiscard]] + inline constexpr auto native_handle() const noexcept + { + return this->cb_; + } + + [[nodiscard]] + inline static constexpr ::std::uint_least32_t hardware_concurrency() noexcept + { + return 0u; + } +}; + +#endif + +} // namespace wasi + +#if defined(__wasi_thread__) +using wasi_thread = wasi::wasi_thread; +using native_thread = wasi_thread; +#endif + +namespace this_thread +{ +using ::fast_io::wasi::this_thread::get_id; +using ::fast_io::wasi::this_thread::sleep_for; +using ::fast_io::wasi::this_thread::sleep_until; +} // namespace this_thread + +} // namespace fast_io + +#endif