Add an mmap() wrapper called GMappedFile. (#148218, David Schleef, Behdad
authorMatthias Clasen <mclasen@redhat.com>
Sat, 25 Jun 2005 03:38:32 +0000 (03:38 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Sat, 25 Jun 2005 03:38:32 +0000 (03:38 +0000)
2005-06-24  Matthias Clasen  <mclasen@redhat.com>

Add an mmap() wrapper called GMappedFile. (#148218,
David Schleef, Behdad Esfahbod)

* glib/gmappedfile.[hc]: New files.

* configure.in: Check for mmap.

* glib/Makefile.am: Add new files.

* glib/glib.symbols: Add new functions.

* glib/glib.h: Include gmappedfile.h

* tests/mapping-test.c: Tests for GMappedFile.

* tests/Makefile.am: Add new file.

16 files changed:
ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-8
configure.in
docs/reference/ChangeLog
docs/reference/glib/glib-sections.txt
glib/Makefile.am
glib/glib.h
glib/glib.symbols
glib/gmappedfile.c [new file with mode: 0644]
glib/gmappedfile.h [new file with mode: 0644]
po/ChangeLog
po/POTFILES.in
tests/Makefile.am
tests/mapping-test.c [new file with mode: 0644]

index f03c19b..f445ec3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,22 @@
 2005-06-24  Matthias Clasen  <mclasen@redhat.com>
 
+       Add an mmap() wrapper called GMappedFile. (#148218,
+       David Schleef, Behdad Esfahbod)
+       
+       * glib/gmappedfile.[hc]: New files.
+
+       * configure.in: Check for mmap.
+       
+       * glib/Makefile.am: Add new files.
+       
+       * glib/glib.symbols: Add new functions.
+
+       * glib/glib.h: Include gmappedfile.h
+
+       * tests/mapping-test.c: Tests for GMappedFile.
+
+       * tests/Makefile.am: Add new file.
+
        * Makefile.am (DISTCHECK_CONFIGURE_FLAGS): Add --enable-man.
 
 2005-06-24  Tor Lillqvist  <tml@novell.com>
index f03c19b..f445ec3 100644 (file)
@@ -1,5 +1,22 @@
 2005-06-24  Matthias Clasen  <mclasen@redhat.com>
 
+       Add an mmap() wrapper called GMappedFile. (#148218,
+       David Schleef, Behdad Esfahbod)
+       
+       * glib/gmappedfile.[hc]: New files.
+
+       * configure.in: Check for mmap.
+       
+       * glib/Makefile.am: Add new files.
+       
+       * glib/glib.symbols: Add new functions.
+
+       * glib/glib.h: Include gmappedfile.h
+
+       * tests/mapping-test.c: Tests for GMappedFile.
+
+       * tests/Makefile.am: Add new file.
+
        * Makefile.am (DISTCHECK_CONFIGURE_FLAGS): Add --enable-man.
 
 2005-06-24  Tor Lillqvist  <tml@novell.com>
index f03c19b..f445ec3 100644 (file)
@@ -1,5 +1,22 @@
 2005-06-24  Matthias Clasen  <mclasen@redhat.com>
 
+       Add an mmap() wrapper called GMappedFile. (#148218,
+       David Schleef, Behdad Esfahbod)
+       
+       * glib/gmappedfile.[hc]: New files.
+
+       * configure.in: Check for mmap.
+       
+       * glib/Makefile.am: Add new files.
+       
+       * glib/glib.symbols: Add new functions.
+
+       * glib/glib.h: Include gmappedfile.h
+
+       * tests/mapping-test.c: Tests for GMappedFile.
+
+       * tests/Makefile.am: Add new file.
+
        * Makefile.am (DISTCHECK_CONFIGURE_FLAGS): Add --enable-man.
 
 2005-06-24  Tor Lillqvist  <tml@novell.com>
index f03c19b..f445ec3 100644 (file)
@@ -1,5 +1,22 @@
 2005-06-24  Matthias Clasen  <mclasen@redhat.com>
 
+       Add an mmap() wrapper called GMappedFile. (#148218,
+       David Schleef, Behdad Esfahbod)
+       
+       * glib/gmappedfile.[hc]: New files.
+
+       * configure.in: Check for mmap.
+       
+       * glib/Makefile.am: Add new files.
+       
+       * glib/glib.symbols: Add new functions.
+
+       * glib/glib.h: Include gmappedfile.h
+
+       * tests/mapping-test.c: Tests for GMappedFile.
+
+       * tests/Makefile.am: Add new file.
+
        * Makefile.am (DISTCHECK_CONFIGURE_FLAGS): Add --enable-man.
 
 2005-06-24  Tor Lillqvist  <tml@novell.com>
index ca5b892..f75db75 100644 (file)
@@ -501,7 +501,7 @@ AC_HEADER_STDC
 
 # Checks for library functions.
 AC_FUNC_VPRINTF
-
+AC_FUNC_MMAP
 AC_FUNC_ALLOCA
 
 AC_CHECK_FUNCS(atexit on_exit)
index 9b13ec6..579a618 100644 (file)
@@ -1,3 +1,7 @@
+2005-06-24  Matthias Clasen  <mclasen@redhat.com>
+
+       * glib/glib-sections.txt: Add GMappedFile functions.
+
 2005-06-18  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/tmpl/option.sgml (GOptionFlags): document
index b844cd7..4db753b 100644 (file)
@@ -994,6 +994,13 @@ g_dir_rewind
 g_dir_close
 
 <SUBSECTION>
+GMappedFile
+g_mapped_file_new
+g_mapped_file_free
+g_mapped_file_get_length
+g_mapped_file_get_contents
+
+<SUBSECTION>
 g_open
 g_rename
 g_mkdir
@@ -1008,6 +1015,7 @@ g_chmod
 g_access
 g_creat
 g_chdir
+
 <SUBSECTION Private>
 g_file_error_quark
 </SECTION>
index 28e27ed..c7021d0 100644 (file)
@@ -82,6 +82,7 @@ libglib_2_0_la_SOURCES =      \
        glibintl.h              \
        glist.c                 \
        gmain.c                 \
+       gmappedfile.c           \
        gmarkup.c               \
        gmem.c                  \
        gmessages.c             \
@@ -155,6 +156,7 @@ glibsubinclude_HEADERS =   \
        glist.h         \
        gmacros.h       \
        gmain.h         \
+       gmappedfile.h   \
        gmarkup.h       \
        gmem.h          \
        gmessages.h     \
index be06699..787efc1 100644 (file)
@@ -47,6 +47,7 @@
 #include <glib/glist.h>
 #include <glib/gmacros.h>
 #include <glib/gmain.h>
+#include <glib/gmappedfile.h>
 #include <glib/gmarkup.h>
 #include <glib/gmem.h>
 #include <glib/gmessages.h>
index 116acd5..38aeaa4 100644 (file)
@@ -532,6 +532,15 @@ g_timeout_source_new
 #endif
 #endif
 
+#if IN_HEADER(__G_MAPPED_FILE_H__)
+#if IN_FILE(__G_MAPPED_FILE_C__)
+g_mapped_file_new G_GNUC_MALLOC
+g_mapped_file_get_length
+g_mapped_file_get_contents
+g_mapped_file_free
+#endif
+#endif
+
 #if IN_HEADER(__G_MARKUP_H__)
 #if IN_FILE(__G_MARKUP_C__)
 g_markup_error_quark
diff --git a/glib/gmappedfile.c b/glib/gmappedfile.c
new file mode 100644 (file)
index 0000000..33bbd1f
--- /dev/null
@@ -0,0 +1,223 @@
+/* GLIB - Library of useful routines for C programming
+ * gmappedfile.c: Simplified wrapper around the mmap() function.
+ *
+ * Copyright 2005 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <sys/types.h> 
+#include <sys/stat.h> 
+#include <fcntl.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+
+#include "gconvert.h"
+#include "gerror.h"
+#include "gfileutils.h"
+#include "gmappedfile.h"
+#include "gmem.h"
+#include "gmessages.h"
+#include "gstdio.h"
+#include "gstrfuncs.h"
+
+#include "glibintl.h"
+
+#include "galias.h"
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif
+
+struct _GMappedFile 
+{
+  gsize  length;
+  gchar *contents;
+};
+
+/**
+ * g_mapped_file_new:
+ * @filename: The path of the file to load, in the GLib filename encoding
+ * @writable: wether the mapping should be writable
+ * @error: return location for a #GError, or %NULL
+ *
+ * Maps a file into memory. On UNIX, this is using the mmap() function.
+ *
+ * If @writable is %TRUE, the mapped buffer may be modified, otherwise
+ * it is an error to modify the mapped buffer. Modifications to the buffer 
+ * are not visible to other processes mapping the same file, and are not 
+ * written back to the file.
+ *
+ * Note that modifications of the underlying file might affect the contents
+ * of the #GMappedFile. Therefore, mapping should only be used if the file 
+ * will not be modified, or if all modifications of the file are done
+ * atomically (e.g. using g_file_set_contents()). 
+ *
+ * Return value: a newly allocated #GMappedFile which must be freed
+ *    with g_mapped_file_free(), or %NULL if the mapping failed. 
+ *
+ * Since: 2.8
+ */
+GMappedFile *
+g_mapped_file_new (const gchar  *filename,
+                  gboolean      writable,
+                  GError      **error)
+{
+  GMappedFile *file;
+  int fd;
+  struct stat st;
+
+  g_return_val_if_fail (filename != NULL, NULL);
+  g_return_val_if_fail (!error || *error == NULL, NULL);
+
+  fd = g_open (filename, writable ? O_RDWR : O_RDONLY);
+  if (fd == -1)
+    {
+      int save_errno = errno;
+      gchar *display_filename = g_filename_display_name (filename);
+      
+      g_set_error (error,
+                   G_FILE_ERROR,
+                   g_file_error_from_errno (save_errno),
+                   _("Failed to open file '%s': open() failed: %s"),
+                   display_filename, 
+                  g_strerror (save_errno));
+      g_free (display_filename);
+      return NULL;
+    }
+
+  file = g_new0 (GMappedFile, 1);
+
+  if (fstat (fd, &st) == -1)
+    {
+      int save_errno = errno;
+      gchar *display_filename = g_filename_display_name (filename);
+
+      g_set_error (error,
+                   G_FILE_ERROR,
+                   g_file_error_from_errno (save_errno),
+                   _("Failed to get attributes of file '%s': fstat() failed: %s"),
+                   display_filename, 
+                  g_strerror (save_errno));
+      g_free (display_filename);
+      goto out;
+    }
+
+  file->contents = MAP_FAILED;
+
+#ifdef HAVE_MMAP
+  file->length = st.st_size;
+  file->contents = (gchar *) mmap (NULL, st.st_size,
+                                  writable ? PROT_READ|PROT_WRITE : PROT_READ,
+                                  MAP_PRIVATE, fd, 0);
+#endif
+  
+  if (file->contents == MAP_FAILED)
+    {
+      int save_errno = errno;
+      gchar *display_filename = g_filename_display_name (filename);
+      
+      g_set_error (error,
+                  G_FILE_ERROR,
+                  g_file_error_from_errno (save_errno),
+                  _("Failed to map file '%s': mmap() failed: %s"),
+                  display_filename,
+                  g_strerror (save_errno));
+      g_free (display_filename);
+      goto out;
+    }
+
+  close (fd);
+  return file;
+
+ out:
+  close (fd);
+  g_free (file);
+
+  return NULL;
+}
+
+/**
+ * g_mapped_file_get_length:
+ * @file: a #GMappedFile
+ *
+ * Returns the length of the contents of a #GMappedFile.
+ *
+ * Returns: the length of the contents of @file.
+ *
+ * Since: 2.8
+ */
+gsize
+g_mapped_file_get_length (GMappedFile *file)
+{
+  g_return_val_if_fail (file != NULL, 0);
+
+  return file->length;
+}
+
+/**
+ * g_mapped_file_get_contents:
+ * @file: a #GMappedFile
+ *
+ * Returns the contents of a #GMappedFile. 
+ *
+ * Note that the contents may not be zero-terminated,
+ * even if the #GMappedFile is backed by a text file.
+ *
+ * Returns: the contents of @file.
+ *
+ * Since: 2.8
+ */
+gchar *
+g_mapped_file_get_contents (GMappedFile *file)
+{
+  g_return_val_if_fail (file != NULL, NULL);
+
+  return file->contents;
+}
+
+/**
+ * g_mapped_file_free:
+ * @file: a #GMappedFile
+ *
+ * Unmaps the buffer of @file and frees it. 
+ *
+ * For writable, shared mappings, the contents
+ * will be written back to the file at this point.
+ *
+ * Since: 2.8
+ */
+void
+g_mapped_file_free (GMappedFile *file)
+{
+  g_return_if_fail (file != NULL);
+
+#ifdef HAVE_MMAP
+  munmap (file->contents, file->length);
+#endif
+}
+
+
+#define __G_MAPPED_FILE_C__
+#include "galiasdef.c"
diff --git a/glib/gmappedfile.h b/glib/gmappedfile.h
new file mode 100644 (file)
index 0000000..123bc6a
--- /dev/null
@@ -0,0 +1,39 @@
+/* GLIB - Library of useful routines for C programming
+ * gmappedfile.h: Simplified wrapper around the mmap function
+ *
+ * Copyright 2005 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __G_MAPPED_FILE_H__
+#define __G_MAPPED_FILE_H__
+
+#include <glib/gerror.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GMappedFile GMappedFile;
+
+GMappedFile *g_mapped_file_new          (const gchar  *filename,
+                                        gboolean      writable,
+                                        GError      **error) G_GNUC_MALLOC;
+gsize        g_mapped_file_get_length   (GMappedFile  *file);
+gchar       *g_mapped_file_get_contents (GMappedFile  *file);
+void         g_mapped_file_free         (GMappedFile  *file);
+
+G_END_DECLS
+
+#endif /* __G_MAPPED_FILE_H__ */
index 446a91e..439ea32 100644 (file)
@@ -1,3 +1,7 @@
+2005-06-24  Matthias Clasen  <mclasen@redhat.com>
+
+       * POTFILES.in: Add gmappedfile.c
+
 2005-06-22  Abel Cheung  <maddog@linuxhall.org>
 
        * zh_TW.po: Fix language team reference.
index 1f11e60..cea41eb 100644 (file)
@@ -3,6 +3,7 @@ glib/gdir.c
 glib/gfileutils.c
 glib/giochannel.c
 glib/giowin32.c
+glib/gmappedfile.c
 glib/gmarkup.c
 glib/gshell.c
 glib/gspawn-win32.c
index 6e41a21..b30c2cf 100644 (file)
@@ -77,6 +77,7 @@ test_programs =                                       \
        keyfile-test                            \
        list-test                               \
        mainloop-test                           \
+       mapping-test                            \
        markup-escape-test                      \
        module-test                             \
        node-test                               \
@@ -133,6 +134,7 @@ keyfile_test_LDADD = $(progs_ldadd)
 list_test_LDADD = $(progs_ldadd)
 mainloop_test_LDADD = $(thread_ldadd)
 markup_test_LDADD = $(progs_ldadd)
+mapping_test_LDADD = $(progs_ldadd)
 markup_escape_test_LDADD = $(progs_ldadd)
 module_test_LDADD = $(module_ldadd) $(module_test_exp)
 module_test_LDFLAGS = $(G_MODULE_LDFLAGS)
diff --git a/tests/mapping-test.c b/tests/mapping-test.c
new file mode 100644 (file)
index 0000000..cfe0c3b
--- /dev/null
@@ -0,0 +1,233 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 2005 Matthias Clasen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include "glib.h"
+
+static gchar *dir, *filename, *displayname, *childname;
+
+static gboolean stop = FALSE;
+
+static void
+handle_usr1 (int signum)
+{
+  stop = TRUE;
+}
+
+static gboolean
+check_stop (gpointer data)
+{
+  GMainLoop *loop = data;
+
+  if (stop)
+    g_main_loop_quit (loop);
+
+  return TRUE;
+}
+
+static void
+write_or_die (const gchar *filename,
+             const gchar *contents,
+             gssize       length)
+{
+  GError *error = NULL;
+  gchar *displayname;    
+
+  if (!g_file_set_contents (filename, contents, length, &error)) 
+    {
+      displayname = g_filename_display_name (childname);
+      g_print ("failed to write '%s': %s\n", 
+              displayname, error->message);
+      exit (1);
+    }
+}
+
+static GMappedFile *
+map_or_die (const gchar *filename,
+           gboolean     writable)
+{
+  GError *error = NULL;
+  GMappedFile *map;
+  gchar *displayname;
+
+  map = g_mapped_file_new (filename, writable, &error);
+  if (!map)
+    {
+      displayname = g_filename_display_name (childname);
+      g_print ("failed to map '%s' non-writable, shared: %s\n", 
+              displayname, error->message);
+      exit (1);
+    }
+
+  return map;
+}
+           
+static int
+child_main (int argc, char *argv[])
+{
+  GMappedFile *map;
+  GMainLoop *loop;
+
+  map = map_or_die (filename, FALSE);
+  
+  loop = g_main_loop_new (NULL, FALSE);
+
+  signal (SIGUSR1, handle_usr1);
+  g_idle_add (check_stop, loop);
+  g_main_loop_run (loop);
+
+  write_or_die (childname, 
+               g_mapped_file_get_contents (map),
+               g_mapped_file_get_length (map));
+
+  return 0;
+}
+
+static void
+test_mapping (void)
+{
+  GMappedFile *map;
+
+  write_or_die (filename, "ABC", -1);
+
+  map = map_or_die (filename, FALSE);
+  g_assert (g_mapped_file_get_length (map) == 3);
+  g_mapped_file_free (map);
+
+  map = map_or_die (filename, TRUE);
+  g_assert (g_mapped_file_get_length (map) == 3);
+  g_mapped_file_free (map);
+}
+
+static void 
+test_private (void)
+{
+  GError *error = NULL;
+  GMappedFile *map;
+  gchar *buffer;
+  gsize len;
+
+  write_or_die (filename, "ABC", -1);
+  map = map_or_die (filename, TRUE);
+
+  buffer = (gchar *)g_mapped_file_get_contents (map);
+  buffer[0] = '1';
+  buffer[1] = '2';
+  buffer[2] = '3';
+  g_mapped_file_free (map);
+
+  if (!g_file_get_contents (filename, &buffer, &len, &error))
+    {
+      g_print ("failed to read '%s': %s\n", 
+              displayname, error->message);
+      exit (1);
+      
+    }
+  g_assert (len == 3);
+  g_assert (strcmp (buffer, "ABC") == 0);
+  g_free (buffer);
+
+}
+
+static void
+test_child_private (gchar *argv0)
+{
+  GError *error = NULL;
+  GMappedFile *map;
+  gchar *buffer;
+  gsize len;
+  gchar *child_argv[3];
+  GPid  child_pid;
+  
+  write_or_die (filename, "ABC", -1);
+  map = map_or_die (filename, TRUE);
+
+  child_argv[0] = argv0;
+  child_argv[1] = "mapchild";
+  child_argv[2] = NULL;
+  if (!g_spawn_async (dir, child_argv, NULL,
+                     0, NULL, NULL, &child_pid, &error))
+    {
+      g_print ("failed to spawn child: %s\n", 
+              error->message);
+      exit (1);            
+    }
+
+  /* give the child some time to set up its mapping */
+  sleep (2);
+
+  buffer = (gchar *)g_mapped_file_get_contents (map);
+  buffer[0] = '1';
+  buffer[1] = '2';
+  buffer[2] = '3';
+  g_mapped_file_free (map);
+
+  kill (child_pid, SIGUSR1);
+
+  /* give the child some time to write the file */
+  sleep (2);
+
+  if (!g_file_get_contents (childname, &buffer, &len, &error))
+    {
+      gchar *name;
+
+      name = g_filename_display_name (childname);
+      g_print ("failed to read '%s': %s\n", name, error->message);
+      exit (1);      
+    }
+  g_assert (len == 3);
+  g_assert (strcmp (buffer, "ABC") == 0);
+  g_free (buffer);
+}
+
+static int 
+parent_main (int   argc,
+            char *argv[])
+{
+  /* test mapping with various flag combinations */
+  test_mapping ();
+
+  /* test private modification */
+  test_private ();
+
+  /* test multiple clients, non-shared */
+  test_child_private (argv[0]);
+
+  return 0;
+}
+
+int
+main (int argc, 
+      char *argv[])
+{
+  dir = g_get_current_dir ();
+  filename = g_build_filename (dir, "maptest", NULL);
+  displayname = g_filename_display_name (filename);
+  childname = g_build_filename (dir, "mapchild", NULL);
+
+  if (argc > 1)
+    return child_main (argc, argv);
+  else 
+    return parent_main (argc, argv);
+}