kabufuda/lib/kabufuda/AsyncIOWin32.cpp

158 lines
4.5 KiB
C++
Raw Normal View History

2018-02-06 17:36:51 -08:00
#include "kabufuda/AsyncIO.hpp"
2018-02-06 01:34:01 -08:00
#include <algorithm>
#include <cstdio>
2018-12-07 21:20:24 -08:00
namespace kabufuda {
2018-02-06 17:36:51 -08:00
#undef min
#undef max
2018-12-07 21:20:24 -08:00
static void ResetOverlapped(OVERLAPPED& aio, DWORD offset = 0) {
aio.Internal = 0;
aio.InternalHigh = 0;
aio.Offset = offset;
aio.OffsetHigh = 0;
2018-02-06 17:36:51 -08:00
}
2021-06-28 15:58:22 -07:00
AsyncIO::AsyncIO(std::string_view filename, bool truncate) {
2018-02-06 17:36:51 -08:00
#if WINDOWS_STORE
2018-12-07 21:20:24 -08:00
CREATEFILE2_EXTENDED_PARAMETERS parms = {};
parms.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
parms.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
parms.dwFileFlags = FILE_FLAG_OVERLAPPED;
m_fh = CreateFile2(filename.data(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
truncate ? CREATE_ALWAYS : OPEN_ALWAYS, &parms);
2018-02-06 17:36:51 -08:00
#else
2021-06-28 15:58:22 -07:00
WStringConv wfilename(filename);
m_fh = CreateFileW(wfilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
2018-12-07 21:20:24 -08:00
truncate ? CREATE_ALWAYS : OPEN_ALWAYS, FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL, nullptr);
2018-02-06 17:36:51 -08:00
#endif
}
2018-12-07 21:20:24 -08:00
AsyncIO::~AsyncIO() {
if (*this) {
if (CancelIoEx(m_fh, nullptr))
waitForCompletion();
CloseHandle(m_fh);
}
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
AsyncIO::AsyncIO(AsyncIO&& other) {
m_fh = other.m_fh;
other.m_fh = INVALID_HANDLE_VALUE;
m_queue = std::move(other.m_queue);
m_maxBlock = other.m_maxBlock;
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
AsyncIO& AsyncIO::operator=(AsyncIO&& other) {
if (*this) {
if (CancelIoEx(m_fh, nullptr))
waitForCompletion();
CloseHandle(m_fh);
}
m_fh = other.m_fh;
other.m_fh = INVALID_HANDLE_VALUE;
m_queue = std::move(other.m_queue);
m_maxBlock = other.m_maxBlock;
return *this;
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
void AsyncIO::_waitForOperation(size_t qIdx) const {
auto& aio = const_cast<AsyncIO*>(this)->m_queue[qIdx];
if (aio.first.hEvent == 0)
return;
GetOverlappedResult(m_fh, &aio.first, &aio.second, TRUE);
CloseHandle(aio.first.hEvent);
aio.first.hEvent = 0;
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
bool AsyncIO::asyncRead(size_t qIdx, void* buf, size_t length, off_t offset) {
OVERLAPPED& aio = m_queue[qIdx].first;
if (aio.hEvent) {
2018-02-06 17:36:51 -08:00
#ifndef NDEBUG
2018-12-07 21:20:24 -08:00
fprintf(stderr, "WARNING: synchronous kabufuda fallback, check access polling\n");
2018-02-06 17:36:51 -08:00
#endif
2018-12-07 21:20:24 -08:00
_waitForOperation(qIdx);
} else {
aio.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
}
ResetOverlapped(aio, DWORD(offset));
m_maxBlock = std::max(m_maxBlock, qIdx + 1);
BOOL res = ReadFile(m_fh, buf, length, nullptr, &aio);
return res == TRUE || GetLastError() == ERROR_IO_PENDING;
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
bool AsyncIO::asyncWrite(size_t qIdx, const void* buf, size_t length, off_t offset) {
OVERLAPPED& aio = m_queue[qIdx].first;
if (aio.hEvent) {
2018-02-06 17:36:51 -08:00
#ifndef NDEBUG
2018-12-07 21:20:24 -08:00
fprintf(stderr, "WARNING: synchronous kabufuda fallback, check access polling\n");
2018-02-06 17:36:51 -08:00
#endif
2018-12-07 21:20:24 -08:00
_waitForOperation(qIdx);
} else {
aio.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
}
ResetOverlapped(aio, DWORD(offset));
m_maxBlock = std::max(m_maxBlock, qIdx + 1);
BOOL res = WriteFile(m_fh, buf, length, nullptr, &aio);
return res == TRUE || GetLastError() == ERROR_IO_PENDING;
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
ECardResult AsyncIO::pollStatus(size_t qIdx, SizeReturn* szRet) const {
auto& aio = const_cast<AsyncIO*>(this)->m_queue[qIdx];
if (aio.first.hEvent == 0) {
if (szRet)
*szRet = aio.second;
return ECardResult::READY;
}
if (GetOverlappedResult(m_fh, &aio.first, &aio.second, FALSE)) {
CloseHandle(aio.first.hEvent);
aio.first.hEvent = 0;
if (szRet)
*szRet = aio.second;
return ECardResult::READY;
} else {
if (GetLastError() == ERROR_IO_INCOMPLETE) {
return ECardResult::BUSY;
} else {
_waitForOperation(qIdx);
return ECardResult::IOERROR;
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
}
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
ECardResult AsyncIO::pollStatus() const {
ECardResult result = ECardResult::READY;
for (auto it = const_cast<AsyncIO*>(this)->m_queue.begin();
it != const_cast<AsyncIO*>(this)->m_queue.begin() + m_maxBlock; ++it) {
auto& aio = *it;
if (aio.first.hEvent == 0)
continue;
if (GetOverlappedResult(m_fh, &aio.first, &aio.second, FALSE)) {
CloseHandle(aio.first.hEvent);
aio.first.hEvent = 0;
} else {
if (GetLastError() == ERROR_IO_INCOMPLETE) {
if (result > ECardResult::BUSY)
result = ECardResult::BUSY;
} else {
_waitForOperation(it - m_queue.cbegin());
if (result > ECardResult::IOERROR)
result = ECardResult::IOERROR;
}
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
}
if (result == ECardResult::READY)
2018-02-06 17:36:51 -08:00
const_cast<AsyncIO*>(this)->m_maxBlock = 0;
2018-12-07 21:20:24 -08:00
return result;
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
void AsyncIO::waitForCompletion() const {
for (size_t i = 0; i < m_maxBlock; ++i)
_waitForOperation(i);
const_cast<AsyncIO*>(this)->m_maxBlock = 0;
2018-02-06 17:36:51 -08:00
}
2018-12-07 21:20:24 -08:00
} // namespace kabufuda