diff options
author | Eric Blake <ebb9@byu.net> | 2008-12-13 21:59:57 -0700 |
---|---|---|
committer | Eric Blake <ebb9@byu.net> | 2008-12-15 06:01:23 -0700 |
commit | 01c5807a56be04620dbe3ff50f72358ef1ed88ed (patch) | |
tree | 813b0f176218ec19395ac212f77710fd65cf0361 | |
parent | 50aba86fb9b76d2371385d2a042101ee30fda337 (diff) | |
download | m4-01c5807a56be04620dbe3ff50f72358ef1ed88ed.tar.gz |
Cache most recently spilled diversion.
* m4/output.c (tmp_file, tmp_file_owner): New variables, for
1-deep cache of spilled diversions.
(m4_tmpfile): Open in append mode, since we might revisit
diversion without closing it now.
(m4_tmpopen): Check cache first.
(m4_tmpclose): Update cache, rather than closing. Add parameter.
(m4_tmpremove): Close cache before removing.
(m4_tmprename): Deal with open files when renaming.
(m4_output_exit): Close cache before exiting.
(make_room_for, m4_make_diversion, insert_diversion_helper):
Adjust callers.
* ltdl/m4/m4-rename.m4 (M4_RENAME): New file.
* configure.ac (M4_RENAME): Invoke it.
Signed-off-by: Eric Blake <ebb9@byu.net>
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | ChangeLog | 15 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | ltdl/m4/m4-rename.m4 | 45 | ||||
-rw-r--r-- | m4/output.c | 65 |
5 files changed, 119 insertions, 9 deletions
@@ -24,7 +24,7 @@ INSTALL intl libtool *.log -m4-* +/m4-* Makefile Makefile.in patches @@ -1,5 +1,20 @@ 2008-12-15 Eric Blake <ebb9@byu.net> + Cache most recently spilled diversion. + * m4/output.c (tmp_file, tmp_file_owner): New variables, for + 1-deep cache of spilled diversions. + (m4_tmpfile): Open in append mode, since we might revisit + diversion without closing it now. + (m4_tmpopen): Check cache first. + (m4_tmpclose): Update cache, rather than closing. Add parameter. + (m4_tmpremove): Close cache before removing. + (m4_tmprename): Deal with open files when renaming. + (m4_output_exit): Close cache before exiting. + (make_room_for, m4_make_diversion, insert_diversion_helper): + Adjust callers. + * ltdl/m4/m4-rename.m4 (M4_RENAME): New file. + * configure.ac (M4_RENAME): Invoke it. + Correctly track size of in-memory diversions. * m4/output.c (insert_diversion_helper): Correctly track total in-memory diversion size after undivert. diff --git a/configure.ac b/configure.ac index 1aa9397a..d8962cc6 100644 --- a/configure.ac +++ b/configure.ac @@ -162,6 +162,7 @@ M4_ERROR M4_GETOPT M4_OBSTACK M4_REGEX +M4_RENAME ## ------------------------ ## diff --git a/ltdl/m4/m4-rename.m4 b/ltdl/m4/m4-rename.m4 new file mode 100644 index 00000000..892032b8 --- /dev/null +++ b/ltdl/m4/m4-rename.m4 @@ -0,0 +1,45 @@ +# -*- Autoconf -*- +# m4-rename.m4 -- Test the abilities of rename. +# Written by Eric Blake <ebb9@byu.net> +# +# Copyright (C) 2008 Free Software Foundation, Inc +# +# This file is part of GNU M4. +# +# GNU M4 is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# GNU M4 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# serial 1 + +# M4_RENAME +# --------- +# Detect platforms like mingw where rename can't move open files. +AC_DEFUN([M4_RENAME], +[AC_CACHE_CHECK([whether an open file can be renamed], + [M4_cv_func_rename_open_file_works], + [AC_RUN_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], + [FILE *f = fopen ("conftest.1", "w+"); + int result = rename ("conftest.1", "conftest.2"); + fclose (f); remove ("conftest.1"); remove ("conftest.2"); + return result;])], + [M4_cv_func_rename_open_file_works=yes], + [M4_cv_func_rename_open_file_works=no], + [M4_cv_func_rename_open_file_works='guessing no'])]) +if test "$M4_cv_func_rename_open_file_works" = yes ; then + M4_rename_open_works=1 +else + M4_rename_open_works=0 +fi +AC_DEFINE_UNQUOTED([RENAME_OPEN_FILE_WORKS], [$M4_rename_open_works], + [Define to 1 if a file can be renamed while open, or to 0 if not.]) +]) diff --git a/m4/output.c b/m4/output.c index 1a97e3a8..2f00902c 100644 --- a/m4/output.c +++ b/m4/output.c @@ -112,6 +112,11 @@ static size_t output_unused; /* Temporary directory holding all spilled diversion files. */ static m4_temp_dir *output_temp_dir; +/* Cache of most recently used spilled diversion file. */ +static FILE *tmp_file; + +/* Diversion that owns tmp_file, or 0. */ +static int tmp_file_owner; /* Internal routines. */ @@ -214,7 +219,7 @@ m4_tmpfile (m4 *context, int divnum) } name = m4_tmpname (divnum); register_temp_file (output_temp_dir, name); - file = fopen_temp (name, O_BINARY ? "wb+" : "w+"); + file = fopen_temp (name, O_BINARY ? "ab+" : "a+"); if (file == NULL) { unregister_temp_file (output_temp_dir, name); @@ -232,9 +237,17 @@ m4_tmpfile (m4 *context, int divnum) static FILE * m4_tmpopen (m4 *context, int divnum) { - const char *name = m4_tmpname (divnum); + const char *name; FILE *file; + if (tmp_file_owner == divnum) + { + if (fseeko (tmp_file, 0, SEEK_SET) != 0) + m4_error (context, EXIT_FAILURE, errno, NULL, + _("cannot seek to beginning of diversion")); + return tmp_file; + } + name = m4_tmpname (divnum); file = fopen_temp (name, O_BINARY ? "ab+" : "a+"); if (file == NULL) m4_error (context, EXIT_FAILURE, errno, NULL, @@ -249,17 +262,36 @@ m4_tmpopen (m4 *context, int divnum) return file; } -/* Close, but don't delete, a temporary FILE. */ +/* Close, but don't delete, a temporary FILE for diversion DIVNUM. To + reduce the I/O overhead of repeatedly opening and closing the same + file, this implementation caches the most recent spilled diversion. + On the other hand, keeping every spilled diversion open would run + into EMFILE limits. */ static int -m4_tmpclose (FILE *file) +m4_tmpclose (FILE *file, int divnum) { - return close_stream_temp (file); + int result = 0; + if (divnum != tmp_file_owner) + { + if (tmp_file_owner) + result = close_stream_temp (tmp_file); + tmp_file = file; + tmp_file_owner = divnum; + } + return result; } /* Delete a closed temporary FILE for diversion DIVNUM. */ static int m4_tmpremove (int divnum) { + if (divnum == tmp_file_owner) + { + int result = close_stream_temp (tmp_file); + if (result) + return result; + tmp_file_owner = 0; + } return cleanup_temp_file (output_temp_dir, m4_tmpname (divnum)); } @@ -273,6 +305,21 @@ m4_tmprename (m4 *context, int oldnum, int newnum) char *oldname = xstrdup (m4_tmpname (oldnum)); const char *newname = m4_tmpname (newnum); register_temp_file (output_temp_dir, newname); + if (oldnum == tmp_file_owner) + { + /* Be careful of mingw, which can't rename an open file. */ + if (RENAME_OPEN_FILE_WORKS) + tmp_file_owner = newnum; + else + { + if (close_stream_temp (tmp_file)) + m4_error (context, EXIT_FAILURE, errno, NULL, + _("cannot close temporary file for diversion")); + tmp_file_owner = 0; + } + } + /* Either it is safe to rename an open file, or no one should have + oldname open at this point. */ if (rename (oldname, newname)) m4_error (context, EXIT_FAILURE, errno, NULL, _("cannot create temporary file for diversion")); @@ -305,6 +352,8 @@ m4_output_exit (void) as an atexit handler, and it must not traverse stale memory. */ gl_oset_t table = diversion_table; assert (gl_oset_size (diversion_table) == 0); + if (tmp_file_owner) + m4_tmpremove (tmp_file_owner); diversion_table = NULL; gl_oset_free (table); obstack_free (&diversion_storage, NULL); @@ -415,7 +464,7 @@ make_room_for (m4 *context, size_t length) { FILE *file = selected_diversion->u.file; selected_diversion->u.file = NULL; - if (m4_tmpclose (file) != 0) + if (m4_tmpclose (file, selected_diversion->divnum) != 0) m4_error (context, 0, errno, NULL, _("cannot close temporary file for diversion")); } @@ -715,7 +764,7 @@ m4_make_diversion (m4 *context, int divnum) assert (output_diversion->divnum != 0); FILE *file = output_diversion->u.file; output_diversion->u.file = NULL; - if (m4_tmpclose (file) != 0) + if (m4_tmpclose (file, output_diversion->divnum) != 0) m4_error (context, 0, errno, NULL, _("cannot close temporary file for diversion")); } @@ -905,7 +954,7 @@ insert_diversion_helper (m4 *context, m4_diversion *diversion, bool escaped) FILE *file = diversion->u.file; diversion->u.file = NULL; diversion->used = 0; - if (m4_tmpclose (file) != 0) + if (m4_tmpclose (file, diversion->divnum) != 0) m4_error (context, 0, errno, NULL, _("cannot clean temporary file for diversion")); } |