Procházet zdrojové kódy

asyncio: don't report failures on closing read-only files with Windows IoRing.

We still need the task to go through the IoRing, even though the flush
operation we use to get it there will always fail on a read-only file. So
check for this specific case and don't report failure.

Fixes #14878.
Ryan C. Gordon před 1 měsícem
rodič
revize
4df13e8806

+ 13 - 7
src/io/SDL_asyncio.c

@@ -23,20 +23,23 @@
 #include "SDL_sysasyncio.h"
 #include "SDL_asyncio_c.h"
 
-static const char *AsyncFileModeValid(const char *mode)
+static const char *AsyncFileModeValid(const char *mode, bool *readonly)
 {
-    static const struct { const char *valid; const char *with_binary; } mode_map[] = {
-        { "r", "rb" },
-        { "w", "wb" },
-        { "r+","r+b" },
-        { "w+", "w+b" }
+    static const struct { const char *valid; const char *with_binary; bool readonly; } mode_map[] = {
+        { "r", "rb", true },
+        { "w", "wb", false },
+        { "r+","r+b", false },
+        { "w+", "w+b", false }
     };
 
     for (int i = 0; i < SDL_arraysize(mode_map); i++) {
         if (SDL_strcmp(mode, mode_map[i].valid) == 0) {
+            *readonly = mode_map[i].readonly;
             return mode_map[i].with_binary;
         }
     }
+
+    *readonly = false;
     return NULL;
 }
 
@@ -51,7 +54,8 @@ SDL_AsyncIO *SDL_AsyncIOFromFile(const char *file, const char *mode)
         return NULL;
     }
 
-    const char *binary_mode = AsyncFileModeValid(mode);
+    bool readonly = false;
+    const char *binary_mode = AsyncFileModeValid(mode, &readonly);
     if (!binary_mode) {
         SDL_SetError("Unsupported file mode");
         return NULL;
@@ -62,6 +66,8 @@ SDL_AsyncIO *SDL_AsyncIOFromFile(const char *file, const char *mode)
         return NULL;
     }
 
+    asyncio->readonly = readonly;
+
     asyncio->lock = SDL_CreateMutex();
     if (!asyncio->lock) {
         SDL_free(asyncio);

+ 1 - 0
src/io/SDL_sysasyncio.h

@@ -124,6 +124,7 @@ struct SDL_AsyncIO
     SDL_AsyncIOTask tasks;
     SDL_AsyncIOTask *closing;  // The close task, which isn't queued until all pending work for this file is done.
     bool oneshot;  // true if this is a SDL_LoadFileAsync open.
+    bool readonly;  // true if this file is opened read-only.
 };
 
 // This is implemented for various platforms; param validation is done before calling this. Open file, fill in iface and userdata.

+ 8 - 2
src/io/windows/SDL_asyncio_windows_ioring.c

@@ -200,8 +200,14 @@ static SDL_AsyncIOTask *ProcessCQE(WinIoRingAsyncIOQueueData *queuedata, IORING_
                 task = NULL; // it already finished or was too far along to cancel, so we'll pick up the actual results later.
             }
         } else if (FAILED(cqe->ResultCode)) {
-            task->result = SDL_ASYNCIO_FAILURE;
-            // !!! FIXME: fill in task->error.
+            if ((task->type == SDL_ASYNCIO_TASK_CLOSE) && (cqe->ResultCode == E_ACCESSDENIED) && task->asyncio->readonly) {
+                // we push all close requests through as flushes, as there is currently no async close operation and flushing writes to disk is the time-consuming part.
+                // However, flushing a read-only handle generates an error, so we catch this specific situation and ignore it. This approach still makes the task go
+                // through the IoRing so we can handle this all in the same place otherwise. The actual close happens below.
+            } else {
+                task->result = SDL_ASYNCIO_FAILURE;
+                // !!! FIXME: fill in task->error.
+            }
         } else {
             if ((task->type == SDL_ASYNCIO_TASK_WRITE) && (((Uint64) cqe->Information) < task->requested_size)) {
                 task->result = SDL_ASYNCIO_FAILURE;  // it's always a failure on short writes.