summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Blake <ebb9@byu.net>2008-12-13 21:59:57 -0700
committerEric Blake <ebb9@byu.net>2008-12-15 06:01:23 -0700
commit01c5807a56be04620dbe3ff50f72358ef1ed88ed (patch)
tree813b0f176218ec19395ac212f77710fd65cf0361
parent50aba86fb9b76d2371385d2a042101ee30fda337 (diff)
downloadm4-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--.gitignore2
-rw-r--r--ChangeLog15
-rw-r--r--configure.ac1
-rw-r--r--ltdl/m4/m4-rename.m445
-rw-r--r--m4/output.c65
5 files changed, 119 insertions, 9 deletions
diff --git a/.gitignore b/.gitignore
index 5e8e96cc..00414942 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,7 +24,7 @@ INSTALL
intl
libtool
*.log
-m4-*
+/m4-*
Makefile
Makefile.in
patches
diff --git a/ChangeLog b/ChangeLog
index 5fa0473c..c7a22930 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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"));
}