<commit_before>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) <NAME>
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCORO_DETAIL_WIN32_OVERLAPPED_OPERATION_HPP_INCLUDED
#define CPPCORO_DETAIL_WIN32_OVERLAPPED_OPERATION_HPP_INCLUDED

#include <cppcoro/cancellation_registration.hpp>
#include <cppcoro/cancellation_token.hpp>
#include <cppcoro/operation_cancelled.hpp>

#include <cppcoro/detail/win32.hpp>

#include <optional>
#include <system_error>
#include <experimental/coroutine>
#include <cassert>

namespace cppcoro
{
	namespace detail
	{
		class win32_overlapped_operation_base
			: protected detail::win32::io_state
		{
		protected:

			win32_overlapped_operation_base(
				detail::win32::io_state::callback_type* callback) noexcept
				: detail::win32::io_state(callback)
				, m_errorCode(0)
				, m_numberOfBytesTransferred(0)
			{}

			win32_overlapped_operation_base(
				void* pointer,
				detail::win32::io_state::callback_type* callback) noexcept
				: detail::win32::io_state(pointer, callback)
				, m_errorCode(0)
				, m_numberOfBytesTransferred(0)
			{}

			win32_overlapped_operation_base(
				std::uint64_t offset,
				detail::win32::io_state::callback_type* callback) noexcept
				: detail::win32::io_state(offset, callback)
				, m_errorCode(0)
				, m_numberOfBytesTransferred(0)
			{}

			_OVERLAPPED* get_overlapped() noexcept
			{
				return reinterpret_cast<_OVERLAPPED*>(
					static_cast<detail::win32::overlapped*>(this));
			}

			std::size_t get_result()
			{
				if (m_errorCode != 0)
				{
					throw std::system_error{
						static_cast<int>(m_errorCode),
						std::system_category()
					};
				}

				return m_numberOfBytesTransferred;
			}

			detail::win32::dword_t m_errorCode;
			detail::win32::dword_t m_numberOfBytesTransferred;

		};

		template<typename OPERATION>
		class win32_overlapped_operation
			: protected win32_overlapped_operation_base
		{
		protected:

			win32_overlapped_operation() noexcept
				: win32_overlapped_operation_base(
					&win32_overlapped_operation::on_operation_completed)
			{}

			win32_overlapped_operation(void* pointer) noexcept
				: win32_overlapped_operation_base(
					pointer,
					&win32_overlapped_operation::on_operation_completed)
			{}

			win32_overlapped_operation(std::uint64_t offset) noexcept
				: win32_overlapped_operation_base(
					offset,
					&win32_overlapped_operation::on_operation_completed)
			{}

		public:

			bool await_ready() const noexcept { return false; }

			bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine)
			{
				static_assert(std::is_base_of_v<win32_overlapped_operation, OPERATION>);

				m_awaitingCoroutine = awaitingCoroutine;
				return static_cast<OPERATION*>(this)->try_start();
			}

			decltype(auto) await_resume()
			{
				return static_cast<OPERATION*>(this)->get_result();
			}

		private:

			static void on_operation_completed(
				detail::win32::io_state* ioState,
				detail::win32::dword_t errorCode,
				detail::win32::dword_t numberOfBytesTransferred,
				[[maybe_unused]] detail::win32::ulongptr_t completionKey) noexcept
			{
				auto* operation = static_cast<win32_overlapped_operation*>(ioState);
				operation->m_errorCode = errorCode;
				operation->m_numberOfBytesTransferred = numberOfBytesTransferred;
				operation->m_awaitingCoroutine.resume();
			}

			std::experimental::coroutine_handle<> m_awaitingCoroutine;

		};

		template<typename OPERATION>
		class win32_overlapped_operation_cancellable
			: protected win32_overlapped_operation_base
		{
			// ERROR_OPERATION_ABORTED value from <Windows.h>
			static constexpr detail::win32::dword_t error_operation_aborted = 995L;

		protected:

			win32_overlapped_operation_cancellable(cancellation_token&& ct) noexcept
				: win32_overlapped_operation_base(&win32_overlapped_operation_cancellable::on_operation_completed)
				, m_state(ct.is_cancellation_requested() ? state::completed : state::not_started)
				, m_cancellationToken(std::move(ct))
			{
				m_errorCode = error_operation_aborted;
			}

			win32_overlapped_operation_cancellable(
				void* pointer,
				cancellation_token&& ct) noexcept
				: win32_overlapped_operation_base(pointer, &win32_overlapped_operation_cancellable::on_operation_completed)
				, m_state(ct.is_cancellation_requested() ? state::completed : state::not_started)
				, m_cancellationToken(std::move(ct))
			{
				m_errorCode = error_operation_aborted;
			}

			win32_overlapped_operation_cancellable(
				std::uint64_t offset,
				cancellation_token&& ct) noexcept
				: win32_overlapped_operation_base(offset, &win32_overlapped_operation_cancellable::on_operation_completed)
				, m_state(ct.is_cancellation_requested() ? state::completed : state::not_started)
				, m_cancellationToken(std::move(ct))
			{
				m_errorCode = error_operation_aborted;
			}

			win32_overlapped_operation_cancellable(
				win32_overlapped_operation_cancellable&& other) noexcept
				: win32_overlapped_operation_base(std::move(other))
				, m_state(other.m_state.load(std::memory_order_relaxed))
				, m_cancellationToken(std::move(other.m_cancellationToken))
				, m_errorCode(other.m_errorCode)
				, m_numberOfBytesTransferred(other.m_numberOfBytesTransferred)
			{
			}

		public:

			bool await_ready() const noexcept
			{
				return m_state.load(std::memory_order_relaxed) == state::completed;
			}

			bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine)
			{
				static_assert(std::is_base_of_v<win32_overlapped_operation_cancellable, OPERATION>);

				m_awaitingCoroutine = awaitingCoroutine;

				// TRICKY: Register cancellation callback before starting the operation
				// in case the callback registration throws due to insufficient
				// memory. We need to make sure that the logic that occurs after
				// starting the operation is noexcept, otherwise we run into the<commit_msg>Fixed move constructor for win32_overlapped_operation_cancellable. (#80)<commit_after>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) <NAME>
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////
#ifndef CPPCORO_DETAIL_WIN32_OVERLAPPED_OPERATION_HPP_INCLUDED
#define CPPCORO_DETAIL_WIN32_OVERLAPPED_OPERATION_HPP_INCLUDED

#include <cppcoro/cancellation_registration.hpp>
#include <cppcoro/cancellation_token.hpp>
#include <cppcoro/operation_cancelled.hpp>

#include <cppcoro/detail/win32.hpp>

#include <optional>
#include <system_error>
#include <experimental/coroutine>
#include <cassert>

namespace cppcoro
{
	namespace detail
	{
		class win32_overlapped_operation_base
			: protected detail::win32::io_state
		{
		protected:

			win32_overlapped_operation_base(
				detail::win32::io_state::callback_type* callback) noexcept
				: detail::win32::io_state(callback)
				, m_errorCode(0)
				, m_numberOfBytesTransferred(0)
			{}

			win32_overlapped_operation_base(
				void* pointer,
				detail::win32::io_state::callback_type* callback) noexcept
				: detail::win32::io_state(pointer, callback)
				, m_errorCode(0)
				, m_numberOfBytesTransferred(0)
			{}

			win32_overlapped_operation_base(
				std::uint64_t offset,
				detail::win32::io_state::callback_type* callback) noexcept
				: detail::win32::io_state(offset, callback)
				, m_errorCode(0)
				, m_numberOfBytesTransferred(0)
			{}

			_OVERLAPPED* get_overlapped() noexcept
			{
				return reinterpret_cast<_OVERLAPPED*>(
					static_cast<detail::win32::overlapped*>(this));
			}

			std::size_t get_result()
			{
				if (m_errorCode != 0)
				{
					throw std::system_error{
						static_cast<int>(m_errorCode),
						std::system_category()
					};
				}

				return m_numberOfBytesTransferred;
			}

			detail::win32::dword_t m_errorCode;
			detail::win32::dword_t m_numberOfBytesTransferred;

		};

		template<typename OPERATION>
		class win32_overlapped_operation
			: protected win32_overlapped_operation_base
		{
		protected:

			win32_overlapped_operation() noexcept
				: win32_overlapped_operation_base(
					&win32_overlapped_operation::on_operation_completed)
			{}

			win32_overlapped_operation(void* pointer) noexcept
				: win32_overlapped_operation_base(
					pointer,
					&win32_overlapped_operation::on_operation_completed)
			{}

			win32_overlapped_operation(std::uint64_t offset) noexcept
				: win32_overlapped_operation_base(
					offset,
					&win32_overlapped_operation::on_operation_completed)
			{}

		public:

			bool await_ready() const noexcept { return false; }

			bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine)
			{
				static_assert(std::is_base_of_v<win32_overlapped_operation, OPERATION>);

				m_awaitingCoroutine = awaitingCoroutine;
				return static_cast<OPERATION*>(this)->try_start();
			}

			decltype(auto) await_resume()
			{
				return static_cast<OPERATION*>(this)->get_result();
			}

		private:

			static void on_operation_completed(
				detail::win32::io_state* ioState,
				detail::win32::dword_t errorCode,
				detail::win32::dword_t numberOfBytesTransferred,
				[[maybe_unused]] detail::win32::ulongptr_t completionKey) noexcept
			{
				auto* operation = static_cast<win32_overlapped_operation*>(ioState);
				operation->m_errorCode = errorCode;
				operation->m_numberOfBytesTransferred = numberOfBytesTransferred;
				operation->m_awaitingCoroutine.resume();
			}

			std::experimental::coroutine_handle<> m_awaitingCoroutine;

		};

		template<typename OPERATION>
		class win32_overlapped_operation_cancellable
			: protected win32_overlapped_operation_base
		{
			// ERROR_OPERATION_ABORTED value from <Windows.h>
			static constexpr detail::win32::dword_t error_operation_aborted = 995L;

		protected:

			win32_overlapped_operation_cancellable(cancellation_token&& ct) noexcept
				: win32_overlapped_operation_base(&win32_overlapped_operation_cancellable::on_operation_completed)
				, m_state(ct.is_cancellation_requested() ? state::completed : state::not_started)
				, m_cancellationToken(std::move(ct))
			{
				m_errorCode = error_operation_aborted;
			}

			win32_overlapped_operation_cancellable(
				void* pointer,
				cancellation_token&& ct) noexcept
				: win32_overlapped_operation_base(pointer, &win32_overlapped_operation_cancellable::on_operation_completed)
				, m_state(ct.is_cancellation_requested() ? state::completed : state::not_started)
				, m_cancellationToken(std::move(ct))
			{
				m_errorCode = error_operation_aborted;
			}

			win32_overlapped_operation_cancellable(
				std::uint64_t offset,
				cancellation_token&& ct) noexcept
				: win32_overlapped_operation_base(offset, &win32_overlapped_operation_cancellable::on_operation_completed)
				, m_state(ct.is_cancellation_requested() ? state::completed : state::not_started)
				, m_cancellationToken(std::move(ct))
			{
				m_errorCode = error_operation_aborted;
			}

			win32_overlapped_operation_cancellable(
				win32_overlapped_operation_cancellable&& other) noexcept
				: win32_overlapped_operation_base(std::move(other))
				, m_state(other.m_state.load(std::memory_order_relaxed))
				, m_cancellationToken(std::move(other.m_cancellationToken))
			{
				assert(m_errorCode == other.m_errorCode);
				assert(m_numberOfBytesTransferred == other.m_numberOfBytesTransferred);
			}

		public:

			bool await_ready() const noexcept
			{
				return m_state.load(std::memory_order_relaxed) == state::completed;
			}

			bool await_suspend(std::experimental::coroutine_handle<> awaitingCoroutine)
			{
				static_assert(std::is_base_of_v<win32_overlapped_operation_cancellable, OPERATION>);

				m_awaitingCoroutine = awaitingCoroutine;

				// TRICKY: Register cancellation callback before starting the operation
				// in case the callback registration throws due to insufficient
				// memory. We need to make sure that the logic that occurs after
				// starting the operation is noexcept, otherwise we run into the
{'fraction_non_alphanumeric': 0.11812257691990431, 'fraction_numerical': 0.019467128598531717, 'mean_word_length': 4.488456315074695, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1687765', 'n_tokens_mistral': 4865, 'n_tokens_neox': 3954, 'n_words': 552}