nxdk
nxdk copied to clipboard
Add overlapped (asynchronous file read/writes) to WinAPI
Asynchronous fileio has some limitations ref https://learn.microsoft.com/en-US/previous-versions/troubleshoot/windows/win32/asynchronous-disk-io-synchronous and https://github.com/JayFoxRox/nxdk/pull/45
Asynchronous file read/writes have these requirements generally:
- They must use FILE_FLAG_NO_BUFFERING which has size and alignemnt requirements of the buffer (DWORD alignment and multiple of sector size)
- Writes that increase the file size must preallocate the space.
If these are not met the request will either fail or complete synchronously
Overlapped structure members seemed a bit odd to me when I initially looked at this but this was my reference https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped
Some test code below which demonstrates async read and write as per win32 docs
#include <hal/debug.h>
#include <hal/video.h>
#include <windows.h>
#include <nxdk/mount.h>
#include <assert.h>
// Async buffer should be DWORD aligned and a multiple of the sector size (512 bytes for HDD, 2048 bytes for DVD normally)
__attribute__((aligned(sizeof(DWORD)))) CHAR buffer[32 * 1024 * 1024];
int main(void)
{
CONST CHAR *lpFileName = "E:\\test.bin";
DWORD lpNumberOfBytesTransferred;
HANDLE hFile;
XVideoSetMode(640, 480, 32, REFRESH_DEFAULT);
nxMountDrive('E', "\\Device\\Harddisk0\\Partition1\\");
OVERLAPPED overlapped = {
.Offset = 0,
.OffsetHigh = 0,
.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)};
/* ASYNC WRITE */
hFile = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL);
assert(hFile != INVALID_HANDLE_VALUE);
// Any write operation to a file that extends its length will be synchronous, so pre-allocate its length to ensure it is asynchronous
assert(SetFilePointer(hFile, sizeof(buffer), NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
assert(SetEndOfFile(hFile));
assert(SetFilePointer(hFile, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
// An asynchronous write will return false, but the error code will be ERROR_IO_PENDING.
assert(WriteFile(hFile, (LPCVOID)buffer, sizeof(buffer), NULL, &overlapped) == FALSE);
assert(GetLastError() == ERROR_IO_PENDING);
debugPrint("Async write started to %s\n", lpFileName);
// Wait for the write to complete asynchronously
assert(GetOverlappedResult(hFile, &overlapped, &lpNumberOfBytesTransferred, TRUE));
debugPrint("WriteFile completed successfully. Wrote %lu bytes\n", lpNumberOfBytesTransferred);
CloseHandle(hFile);
/* ASYNC READ */
hFile = CreateFile(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL);
assert(hFile != INVALID_HANDLE_VALUE);
// An asynchronous read will return false, but the error code will be ERROR_IO_PENDING.
assert(ReadFile(hFile, (LPVOID)buffer, sizeof(buffer), NULL, &overlapped) == FALSE);
assert(GetLastError() == ERROR_IO_PENDING);
debugPrint("Async read started to %s\n", lpFileName);
// Wait for the read to complete asynchronously
assert(GetOverlappedResult(hFile, &overlapped, &lpNumberOfBytesTransferred, TRUE));
debugPrint("ReadFile completed successfully. Read %lu bytes\n", lpNumberOfBytesTransferred);
while (1)
{
Sleep(2000);
}
return 0;
}