nxdk icon indicating copy to clipboard operation
nxdk copied to clipboard

Add overlapped (asynchronous file read/writes) to WinAPI

Open Ryzee119 opened this issue 1 year ago • 0 comments

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;
}

image

Ryzee119 avatar Feb 13 '24 23:02 Ryzee119