[libglnx] fdio: Add cleanup+flush API for FILE*



commit e30a773f2ca45ed7c0654a7c0b2a6449d3de9af0
Author: Colin Walters <walters verbum org>
Date:   Mon Jul 10 12:40:33 2017 -0400

    fdio: Add cleanup+flush API for FILE*
    
    Mostly in ostree/rpm-ostree, we work in either raw `int fd`, or
    `G{Input,Output}Stream`.  One exception is the rpm-ostree `/etc/passwd`
    handling, which uses `FILE*` since that's what glibc exposes.
    
    And in general, there are use cases for `FILE*`; the raw `GUnixOutputStream` for
    example isn't buffered, and doing so via e.g. `GBufferedOutputStream` means
    allocating *two* GObjects and even worse going through multiple vfuncs for every
    write.
    
    `FILE*` is used heavily in systemd, and provides buffering. It is a bit cheaper
    than gobjects, but has its own trap; by default every operation locks a mutex.
    For more information on that, see `unlocked_stdio(3)`. However, callers can
    avoid that by using e.g. `fwrite_unlocked`, which I plan to do for most users of
    `FILE*` that aren't writing to one of the standard streams like `stdout` etc.

 glnx-fdio.c               |    9 +++++++++
 glnx-fdio.h               |   17 +++++++++++++++++
 tests/test-libglnx-fdio.c |   23 +++++++++++++++++++++++
 3 files changed, 49 insertions(+), 0 deletions(-)
---
diff --git a/glnx-fdio.c b/glnx-fdio.c
index 09aa87f..ffe5400 100644
--- a/glnx-fdio.c
+++ b/glnx-fdio.c
@@ -53,6 +53,15 @@
             sizeof(type) <= 4 ? 10 :                                    \
             sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
 
+gboolean
+glnx_stdio_file_flush (FILE *f, GError **error)
+{
+  if (fflush (f) != 0)
+    return glnx_throw_errno_prefix (error, "fflush");
+  if (ferror (f) != 0)
+    return glnx_throw_errno_prefix (error, "ferror");
+  return TRUE;
+}
 
 /* An implementation of renameat2(..., RENAME_NOREPLACE)
  * with fallback to a non-atomic version.
diff --git a/glnx-fdio.h b/glnx-fdio.h
index f459e93..150b22e 100644
--- a/glnx-fdio.h
+++ b/glnx-fdio.h
@@ -50,6 +50,23 @@ const char *glnx_basename (const char *path)
   return (basename) (path);
 }
 
+/* Utilities for standard FILE* */
+static inline void
+glnx_stdio_file_cleanup (void *filep)
+{
+  FILE *f = filep;
+  if (f)
+    fclose (f);
+}
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(FILE, glnx_stdio_file_cleanup)
+
+/**
+ * glnx_stdio_file_flush:
+ * Call fflush() and check ferror().
+ */
+gboolean
+glnx_stdio_file_flush (FILE *f, GError **error);
+
 typedef struct {
   gboolean initialized;
   gboolean anonymous;
diff --git a/tests/test-libglnx-fdio.c b/tests/test-libglnx-fdio.c
index cee7cb6..d4e6272 100644
--- a/tests/test-libglnx-fdio.c
+++ b/tests/test-libglnx-fdio.c
@@ -160,6 +160,28 @@ test_tmpfile (void)
   g_assert_no_error (local_error);
 }
 
+static void
+test_stdio_file (void)
+{
+  g_autoptr(GError) local_error = NULL;
+  GError **error = &local_error;
+  g_auto(GLnxTmpfile) tmpf = { 0, };
+  if (!glnx_open_anonymous_tmpfile (O_RDWR|O_CLOEXEC, &tmpf, error))
+    goto out;
+
+  g_autoptr(FILE) f = fdopen (tmpf.fd, "w");
+  if (fwrite ("hello", 1, strlen ("hello"), f) != strlen ("hello"))
+    {
+      (void)glnx_throw_errno_prefix (error, "fwrite");
+      goto out;
+    }
+  if (!glnx_stdio_file_flush (f, error))
+    goto out;
+
+ out:
+  g_assert_no_error (local_error);
+}
+
 int main (int argc, char **argv)
 {
   int ret;
@@ -167,6 +189,7 @@ int main (int argc, char **argv)
   g_test_init (&argc, &argv, NULL);
 
   g_test_add_func ("/tmpfile", test_tmpfile);
+  g_test_add_func ("/stdio-file", test_stdio_file);
   g_test_add_func ("/renameat2-noreplace", test_renameat2_noreplace);
   g_test_add_func ("/renameat2-exchange", test_renameat2_exchange);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]