diff --git a/examples/0039.gen_win32_mangling/prebuild.cc b/examples/0039.gen_win32_mangling/prebuild.cc index 45af6a933..c18040e7e 100644 --- a/examples/0039.gen_win32_mangling/prebuild.cc +++ b/examples/0039.gen_win32_mangling/prebuild.cc @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -162,8 +163,11 @@ rule toasm } result.append(u8R"( rule compile - command = clang++ $in -o $out -O2 -I ../../../include -std=c++23 -lntdll - + command = clang++ $in -o $out -O2 -I ../../../include -std=c++23 )" +#ifdef _WIN32 + "-lntdll" +#endif +R"( rule run command = ./$in )"); @@ -222,7 +226,7 @@ int main() noexcept { ::fast_io::native_file build_ninja{"build/build.ninja", ::fast_io::open_mode::out}; if (argc == 2) { - ::std::size_t len_argv = strlen(argv[1]); + ::std::size_t len_argv = ::std::strlen(argv[1]); if (argv[1][len_argv - 1] == '\\') { argv[1][len_argv - 1] = 0; diff --git a/include/fast_io_hosted/threads/thread/c_malloc_guard.h b/include/fast_io_hosted/threads/thread/c_malloc_guard.h new file mode 100644 index 000000000..f86f53a64 --- /dev/null +++ b/include/fast_io_hosted/threads/thread/c_malloc_guard.h @@ -0,0 +1,30 @@ +#pragma once + +namespace fast_io::details +{ +class thread_start_routine_tuple_c_malloc_allocate_guard +{ +public: + void *ptr_{nullptr}; + + constexpr thread_start_routine_tuple_c_malloc_allocate_guard() = default; + + constexpr thread_start_routine_tuple_c_malloc_allocate_guard(void *ptr) : ptr_{ptr} + {} + + constexpr thread_start_routine_tuple_c_malloc_allocate_guard(thread_start_routine_tuple_c_malloc_allocate_guard const &) noexcept = delete; + constexpr thread_start_routine_tuple_c_malloc_allocate_guard(thread_start_routine_tuple_c_malloc_allocate_guard &&other) noexcept + : ptr_{other.ptr_} + { + other.ptr_ = nullptr; + } + + constexpr ~thread_start_routine_tuple_c_malloc_allocate_guard() + { + if (ptr_ != nullptr) + { + ::fast_io::generic_allocator_adapter<::fast_io::c_malloc_allocator>::deallocate(this->ptr_); + } + } +}; +} // namespace fast_io::details diff --git a/include/fast_io_hosted/threads/thread/impl.h b/include/fast_io_hosted/threads/thread/impl.h index 73b4b8665..61944f32d 100644 --- a/include/fast_io_hosted/threads/thread/impl.h +++ b/include/fast_io_hosted/threads/thread/impl.h @@ -1 +1,42 @@ -#pragma once +#pragma once + +#if (defined(_WIN32) && !defined(__WINE__)) && !defined(__CYGWIN__) +#ifdef _WIN32_WINDOWS +#include "win32.h" + +namespace fast_io +{ +using thread = ::fast_io::win32::win32_thread; + +namespace this_thread +{ + +using ::fast_io::win32::this_thread::get_id; +using ::fast_io::win32::this_thread::sleep_for; +using ::fast_io::win32::this_thread::sleep_until; + +} // namespace this_thread + +} // namespace fast_io +#else +#include "nt.h" + +namespace fast_io +{ +template +using thread = ::fast_io::win32::nt::nt_thread; + +namespace this_thread +{ + +using ::fast_io::win32::nt::this_thread::get_id; +using ::fast_io::win32::nt::this_thread::sleep_for; +using ::fast_io::win32::nt::this_thread::sleep_until; + +} // namespace this_thread + +} // namespace fast_io +#endif +#elif defined (__linux__) + +#endif diff --git a/include/fast_io_hosted/threads/thread/nt.h b/include/fast_io_hosted/threads/thread/nt.h index 73b4b8665..42892bfcd 100644 --- a/include/fast_io_hosted/threads/thread/nt.h +++ b/include/fast_io_hosted/threads/thread/nt.h @@ -1 +1,277 @@ -#pragma once +#pragma once + +#include +#include +#include +#include +#include +#include +#include "../../../fast_io_dsal/tuple.h" +#include "../../../fast_io_core_impl/allocation/common.h" + +namespace fast_io::win32::nt +{ + +namespace details +{ + +class nt_thread_start_routine_tuple_allocate_guard +{ +public: + void *ptr_{nullptr}; + + constexpr nt_thread_start_routine_tuple_allocate_guard() = default; + + constexpr nt_thread_start_routine_tuple_allocate_guard(void *ptr) : ptr_{ptr} + {} + + constexpr nt_thread_start_routine_tuple_allocate_guard(nt_thread_start_routine_tuple_allocate_guard const &) noexcept = delete; + constexpr nt_thread_start_routine_tuple_allocate_guard(nt_thread_start_routine_tuple_allocate_guard &&other) noexcept + : ptr_{other.ptr_} + { + other.ptr_ = nullptr; + } + + constexpr ~nt_thread_start_routine_tuple_allocate_guard() + { + if (ptr_ != nullptr) + { + ::fast_io::generic_allocator_adapter<::fast_io::nt_rtlallocateheap_allocator>::deallocate(this->ptr_); + } + } +}; + +template +constexpr ::std::uint_least32_t thread_start_routine(void *args) noexcept(noexcept( + ::std::invoke(::fast_io::get(*reinterpret_cast(args))...))) +{ + [[maybe_unused]] ::fast_io::win32::nt::details::nt_thread_start_routine_tuple_allocate_guard _(args); + ::std::invoke(::fast_io::get(*reinterpret_cast(args))...); + return 0; +} + +template +constexpr auto get_thread_start_routine(::std::index_sequence) noexcept +{ + return ::fast_io::win32::nt::details::thread_start_routine; +} + +} // namespace details + +template +class nt_thread +{ +public: + using id = void *; + using native_handle_type = void *; + +private: + id id_; + native_handle_type handle_; + +public: + nt_thread() noexcept + : id_{nullptr}, handle_{nullptr} + {} + + template + requires(::std::invocable) + constexpr nt_thread(Func &&func, Args &&...args) + { + using start_routine_tuple_type = ::fast_io::tuple; +#if __has_cpp_attribute(indeterminate) + ::fast_io::win32::nt::client_id cid [[indeterminate]]; +#else + ::fast_io::win32::nt::client_id cid; +#endif + void *start_routine_tuple{::fast_io::generic_allocator_adapter<::fast_io::nt_rtlallocateheap_allocator>::allocate(sizeof(start_routine_tuple_type))}; +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-braces" +#endif + ::new (start_routine_tuple) start_routine_tuple_type{::std::forward(func), ::std::forward(args)...}; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + auto start_routine = ::fast_io::win32::nt::details::get_thread_start_routine( + ::std::make_index_sequence{}); + ::std::uint_least32_t status{::fast_io::win32::nt::RtlCreateUserThread( + reinterpret_cast(-1), // Result of GetCurrentProcess() + nullptr, // SecurityDescriptor + false, // no suspand + 0, // StackZeroBits + 0, // StackReserved + 0, // StackCommit + reinterpret_cast(start_routine), + start_routine_tuple, // args of func + __builtin_addressof(this->handle_), + __builtin_addressof(cid))}; + if (status) [[unlikely]] + { + ::fast_io::throw_nt_error(status); + } + this->id_ = cid.UniqueThread; + } + + constexpr nt_thread(nt_thread const &) noexcept = delete; + + constexpr nt_thread(nt_thread &&other) noexcept + : id_(other.id_), handle_(other.handle_) + { + other.id_ = nullptr; + other.handle_ = nullptr; + } + + constexpr ~nt_thread() noexcept + { + if (handle_ != nullptr) + { + if (this->joinable()) [[unlikely]] + { + ::std::terminate(); + } + auto status{::fast_io::win32::nt::nt_close(this->handle_)}; + if (status) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + } + } + + constexpr nt_thread &operator=(nt_thread const &) noexcept = delete; + + constexpr nt_thread &operator=(nt_thread &&other) noexcept + { + if (this == __builtin_addressof(other)) [[unlikely]] + { + return *this; + } + this->swap(other); + return *this; + } + constexpr bool joinable() const noexcept + { + return this->id_ != nullptr; + } + + constexpr void join() + { + if (!this->joinable()) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + auto status{::fast_io::win32::nt::nt_wait_for_single_object(this->handle_, /* INFINITE = */ int(0xffffffff), nullptr)}; + if (status) [[unlikely]] + { + ::fast_io::throw_nt_error(status); + } + this->id_ = nullptr; + } + + constexpr void detach() + { + if (!this->joinable()) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + auto status{::fast_io::win32::nt::nt_close(this->handle_)}; + if (status) [[unlikely]] + { + ::fast_io::throw_nt_error(status); + } + this->handle_ = nullptr; + this->id_ = nullptr; + } + + constexpr void swap(nt_thread &other) noexcept + { + ::std::ranges::swap(handle_, other.handle_); + ::std::ranges::swap(id_, other.id_); + } + + /** + * @note Unlike std::thread::get_id, this method return the const reference. + */ + [[nodiscard]] + constexpr auto &&get_id() const noexcept + { + return this->id_; + } + + [[nodiscard]] + constexpr auto &&native_handle() const noexcept + { + return this->handle_; + } + + /** + * @brief Get the win32 id of the thread. + * @note same as win32 GetCurrentThreadId + */ + constexpr ::std::uint_least32_t get_win32_id() const noexcept + { + auto peb = ::fast_io::win32::nt::nt_current_teb(); + return static_cast<::std::uint_least32_t>(reinterpret_cast<::std::size_t>(peb->ClientId.UniqueThread)); + } + + static constexpr ::std::uint_least32_t hardware_concurrency() + { + ::fast_io::win32::nt::system_basic_information sb{}; + auto status{::fast_io::win32::nt::nt_query_system_information(::fast_io::win32::nt::system_information_class::SystemBasicInformation, + __builtin_addressof(sb), static_cast<::std::uint_least32_t>(sizeof(sb)), nullptr)}; + if (status) [[unlikely]] + { + ::fast_io::throw_nt_error(status); + } + return static_cast<::std::uint_least32_t>(sb.NumberOfProcessors); + } +}; + +namespace this_thread +{ + +template +constexpr auto get_id() -> ::fast_io::win32::nt::nt_thread::id +{ + ::fast_io::win32::nt::thread_basic_information tbi; + ::std::uint_least32_t status{::fast_io::win32::nt::nt_query_information_thread( + reinterpret_cast(-2), // NtCurrentThread + ::fast_io::win32::nt::thread_information_class::ThreadBasicInformation, + __builtin_addressof(tbi), + ::std::uint_least32_t(sizeof(tbi)), + nullptr)}; + if (status) [[unlikely]] + { + ::fast_io::throw_nt_error(status); + } + return tbi.ClientId.UniqueThread; +} + +template +constexpr void sleep_for(::std::chrono::duration const &sleep_duration) +{ + auto val = -static_cast<::std::int_least64_t>(::std::chrono::duration_cast<::std::chrono::microseconds>(sleep_duration).count() * 10); + ::std::uint_least32_t status{::fast_io::win32::nt::nt_delay_execution(false, __builtin_addressof(val))}; + if (status) [[unlikely]] + { + ::fast_io::throw_nt_error(status); + } +} + +template +constexpr void sleep_until(::std::chrono::time_point const &expect_time) +{ + auto const unix_ts = ::std::chrono::duration_cast( + expect_time.time_since_epoch()) + .count(); + auto nt_ts = (unix_ts + 11644473600) * 10000000; + ::std::uint_least32_t status{::fast_io::win32::nt::nt_delay_execution(false, __builtin_addressof(nt_ts))}; + if (status) [[unlikely]] + { + ::fast_io::throw_nt_error(status); + } +} + +} // namespace this_thread + +} // namespace fast_io::win32::nt diff --git a/include/fast_io_hosted/threads/thread/posix.h b/include/fast_io_hosted/threads/thread/posix.h index 73b4b8665..6f70f09be 100644 --- a/include/fast_io_hosted/threads/thread/posix.h +++ b/include/fast_io_hosted/threads/thread/posix.h @@ -1 +1 @@ -#pragma once +#pragma once diff --git a/include/fast_io_hosted/threads/thread/win32.h b/include/fast_io_hosted/threads/thread/win32.h index 73b4b8665..ee3ce2830 100644 --- a/include/fast_io_hosted/threads/thread/win32.h +++ b/include/fast_io_hosted/threads/thread/win32.h @@ -1 +1,226 @@ -#pragma once +#pragma once + +#include +#include +#include +#include +#include +#include +#include "../../../fast_io_dsal/tuple.h" +#include "../../../fast_io_core_impl/allocation/common.h" + +namespace fast_io::win32 +{ + +namespace details +{ + +class win32_thread_start_routine_tuple_allocate_guard +{ +public: + void *ptr_{nullptr}; + + constexpr win32_thread_start_routine_tuple_allocate_guard() = default; + + constexpr win32_thread_start_routine_tuple_allocate_guard(void *ptr) : ptr_{ptr} + {} + + constexpr win32_thread_start_routine_tuple_allocate_guard(win32_thread_start_routine_tuple_allocate_guard const &) noexcept = delete; + constexpr win32_thread_start_routine_tuple_allocate_guard(win32_thread_start_routine_tuple_allocate_guard &&other) noexcept + : ptr_{other.ptr_} + { + other.ptr_ = nullptr; + } + + constexpr ~win32_thread_start_routine_tuple_allocate_guard() + { + if (ptr_ != nullptr) + { + ::fast_io::generic_allocator_adapter<::fast_io::win32_heapalloc_allocator>::deallocate(this->ptr_); + } + } +}; + +template +constexpr ::std::uint_least32_t thread_start_routine(void *args) noexcept(noexcept( + ::std::invoke(::fast_io::get(*reinterpret_cast(args))...))) +{ + [[maybe_unused]] ::fast_io::win32::details::win32_thread_start_routine_tuple_allocate_guard _(args); + ::std::invoke(::fast_io::get(*reinterpret_cast(args))...); + return 0; +} + +template +constexpr auto get_thread_start_routine(::std::index_sequence) noexcept +{ + return ::fast_io::win32::details::thread_start_routine; +} + +} // namespace details + +class win32_thread +{ +public: + using id = ::std::uint_least32_t; + using native_handle_type = void *; + +private: + id id_; + native_handle_type handle_; + +public: + win32_thread() noexcept + : id_{}, handle_{nullptr} + {} + + template + requires(::std::invocable) + constexpr win32_thread(Func &&func, Args &&...args) + { + using start_routine_tuple_type = ::fast_io::tuple; + void *start_routine_tuple{::fast_io::generic_allocator_adapter<::fast_io::win32_heapalloc_allocator>::allocate(sizeof(start_routine_tuple_type))}; +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-braces" +#endif + ::new (start_routine_tuple) start_routine_tuple_type{::std::forward(func), ::std::forward(args)...}; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + auto start_routine = ::fast_io::win32::details::get_thread_start_routine( + ::std::make_index_sequence{}); + this->handle_ = ::fast_io::win32::CreateThread( + nullptr, + 0, + start_routine, + start_routine_tuple, // args of func + 0, + __builtin_addressof(this->id_)); + if (this->handle_ == nullptr) [[unlikely]] + { + ::fast_io::throw_win32_error(); + } + } + + constexpr win32_thread(win32_thread const &) noexcept = delete; + + constexpr win32_thread(win32_thread &&other) noexcept + : id_(other.id_), handle_(other.handle_) + { + other.id_ = 0; + other.handle_ = nullptr; + } + + constexpr ~win32_thread() noexcept + { + if (handle_ != nullptr) + { + if (this->joinable()) [[unlikely]] + { + ::std::terminate(); + } + if (!::fast_io::win32::CloseHandle(this->handle_)) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + } + } + + constexpr win32_thread &operator=(win32_thread const &) noexcept = delete; + + constexpr win32_thread &operator=(win32_thread &&other) noexcept + { + if (this == __builtin_addressof(other)) [[unlikely]] + { + return *this; + } + this->swap(other); + return *this; + } + constexpr bool joinable() const noexcept + { + return this->id_ != 0; + } + + constexpr void join() + { + if (!this->joinable()) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + ::fast_io::win32::WaitForSingleObject(this->handle_, /* INFINITE = */ 0xffffffff); + this->id_ = 0; + } + + constexpr void detach() + { + if (!this->joinable()) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + if (!::fast_io::win32::CloseHandle(this->handle_)) [[unlikely]] + { + ::fast_io::throw_win32_error(); + } + this->handle_ = nullptr; + this->id_ = 0; + } + + constexpr void swap(win32_thread &other) noexcept + { + ::std::ranges::swap(handle_, other.handle_); + ::std::ranges::swap(id_, other.id_); + } + + /** + * @note Unlike std::thread::get_id, this method return the const reference. + */ + [[nodiscard]] + constexpr auto &&get_id() const noexcept + { + return this->id_; + } + + [[nodiscard]] + constexpr auto &&native_handle() const noexcept + { + return this->handle_; + } + + static constexpr ::std::uint_least32_t hardware_concurrency() noexcept + { + ::fast_io::win32::system_info si{}; + ::fast_io::win32::GetSystemInfo(__builtin_addressof(si)); + return si.dwNumberOfProcessors; + } +}; + +namespace this_thread +{ + +constexpr auto get_id() noexcept -> ::fast_io::win32::win32_thread::id +{ + return ::fast_io::win32::GetCurrentThreadId(); +} + +template +constexpr void sleep_for(::std::chrono::duration const &sleep_duration) noexcept +{ + ::fast_io::win32::Sleep(static_cast<::std::uint_least32_t>(::std::chrono::duration_cast<::std::chrono::milliseconds>(sleep_duration).count())); +} + +template +constexpr void sleep_until(::std::chrono::time_point const &expect_time) noexcept +{ + + auto now = Clock::now(); + if (now < expect_time) + { + ::fast_io::win32::Sleep(static_cast<::std::uint_least32_t>( + ::std::chrono::duration_cast<::std::chrono::milliseconds>(expect_time - now).count())); + } +} + +} // namespace this_thread + +} // namespace fast_io::win32