On Tue, Dec 07, 2021 at 02:46:49AM +0000, Neeraj Singh via GitGitGadget wrote:
From: Neeraj Singh <redacted>
[snip]
quoted hunk ↗ jump to hunk
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -329,6 +329,9 @@ int mingw_getpagesize(void);
#define getpagesize mingw_getpagesize
#endif
+int win32_fsync_no_flush(int fd);
+#define fsync_no_flush win32_fsync_no_flush
+
struct rlimit {
unsigned int rlim_cur;
};diff --git a/compat/win32/flush.c b/compat/win32/flush.c
new file mode 100644
index 00000000000..75324c24ee7
--- /dev/null
+++ b/compat/win32/flush.c
@@ -0,0 +1,28 @@
+#include "../../git-compat-util.h"
+#include <winternl.h>
+#include "lazyload.h"
+
+int win32_fsync_no_flush(int fd)
+{
+ IO_STATUS_BLOCK io_status;
+
+#define FLUSH_FLAGS_FILE_DATA_ONLY 1
+
+ DECLARE_PROC_ADDR(ntdll.dll, NTSTATUS, NtFlushBuffersFileEx,
+ HANDLE FileHandle, ULONG Flags, PVOID Parameters, ULONG ParameterSize,
+ PIO_STATUS_BLOCK IoStatusBlock);
+
+ if (!INIT_PROC_ADDR(NtFlushBuffersFileEx)) {
+ errno = ENOSYS;
+ return -1;
+ }
I'm wondering whether it would make sense to fall back to fsync(3P) in
case we cannot use writeout-only, but I see that were doing essentially
that in `fsync_or_die()`. There is no indicator to the user though that
writeout-only doesn't work -- do we want to print a one-time warning?
quoted hunk ↗ jump to hunk
+ memset(&io_status, 0, sizeof(io_status));
+ if (NtFlushBuffersFileEx((HANDLE)_get_osfhandle(fd), FLUSH_FLAGS_FILE_DATA_ONLY,
+ NULL, 0, &io_status)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
[snip]
quoted hunk ↗ jump to hunk
diff --git a/wrapper.c b/wrapper.c
index 36e12119d76..1c5f2c87791 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -546,6 +546,62 @@ int xmkstemp_mode(char *filename_template, int mode)
return fd;
}
+int git_fsync(int fd, enum fsync_action action)
+{
+ switch (action) {
+ case FSYNC_WRITEOUT_ONLY:
+
+#ifdef __APPLE__
+ /*
+ * on macOS, fsync just causes filesystem cache writeback but does not
+ * flush hardware caches.
+ */
+ return fsync(fd);
Below we're looping around `EINTR` -- are Apple systems never returning
it?
Patrick
quoted hunk ↗ jump to hunk
+#endif
+
+#ifdef HAVE_SYNC_FILE_RANGE
+ /*
+ * On linux 2.6.17 and above, sync_file_range is the way to issue
+ * a writeback without a hardware flush. An offset of 0 and size of 0
+ * indicates writeout of the entire file and the wait flags ensure that all
+ * dirty data is written to the disk (potentially in a disk-side cache)
+ * before we continue.
+ */
+
+ return sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WAIT_BEFORE |
+ SYNC_FILE_RANGE_WRITE |
+ SYNC_FILE_RANGE_WAIT_AFTER);
+#endif
+
+#ifdef fsync_no_flush
+ return fsync_no_flush(fd);
+#endif
+
+ errno = ENOSYS;
+ return -1;
+
+ case FSYNC_HARDWARE_FLUSH:
+ /*
+ * On some platforms fsync may return EINTR. Try again in this
+ * case, since callers asking for a hardware flush may die if
+ * this function returns an error.
+ */
+ for (;;) {
+ int err;
+#ifdef __APPLE__
+ err = fcntl(fd, F_FULLFSYNC);
+#else
+ err = fsync(fd);
+#endif
+ if (err >= 0 || errno != EINTR)
+ return err;
+ }
+
+ default:
+ BUG("unexpected git_fsync(%d) call", action);
+ }
+}
+
static int warn_if_unremovable(const char *op, const char *file, int rc)
{
int err;diff --git a/write-or-die.c b/write-or-die.c
index 0b1ec8190b6..0702acdd5e8 100644
--- a/write-or-die.c
+++ b/write-or-die.c
@@ -57,10 +57,12 @@ void fprintf_or_die(FILE *f, const char *fmt, ...)
void fsync_or_die(int fd, const char *msg)
{
- while (fsync(fd) < 0) {
- if (errno != EINTR)
- die_errno("fsync error on '%s'", msg);
- }
+ if (fsync_method == FSYNC_METHOD_WRITEOUT_ONLY &&
+ git_fsync(fd, FSYNC_WRITEOUT_ONLY) >= 0)
+ return;
+
+ if (git_fsync(fd, FSYNC_HARDWARE_FLUSH) < 0)
+ die_errno("fsync error on '%s'", msg);
}
void write_or_die(int fd, const void *buf, size_t count)--
gitgitgadget