From: Matthias Clasen Date: Sat, 25 Jun 2005 03:38:32 +0000 (+0000) Subject: Add an mmap() wrapper called GMappedFile. (#148218, David Schleef, Behdad X-Git-Url: http://git.openbox.org/?a=commitdiff_plain;h=748c2302056a6f447aa2aac4a181d42281624bdd;p=dana%2Fcg-glib.git Add an mmap() wrapper called GMappedFile. (#148218, David Schleef, Behdad 2005-06-24 Matthias Clasen 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. --- diff --git a/ChangeLog b/ChangeLog index f03c19b5..f445ec35 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,22 @@ 2005-06-24 Matthias Clasen + 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 diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index f03c19b5..f445ec35 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,5 +1,22 @@ 2005-06-24 Matthias Clasen + 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 diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index f03c19b5..f445ec35 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,5 +1,22 @@ 2005-06-24 Matthias Clasen + 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 diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index f03c19b5..f445ec35 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,5 +1,22 @@ 2005-06-24 Matthias Clasen + 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 diff --git a/configure.in b/configure.in index ca5b8927..f75db75f 100644 --- a/configure.in +++ b/configure.in @@ -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) diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 9b13ec6b..579a6187 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,7 @@ +2005-06-24 Matthias Clasen + + * glib/glib-sections.txt: Add GMappedFile functions. + 2005-06-18 Matthias Clasen * glib/tmpl/option.sgml (GOptionFlags): document diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index b844cd7e..4db753b5 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -993,6 +993,13 @@ g_dir_read_name g_dir_rewind g_dir_close + +GMappedFile +g_mapped_file_new +g_mapped_file_free +g_mapped_file_get_length +g_mapped_file_get_contents + g_open g_rename @@ -1008,6 +1015,7 @@ g_chmod g_access g_creat g_chdir + g_file_error_quark diff --git a/glib/Makefile.am b/glib/Makefile.am index 28e27ede..c7021d08 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -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 \ diff --git a/glib/glib.h b/glib/glib.h index be06699e..787efc12 100644 --- a/glib/glib.h +++ b/glib/glib.h @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include diff --git a/glib/glib.symbols b/glib/glib.symbols index 116acd57..38aeaa4c 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -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 index 00000000..33bbd1f7 --- /dev/null +++ b/glib/gmappedfile.c @@ -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 +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_MMAP +#include +#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 index 00000000..123bc6af --- /dev/null +++ b/glib/gmappedfile.h @@ -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 + +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__ */ diff --git a/po/ChangeLog b/po/ChangeLog index 446a91e4..439ea322 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,7 @@ +2005-06-24 Matthias Clasen + + * POTFILES.in: Add gmappedfile.c + 2005-06-22 Abel Cheung * zh_TW.po: Fix language team reference. diff --git a/po/POTFILES.in b/po/POTFILES.in index 1f11e60a..cea41eb8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 diff --git a/tests/Makefile.am b/tests/Makefile.am index 6e41a21b..b30c2cf7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 00000000..cfe0c3b2 --- /dev/null +++ b/tests/mapping-test.c @@ -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 +#include +#include +#include +#include + +#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); +}