From 4d4dfb7b32fc3e9137baf4a9416cf5f503fe4026 Mon Sep 17 00:00:00 2001 From: Eugene Kabanov Date: Wed, 4 Sep 2024 00:15:41 +0300 Subject: [PATCH] Add cross-platform fsync() primitive and improve Direct mode. (#228) --- stew/io2.nim | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/stew/io2.nim b/stew/io2.nim index 2e54d6a..ba5a5ba 100644 --- a/stew/io2.nim +++ b/stew/io2.nim @@ -35,6 +35,7 @@ when defined(windows): FILE_APPEND_DATA = 0x00000004'u32 FILE_FLAG_NO_BUFFERING = 0x20000000'u32 + FILE_FLAG_WRITE_THROUGH = 0x80000000'u32 FILE_ATTRIBUTE_READONLY = 0x00000001'u32 FILE_ATTRIBUTE_DIRECTORY = 0x00000010'u32 @@ -200,6 +201,8 @@ when defined(windows): sideEffect.} proc getTempPathW(nBufferLength: uint32, lpBuffer: WideCString): uint32 {. importc: "GetTempPathW", dynlib: "kernel32", stdcall, sideEffect.} + proc flushFileBuffers(hFile: uint): int32 {. + importc: "FlushFileBuffers", dynlib: "kernel32", stdcall, sideEffect.} const NO_ERROR = IoErrorCode(0) @@ -1024,15 +1027,16 @@ proc openFile*(pathName: string, flags: set[OpenFlags], when defined(linux) or defined(freebsd) or defined(netbsd) or defined(dragonflybsd): if OpenFlags.Direct in flags: - cflags = cflags or O_DIRECT + cflags = cflags or O_DIRECT or O_SYNC if OpenFlags.Inherit notin flags: cflags = cflags or O_CLOEXEC if OpenFlags.NonBlock in flags: cflags = cflags or posix.O_NONBLOCK while true: - let omask = setUmask(0) - let ores = posix.open(cstring(pathName), cflags, Mode(createMode)) + let + omask = setUmask(0) + ores = posix.open(cstring(pathName), cflags, Mode(createMode)) discard setUmask(omask) if ores == -1: let errCode = ioLastError() @@ -1041,7 +1045,7 @@ proc openFile*(pathName: string, flags: set[OpenFlags], else: return err(errCode) else: - when defined(macosx): + when defined(macosx) or defined(macos): if OpenFlags.Direct in flags: while true: let fres = posix.fcntl(cint(ores), F_NOCACHE, 1) @@ -1105,7 +1109,7 @@ proc openFile*(pathName: string, flags: set[OpenFlags], if OpenFlags.NonBlock in flags: dwFlags = dwFlags or FILE_FLAG_OVERLAPPED if OpenFlags.Direct in flags: - dwFlags = dwFlags or FILE_FLAG_NO_BUFFERING + dwFlags = dwFlags or FILE_FLAG_NO_BUFFERING or FILE_FLAG_WRITE_THROUGH if OpenFlags.Inherit in flags: sa.bInheritHandle = 1 @@ -1391,6 +1395,26 @@ proc truncate*(pathName: string, length: int64): IoResult[void] = else: ok() +proc fsync*(handle: IoHandle): IoResult[void] = + ## Ensure that all data for the open file descriptor named by ``handle`` + ## is to be transferred to the storage device associated with the file. + when defined(windows): + let res = flushFileBuffers(uint(handle)) + if res == 0: + return err(ioLastError()) + ok() + else: + while true: + let res = posix.fsync(cint(handle)) + if res == -1: + let errCode = ioLastError() + if errCode == EINTR: + continue + else: + return err(errCode) + else: + return ok() + proc checkFileSize*(value: int64): IoResult[int] = ## Checks if ``value`` fits into supported by Nim string/sequence indexing ## mechanism.