File windows.cpp¶
File List > modules > system > windows.cpp
Go to the documentation of this file.
// clang-format off
#include <Windows.h>
#include <TlHelp32.h>
#include <threadpoolapiset.h>
#include <synchapi.h>
// Windows Concurrency Runtime's event is not alertible.
//#include <concrt.h>
// clang-format on
#include <coroutine/windows.h>
#include <gsl/gsl>
#include <cassert>
#include <system_error>
using namespace std;
using namespace gsl;
namespace coro {
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_GAMES)
static_assert(is_move_assignable_v<set_or_cancel> == false);
static_assert(is_move_constructible_v<set_or_cancel> == false);
static_assert(is_copy_assignable_v<set_or_cancel> == false);
static_assert(is_copy_constructible_v<set_or_cancel> == false);
GSL_SUPPRESS(con .4)
void __stdcall wait_event_on_thread_pool(PVOID ctx, BOOLEAN timedout) {
UNREFERENCED_PARAMETER(timedout);
auto coro = coroutine_handle<void>::from_address(ctx);
assert(coro.done() == false);
coro.resume();
}
set_or_cancel::set_or_cancel(HANDLE target_event) noexcept(false)
: hobject{target_event} {
// wait object is used as a storage for the event handle
// until it is going to suspend
}
auto set_or_cancel::unregister() noexcept -> uint32_t {
UnregisterWait(hobject);
const auto ec = GetLastError();
if (ec == ERROR_IO_PENDING) {
// this is expected since we are using INFINITE timeout
return NO_ERROR;
}
return ec;
}
void set_or_cancel::suspend(coroutine_handle<void> coro) noexcept(false) {
// since this point, wo becomes a handle for the request
// this is one-shot event. so use infinite timeout
if (RegisterWaitForSingleObject(addressof(hobject), hobject,
wait_event_on_thread_pool, coro.address(),
INFINITE, WT_EXECUTEONLYONCE) == FALSE) {
throw system_error{gsl::narrow_cast<int>(GetLastError()),
system_category(), "RegisterWaitForSingleObject"};
}
}
#endif
// auto get_threads_of(DWORD pid) noexcept(false) -> enumerable<DWORD> {
// // for current process
// auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
// if (snapshot == INVALID_HANDLE_VALUE)
// co_return;
// auto on_return = gsl::finally([=]() noexcept { CloseHandle(snapshot); });
// THREADENTRY32 entry{};
// entry.dwSize = sizeof(THREADENTRY32);
// for (Thread32First(snapshot, &entry); Thread32Next(snapshot, &entry);) {
// // filter other process's threads
// if (entry.th32OwnerProcessID != pid)
// co_yield entry.th32ThreadID;
// entry.dwSize = sizeof(THREADENTRY32);
// }
// }
GSL_SUPPRESS(con .4)
void continue_on_thread_pool::resume_on_thread_pool(PTP_CALLBACK_INSTANCE, //
PVOID ctx, PTP_WORK work) {
if (auto coro = coroutine_handle<void>::from_address(ctx))
if (coro.done() == false)
coro.resume();
::CloseThreadpoolWork(work); // one-time work item
}
auto continue_on_thread_pool::create_and_submit_work(
coroutine_handle<void> coro) noexcept -> uint32_t {
// just make sure no data loss in `static_cast`
static_assert(sizeof(uint32_t) == sizeof(DWORD));
auto work = ::CreateThreadpoolWork(resume_on_thread_pool, //
coro.address(), nullptr);
if (work == nullptr)
return GetLastError();
SubmitThreadpoolWork(work);
return S_OK;
}
GSL_SUPPRESS(type .1)
void continue_on_apc::resume_on_apc(ULONG_PTR param) {
auto ptr = reinterpret_cast<void*>(param);
if (auto coro = coroutine_handle<void>::from_address(ptr))
coro.resume();
}
GSL_SUPPRESS(type .1)
auto continue_on_apc::queue_user_apc(coroutine_handle<void> coro) noexcept
-> uint32_t {
const auto param = reinterpret_cast<ULONG_PTR>(coro.address());
if (QueueUserAPC(resume_on_apc, this->thread, param) == 0)
return GetLastError();
return S_OK;
}
class section final : CRITICAL_SECTION {
section(const section&) = delete;
section(section&&) = delete;
section& operator=(const section&) = delete;
section& operator=(section&&) = delete;
public:
section() noexcept(false);
~section() noexcept;
bool try_lock() noexcept;
void lock() noexcept(false);
void unlock() noexcept(false);
};
section::section() noexcept(false) {
InitializeCriticalSectionAndSpinCount(this, 0600);
}
section::~section() noexcept {
DeleteCriticalSection(this);
}
bool section::try_lock() noexcept {
return TryEnterCriticalSection(this);
}
void section::lock() noexcept(false) {
EnterCriticalSection(this);
}
void section::unlock() noexcept(false) {
LeaveCriticalSection(this);
}
} // namespace coro