From cf4599bd942e9b2de929880ec8c8a3a812657e62 Mon Sep 17 00:00:00 2001 From: csilvers Date: Fri, 22 Jun 2007 00:58:05 +0000 Subject: [PATCH] Thu Jun 21 14:02:32 2007 Google Inc. * ctemplate: version 0.6.1 release * Bugfix: data corruption bug with >2 template modifiers (jmacgill) * Bugfix: syntax error in template-namelist: configure-bug (csilvers) * Bugfix: improve lock hygenie to avoid potential deadlock (csilvers) --- ChangeLog | 7 + INSTALL | 70 ++++----- Makefile.am | 2 +- Makefile.in | 22 +-- configure | 24 +-- configure.ac | 3 +- doc/example.html | 69 +++++++++ ltmain.sh | 65 ++++++-- packages/deb/changelog | 6 + src/base/mutex.cc | 79 ---------- src/base/mutex.h | 78 ++++++++-- src/google/template.h.in | 4 + src/google/template_from_string.h.in | 16 +- src/make_tpl_varnames_h.cc | 2 + src/template.cc | 165 +++++++++++++-------- src/template_from_string.cc | 44 +++--- src/template_namelist.cc | 2 + src/tests/template_from_string_unittest.cc | 22 +++ src/tests/template_unittest.cc | 18 +++ 19 files changed, 436 insertions(+), 262 deletions(-) delete mode 100644 src/base/mutex.cc diff --git a/ChangeLog b/ChangeLog index d2b0c15..42fe27d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -51,3 +51,10 @@ Sat Jun 9 22:34:52 2007 Google Inc. * New modifiers for url-escaping, attribute-cleansing, etc (ribrdb) * Annotations now include modifier information (csilvers) * Support embedded NULs in template names and values (csilvers) + +Thu Jun 21 14:02:32 2007 Google Inc. + + * ctemplate: version 0.6.1 release + * Bugfix: data corruption bug with >2 template modifiers (jmacgill) + * Bugfix: syntax error in template-namelist: configure-bug (csilvers) + * Bugfix: improve lock hygenie to avoid potential deadlock (csilvers) diff --git a/INSTALL b/INSTALL index 23e5f25..316e397 100644 --- a/INSTALL +++ b/INSTALL @@ -1,16 +1,14 @@ -Installation Instructions -************************* +Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free -Software Foundation, Inc. - -This file is free documentation; the Free Software Foundation gives + This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. + Basic Installation ================== -These are generic installation instructions. + These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses @@ -70,9 +68,9 @@ The simplest way to compile this package is: Compilers and Options ===================== -Some systems require unusual options for compilation or linking that the -`configure' script does not know about. Run `./configure --help' for -details on some of the pertinent environment variables. + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here @@ -85,7 +83,7 @@ is an example: Compiling For Multiple Architectures ==================================== -You can compile the package for more than one kind of computer at the + You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the @@ -102,19 +100,19 @@ for another architecture. Installation Names ================== -By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX'. + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular +options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. @@ -125,7 +123,7 @@ option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= -Some packages pay attention to `--enable-FEATURE' options to + Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The @@ -140,11 +138,11 @@ you can use the `configure' options `--x-includes=DIR' and Specifying the System Type ========================== -There may be some features `configure' cannot figure out automatically, -but needs to determine by the type of machine the package will run on. -Usually, assuming the package is built to be run on the _same_ -architectures, `configure' can figure that out, but if it prints a -message saying it cannot guess the machine type, give it the + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: @@ -159,7 +157,7 @@ where SYSTEM can have one of these forms: need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will +use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a @@ -170,9 +168,9 @@ eventually be run) with `--host=TYPE'. Sharing Defaults ================ -If you want to set default values for `configure' scripts to share, you -can create a site shell script called `config.site' that gives default -values for variables like `CC', `cache_file', and `prefix'. + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. @@ -181,7 +179,7 @@ A warning: not all `configure' scripts look for a site script. Defining Variables ================== -Variables not defined in a site shell script can be set in the + Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set @@ -189,18 +187,14 @@ them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). Here is a another example: - - /bin/bash ./configure CONFIG_SHELL=/bin/bash - -Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent -configuration-related scripts to be executed by `/bin/bash'. +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). `configure' Invocation ====================== -`configure' recognizes the following options to control how it operates. + `configure' recognizes the following options to control how it +operates. `--help' `-h' diff --git a/Makefile.am b/Makefile.am index 9ce6cd8..980b004 100644 --- a/Makefile.am +++ b/Makefile.am @@ -64,7 +64,7 @@ CTEMPLATE_SYMBOLS = '[^A-Za-z](Template|TemplateDictionary|TemplateNamelist|Temp lib_LTLIBRARIES += libctemplate.la libctemplate_la_SOURCES = $(googleinclude_HEADERS) src/config.h \ - src/base/arena.h src/base/arena.cc src/base/mutex.h src/base/mutex.cc \ + src/base/arena.h src/base/arena.cc src/base/mutex.h \ src/template.cc src/template_dictionary.cc src/template_modifiers.cc \ src/template_namelist.cc src/template_from_string.cc libctemplate_la_CXXFLAGS = $(PTHREAD_CFLAGS) -DNDEBUG diff --git a/Makefile.in b/Makefile.in index a728175..5068618 100644 --- a/Makefile.in +++ b/Makefile.in @@ -90,7 +90,7 @@ am__DEPENDENCIES_1 = libctemplate_la_DEPENDENCIES = $(am__DEPENDENCIES_1) am__objects_1 = am_libctemplate_la_OBJECTS = $(am__objects_1) libctemplate_la-arena.lo \ - libctemplate_la-mutex.lo libctemplate_la-template.lo \ + libctemplate_la-template.lo \ libctemplate_la-template_dictionary.lo \ libctemplate_la-template_modifiers.lo \ libctemplate_la-template_namelist.lo \ @@ -98,7 +98,6 @@ am_libctemplate_la_OBJECTS = $(am__objects_1) libctemplate_la-arena.lo \ libctemplate_la_OBJECTS = $(am_libctemplate_la_OBJECTS) libctemplate_nothreads_la_LIBADD = am__objects_2 = $(am__objects_1) libctemplate_nothreads_la-arena.lo \ - libctemplate_nothreads_la-mutex.lo \ libctemplate_nothreads_la-template.lo \ libctemplate_nothreads_la-template_dictionary.lo \ libctemplate_nothreads_la-template_modifiers.lo \ @@ -326,6 +325,7 @@ ac_ct_RANLIB = @ac_ct_RANLIB@ ac_ct_STRIP = @ac_ct_STRIP@ ac_cv_cxx_hash_map = @ac_cv_cxx_hash_map@ ac_cv_cxx_hash_namespace = @ac_cv_cxx_hash_namespace@ +ac_cv_cxx_hash_set = @ac_cv_cxx_hash_set@ ac_google_attribute = @ac_google_attribute@ ac_google_end_namespace = @ac_google_end_namespace@ ac_google_namespace = @ac_google_namespace@ @@ -435,7 +435,7 @@ noinst_SCRIPTS = src/tests/make_tpl_varnames_h_unittest.sh # These are the symbols (classes, mostly) we want to export from our library CTEMPLATE_SYMBOLS = '[^A-Za-z](Template|TemplateDictionary|TemplateNamelist|TemplateFromString|TemplateString|TemplateState|Strip)[^A-Za-z]' libctemplate_la_SOURCES = $(googleinclude_HEADERS) src/config.h \ - src/base/arena.h src/base/arena.cc src/base/mutex.h src/base/mutex.cc \ + src/base/arena.h src/base/arena.cc src/base/mutex.h \ src/template.cc src/template_dictionary.cc src/template_modifiers.cc \ src/template_namelist.cc src/template_from_string.cc @@ -723,14 +723,12 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_la-arena.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_la-mutex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_la-template.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_la-template_dictionary.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_la-template_from_string.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_la-template_modifiers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_la-template_namelist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_nothreads_la-arena.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_nothreads_la-mutex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_nothreads_la-template.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_nothreads_la-template_dictionary.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libctemplate_nothreads_la-template_from_string.Plo@am__quote@ @@ -779,13 +777,6 @@ libctemplate_la-arena.lo: src/base/arena.cc @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libctemplate_la_CXXFLAGS) $(CXXFLAGS) -c -o libctemplate_la-arena.lo `test -f 'src/base/arena.cc' || echo '$(srcdir)/'`src/base/arena.cc -libctemplate_la-mutex.lo: src/base/mutex.cc -@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libctemplate_la_CXXFLAGS) $(CXXFLAGS) -MT libctemplate_la-mutex.lo -MD -MP -MF "$(DEPDIR)/libctemplate_la-mutex.Tpo" -c -o libctemplate_la-mutex.lo `test -f 'src/base/mutex.cc' || echo '$(srcdir)/'`src/base/mutex.cc; \ -@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/libctemplate_la-mutex.Tpo" "$(DEPDIR)/libctemplate_la-mutex.Plo"; else rm -f "$(DEPDIR)/libctemplate_la-mutex.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/base/mutex.cc' object='libctemplate_la-mutex.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libctemplate_la_CXXFLAGS) $(CXXFLAGS) -c -o libctemplate_la-mutex.lo `test -f 'src/base/mutex.cc' || echo '$(srcdir)/'`src/base/mutex.cc - libctemplate_la-template.lo: src/template.cc @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libctemplate_la_CXXFLAGS) $(CXXFLAGS) -MT libctemplate_la-template.lo -MD -MP -MF "$(DEPDIR)/libctemplate_la-template.Tpo" -c -o libctemplate_la-template.lo `test -f 'src/template.cc' || echo '$(srcdir)/'`src/template.cc; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/libctemplate_la-template.Tpo" "$(DEPDIR)/libctemplate_la-template.Plo"; else rm -f "$(DEPDIR)/libctemplate_la-template.Tpo"; exit 1; fi @@ -828,13 +819,6 @@ libctemplate_nothreads_la-arena.lo: src/base/arena.cc @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libctemplate_nothreads_la_CXXFLAGS) $(CXXFLAGS) -c -o libctemplate_nothreads_la-arena.lo `test -f 'src/base/arena.cc' || echo '$(srcdir)/'`src/base/arena.cc -libctemplate_nothreads_la-mutex.lo: src/base/mutex.cc -@am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libctemplate_nothreads_la_CXXFLAGS) $(CXXFLAGS) -MT libctemplate_nothreads_la-mutex.lo -MD -MP -MF "$(DEPDIR)/libctemplate_nothreads_la-mutex.Tpo" -c -o libctemplate_nothreads_la-mutex.lo `test -f 'src/base/mutex.cc' || echo '$(srcdir)/'`src/base/mutex.cc; \ -@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/libctemplate_nothreads_la-mutex.Tpo" "$(DEPDIR)/libctemplate_nothreads_la-mutex.Plo"; else rm -f "$(DEPDIR)/libctemplate_nothreads_la-mutex.Tpo"; exit 1; fi -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/base/mutex.cc' object='libctemplate_nothreads_la-mutex.lo' libtool=yes @AMDEPBACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libctemplate_nothreads_la_CXXFLAGS) $(CXXFLAGS) -c -o libctemplate_nothreads_la-mutex.lo `test -f 'src/base/mutex.cc' || echo '$(srcdir)/'`src/base/mutex.cc - libctemplate_nothreads_la-template.lo: src/template.cc @am__fastdepCXX_TRUE@ if $(LIBTOOL) --tag=CXX --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libctemplate_nothreads_la_CXXFLAGS) $(CXXFLAGS) -MT libctemplate_nothreads_la-template.lo -MD -MP -MF "$(DEPDIR)/libctemplate_nothreads_la-template.Tpo" -c -o libctemplate_nothreads_la-template.lo `test -f 'src/template.cc' || echo '$(srcdir)/'`src/template.cc; \ @am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/libctemplate_nothreads_la-template.Tpo" "$(DEPDIR)/libctemplate_nothreads_la-template.Plo"; else rm -f "$(DEPDIR)/libctemplate_nothreads_la-template.Tpo"; exit 1; fi diff --git a/configure b/configure index 8b08ec0..2a9f488 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.59 for ctemplate 0.6. +# Generated by GNU Autoconf 2.59 for ctemplate 0.6.1. # # Report bugs to . # @@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='ctemplate' PACKAGE_TARNAME='ctemplate' -PACKAGE_VERSION='0.6' -PACKAGE_STRING='ctemplate 0.6' +PACKAGE_VERSION='0.6.1' +PACKAGE_STRING='ctemplate 0.6.1' PACKAGE_BUGREPORT='opensource@google.com' ac_unique_file="README" @@ -465,7 +465,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CPP CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE build build_cpu build_vendor build_os host host_cpu host_vendor host_os EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL LIBTOOL_DEPS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS ac_google_namespace ac_google_start_namespace ac_google_end_namespace ac_cv_cxx_hash_namespace ac_cv_cxx_hash_map ac_google_attribute LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CPP CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE build build_cpu build_vendor build_os host host_cpu host_vendor host_os EGREP LN_S ECHO AR ac_ct_AR RANLIB ac_ct_RANLIB CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL LIBTOOL_DEPS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS ac_google_namespace ac_google_start_namespace ac_google_end_namespace ac_cv_cxx_hash_namespace ac_cv_cxx_hash_map ac_cv_cxx_hash_set ac_google_attribute LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -954,7 +954,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures ctemplate 0.6 to adapt to many kinds of systems. +\`configure' configures ctemplate 0.6.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1020,7 +1020,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of ctemplate 0.6:";; + short | recursive ) echo "Configuration of ctemplate 0.6.1:";; esac cat <<\_ACEOF @@ -1163,7 +1163,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -ctemplate configure 0.6 +ctemplate configure 0.6.1 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1177,7 +1177,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by ctemplate $as_me 0.6, which was +It was created by ctemplate $as_me 0.6.1, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1823,7 +1823,7 @@ fi # Define the identity of the package. PACKAGE='ctemplate' - VERSION='0.6' + VERSION='0.6.1' cat >>confdefs.h <<_ACEOF @@ -20613,6 +20613,7 @@ echo "$as_me: WARNING: could not find an STL hash_map" >&2;} + if test "$ac_cv___attribute__" == "yes"; then ac_google_attribute=1 @@ -21007,7 +21008,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by ctemplate $as_me 0.6, which was +This file was extended by ctemplate $as_me 0.6.1, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -21070,7 +21071,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -ctemplate config.status 0.6 +ctemplate config.status 0.6.1 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" @@ -21351,6 +21352,7 @@ s,@ac_google_start_namespace@,$ac_google_start_namespace,;t t s,@ac_google_end_namespace@,$ac_google_end_namespace,;t t s,@ac_cv_cxx_hash_namespace@,$ac_cv_cxx_hash_namespace,;t t s,@ac_cv_cxx_hash_map@,$ac_cv_cxx_hash_map,;t t +s,@ac_cv_cxx_hash_set@,$ac_cv_cxx_hash_set,;t t s,@ac_google_attribute@,$ac_google_attribute,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t diff --git a/configure.ac b/configure.ac index f601a6a..e8b658b 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ # make sure we're interpreted by some minimal autoconf AC_PREREQ(2.57) -AC_INIT(ctemplate, 0.6, opensource@google.com) +AC_INIT(ctemplate, 0.6.1, opensource@google.com) # The argument here is just something that should be in the current directory # (for sanity checking) AC_CONFIG_SRCDIR(README) @@ -58,6 +58,7 @@ AC_SUBST(ac_google_start_namespace) AC_SUBST(ac_google_end_namespace) AC_SUBST(ac_cv_cxx_hash_namespace) AC_SUBST(ac_cv_cxx_hash_map) +AC_SUBST(ac_cv_cxx_hash_set) if test "$ac_cv___attribute__" == "yes"; then AC_SUBST(ac_google_attribute, 1) else diff --git a/doc/example.html b/doc/example.html index a59bca0..714398e 100644 --- a/doc/example.html +++ b/doc/example.html @@ -32,6 +32,75 @@
+

Simple Example

+ +

One reason this example is so simple is that it doesn't even +require a separate template file, but instead uses +TemplateFromString. It also doesn't use sections or +template-includes.

+ +
+
+int main() {
+  static const char template_text[] =
+    "ERROR: {{FUNCTION}}({{ARGS}}) returned {{ERROR_CODE}}: {{ERROR_MESSAGE}}\n";
+  Template* tpl = TemplateFromString::GetTemplate("error_msg_tpl", template_text,
+                                                  DO_NOT_STRIP);
+  FILE* fp = fopen(argv[1], "r");
+  if (fp == NULL) {
+    int err_no = errno;   // squirrel this away
+    TemplateDictionary dict("error_msg: fopen()");
+    dict.SetValue("FUNCTION", "fopen");
+    dict.SetValue("ARGS", argv[1]);
+    dict.SetIntValue("ERROR_CODE", err_no);
+    dict.SetValue("ERROR_MESSAGE", strerror(err_no));
+
+    string error_text;
+    tpl->Expand(&error_text, &dict);
+    puts(error_text.c_str());
+  }
+}
+
+
+ +

This example is only slightly more complicated: we only print the +": <error message>" part when the error message isn't the empty +string.

+ +
+
+int main() {
+  static const char template_text[] =
+     "ERROR: {{FUNCTION}}({{ARGS}}) returned {{ERROR_CODE}}"
+     "{{#MSG_SECTION}}: {{ERROR_MESSAGE}}{{/MSG_SECTION}}\n";
+  Template* tpl = TemplateFromString::GetTemplate("error_msg", template_text,
+                                                  DO_NOT_STRIP);
+  FILE* fp = fopen(argv[1], "r");
+  if (fp == NULL) {
+    int err_no = errno;   // squirrel this away
+    TemplateDictionary dict("file_error_message");
+    dict.SetValue("FUNCTION", "fopen");
+    dict.SetValue("ARGS", argv[1]);
+    dict.SetIntValue("ERROR_CODE", err_no);
+    if (err_no > 0)
+      dict.SetValueAndShowSection("ERROR_MESSAGE", strerror(err_no),
+                                  "MSG_SECTION");
+
+    string error_text;
+    tpl->Expand(&error_text, &dict);
+    puts(error_text.c_str());
+  }
+}
+
+
+ +

This maybe-show-text functionality is one way the template +machinery is more powerful than just using printf. +Another nice property of templates is you can reuse the same variable +multiple times in your template string. You can also define the +variable values in any order.

+ +

Search Results Page

Here is an example template that could be used to format a Google diff --git a/ltmain.sh b/ltmain.sh index 8fc56db..ac99f55 100644 --- a/ltmain.sh +++ b/ltmain.sh @@ -1,3 +1,8 @@ +# NOTE(csilvers): This file (ltmain.sh) is taken from +# http://ftp.gnu.org/gnu/libtool/libtool-1.5.22.tar.gz +# with the following patch applied: +# http://www.marcuscom.com/downloads/patch-ltmain.sh + # ltmain.sh - Provide generalized library-building support services. # NOTE: Changing this file will not affect anything until you rerun configure. # @@ -43,7 +48,7 @@ EXIT_FAILURE=1 PROGRAM=ltmain.sh PACKAGE=libtool -VERSION="1.5.22 Debian 1.5.22-2" +VERSION=1.5.22 TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)" # See if we are running on zsh, and set the options which allow our @@ -1604,11 +1609,18 @@ EOF compiler_flags="$compiler_flags $arg" compile_command="$compile_command $arg" finalize_command="$finalize_command $arg" + deplibs="$deplibs $arg" continue ;; -module) module=yes + case $host in + *-*-freebsd*) + # Do not build the useless static library + build_old_libs=no + ;; + esac continue ;; @@ -2082,10 +2094,7 @@ EOF case $pass in dlopen) libs="$dlfiles" ;; dlpreopen) libs="$dlprefiles" ;; - link) - libs="$deplibs %DEPLIBS%" - test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" - ;; + link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; esac fi if test "$pass" = dlopen; then @@ -2104,6 +2113,29 @@ EOF else compiler_flags="$compiler_flags $deplib" fi + + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + ;; + *) + ;; + esac # linkmode + continue ;; -l*) @@ -3204,11 +3236,6 @@ EOF age="$number_minor" revision="$number_minor" ;; - *) - $echo "$modename: unknown library version type \`$version_type'" 1>&2 - $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 - exit $EXIT_FAILURE - ;; esac ;; no) @@ -4713,6 +4740,9 @@ static const void *lt_preloaded_setup() { ;; esac ;; + *-*-freebsd*) + # FreeBSD doesn't need this... + ;; *) $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 exit $EXIT_FAILURE @@ -6003,10 +6033,17 @@ relink_command=\"$relink_command\"" fi # Install the pseudo-library for information purposes. - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - instname="$dir/$name"i - $show "$install_prog $instname $destdir/$name" - $run eval "$install_prog $instname $destdir/$name" || exit $? + case $host in + *-*-freebsd*) + # Do not install the useless pseudo-library + ;; + *) + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + instname="$dir/$name"i + $show "$install_prog $instname $destdir/$name" + $run eval "$install_prog $instname $destdir/$name" || exit $? + ;; + esac # Maybe install the static library, too. test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" diff --git a/packages/deb/changelog b/packages/deb/changelog index 6677a0e..a09adc3 100644 --- a/packages/deb/changelog +++ b/packages/deb/changelog @@ -1,3 +1,9 @@ +ctemplate (0.6.1-1) unstable; urgency=low + + * New upstream release. + + -- Google Inc. Thu, 21 Jun 2007 14:02:32 -0700 + ctemplate (0.6-1) unstable; urgency=low * New upstream release. diff --git a/src/base/mutex.cc b/src/base/mutex.cc deleted file mode 100644 index d5b7c73..0000000 --- a/src/base/mutex.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright (c) 2007, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * --- - * Author: Craig Silverstein - * - * A simple mutex wrapper. Right now, it's implemented in terms of - * pthreads, but is meant to be easy to extend to other threads impls. - */ - -#include "config.h" -#include "mutex.h" - -#if defined(NO_THREADS) - -Mutex::Mutex() {} -Mutex::~Mutex() {} -void Mutex::Lock() {} -void Mutex::Unlock() {} -void Mutex::ReaderLock() {} -void Mutex::ReaderUnlock() {} - -#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK) - -#include // for abort() -#include -#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0) - -Mutex::Mutex() { SAFE_PTHREAD(pthread_rwlock_init(&mutex_, NULL)); } -Mutex::~Mutex() { SAFE_PTHREAD(pthread_rwlock_destroy(&mutex_)); } -void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock(&mutex_)); } -void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); } -void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock(&mutex_)); } -void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); } - -#elif defined(HAVE_PTHREAD) - -#include // for abort() -#include -#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0) - -Mutex::Mutex() { SAFE_PTHREAD(pthread_mutex_init(&mutex_, NULL)); } -Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy(&mutex_)); } -void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock(&mutex_)); } -void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock(&mutex_)); } -void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks -void Mutex::ReaderUnlock() { Unlock(); } - -#else - -#error Need to implement mutex.h/cc for your architecture, or #define NO_THREADS - -#endif diff --git a/src/base/mutex.h b/src/base/mutex.h index 35bd85a..cc2d9fb 100644 --- a/src/base/mutex.h +++ b/src/base/mutex.h @@ -45,21 +45,25 @@ #ifndef GOOGLE_MUTEX_H__ #define GOOGLE_MUTEX_H__ -#include "config.h" // to figure out pthreads support +#include "config.h" // to figure out pthreads support #if defined(NO_THREADS) - typedef int MutexType; // some dummy type; it won't be used + typedef int MutexType; // some dummy type; it won't be used #elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK) // Needed for pthread_rwlock_*. If it causes problems, you could take // it out, but then you'd have to unset HAVE_RWLOCK (at least on linux). -# define _XOPEN_SOURCE 500 // needed to get the rwlock calls +# define _XOPEN_SOURCE 500 // needed to get the rwlock calls # include typedef pthread_rwlock_t MutexType; #elif defined(HAVE_PTHREAD) # include typedef pthread_mutex_t MutexType; +#elif defined(WIN32) +# define WIN32_LEAN_AND_MEAN // We only need minimal includes +# include + typedef CRITICAL_SECTION MutexType; #else -# error Need to implement mutex.h/cc for your architecture, or #define NO_THREADS +# error Need to implement mutex.h for your architecture, or #define NO_THREADS #endif class Mutex { @@ -68,22 +72,22 @@ class Mutex { // typically used for Mutexes allocated on the heap or the stack. // See below for a recommendation for constructing global Mutex // objects. - Mutex(); + inline Mutex(); // Destructor - ~Mutex(); + inline ~Mutex(); - void Lock(); // Block if necessary until free, then acquire exclusively - void Unlock(); // Release. Caller must hold it exclusively (via Lock()) + inline void Lock(); // Block if needed until free then acquire exclusively + inline void Unlock(); // Release a lock acquired via Lock() // Note that on systems that don't support read-write locks, these may // be implemented as synonyms to Lock() and Unlock(). So you can use // these for efficiency, but don't use them anyplace where being able // to do shared reads is necessary to avoid deadlock. - void ReaderLock(); // Block until free or shared, then acquire a share - void ReaderUnlock(); // Release a read share of this Mutex - void WriterLock() { Lock(); } // Block until free, then acquire exclusively - void WriterUnlock() { Unlock(); } // Release the exclusive lock of this Mutex + inline void ReaderLock(); // Block until free or shared then acquire a share + inline void ReaderUnlock(); // Release a read share of this Mutex + inline void WriterLock() { Lock(); } // Acquire an exclusive lock + inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock() private: MutexType mutex_; @@ -95,6 +99,56 @@ class Mutex { void operator=(const Mutex&); }; +// Now the implementation of Mutex for various systems +#if defined(NO_THREADS) + +Mutex::Mutex() {} +Mutex::~Mutex() {} +void Mutex::Lock() {} +void Mutex::Unlock() {} +void Mutex::ReaderLock() {} +void Mutex::ReaderUnlock() {} + +#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK) + +#include // for abort() +#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0) + +Mutex::Mutex() { SAFE_PTHREAD(pthread_rwlock_init(&mutex_, NULL)); } +Mutex::~Mutex() { SAFE_PTHREAD(pthread_rwlock_destroy(&mutex_)); } +void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock(&mutex_)); } +void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); } +void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock(&mutex_)); } +void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock(&mutex_)); } +#undef SAFE_PTHREAD + +#elif defined(HAVE_PTHREAD) + +#include // for abort() +#define SAFE_PTHREAD(fncall) do { if ((fncall) != 0) abort(); } while (0) + +Mutex::Mutex() { SAFE_PTHREAD(pthread_mutex_init(&mutex_, NULL)); } +Mutex::~Mutex() { SAFE_PTHREAD(pthread_mutex_destroy(&mutex_)); } +void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock(&mutex_)); } +void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock(&mutex_)); } +void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks +void Mutex::ReaderUnlock() { Unlock(); } +#undef SAFE_PTHREAD + +#elif defined(WIN32) + +Mutex::Mutex() { InitializeCriticalSection(&mutex_); } +Mutex::~Mutex() { DeleteCriticalSection(&mutex_); } +void Mutex::Lock() { EnterCriticalSection(&mutex_); } +void Mutex::Unlock() { LeaveCriticalSection(&mutex_); } +void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks +void Mutex::ReaderUnlock() { Unlock(); } + +#endif + + +// -------------------------------------------------------------------------- +// Some helper classes // MutexLock(mu) acquires mu when constructed and releases it when destroyed. class MutexLock { diff --git a/src/google/template.h.in b/src/google/template.h.in index a6bf474..adb9c84 100644 --- a/src/google/template.h.in +++ b/src/google/template.h.in @@ -201,6 +201,10 @@ class Template { const TemplateDictionary *dictionary, const TemplateDictionary *force_annotate_dict) const; + // Internal version of ReloadIfChanged, used when the function already + // has a write-lock on mutex_. + bool ReloadIfChangedLocked(); + // set_state // Sets the state of the template. Used during BuildTree(). void set_state(TemplateState new_state); diff --git a/src/google/template_from_string.h.in b/src/google/template_from_string.h.in index 0431809..65875f5 100644 --- a/src/google/template_from_string.h.in +++ b/src/google/template_from_string.h.in @@ -67,28 +67,32 @@ class TemplateFromString : public Template { /* GetTemplate Parameters: (NOTE: The parameter list is not the same as Template::GetTemplate.) - template_name - a logical name for the template text + cache_key - the cache string used for the template text. template_text - the text of the template containing the template code with markers, the very same language that would be stored in a file for the parent class strip - same as the parent class Description: - Attempts to find an instance of the class with the given template_name + Attempts to find an instance of the class with the given cache_key stored in the cache. If it finds one, it returns it, ignoring the template_text passed to the method. If it does not find one, it creates a new instance of the class, stores it in the cache under the template_name, and returns it. - Note: since cache lookup is by name, you can't have two instances - with the same name but different text, and expect it to work. + Note: since cache lookup is by key, you can't have two instances + with the same key but different text, and expect it to work. + However, if cache_key is the empty string, we ignore the cache, + and always create a new instance of the class (without storing it + in the cache). In this case *only*, you're responsible for + deleting the returned TemplateFromString object when done with it. */ - static TemplateFromString *GetTemplate(const std::string& template_name, + static TemplateFromString *GetTemplate(const std::string& cache_key, const std::string& template_text, Strip strip); private: /* This templates constuctor is private just like the parent's is. New ones are acquired through TemplateFromString::GetTemplate */ - TemplateFromString(const std::string& template_name, + TemplateFromString(const std::string& cache_key, const std::string& template_text, Strip strip); diff --git a/src/make_tpl_varnames_h.cc b/src/make_tpl_varnames_h.cc index 95f56e0..c11177a 100644 --- a/src/make_tpl_varnames_h.cc +++ b/src/make_tpl_varnames_h.cc @@ -46,7 +46,9 @@ #include "config.h" #include #include +#ifdef HAVE_UNISTD_H #include +#endif #include #include #include diff --git a/src/template.cc b/src/template.cc index dedd6a7..b206c86 100644 --- a/src/template.cc +++ b/src/template.cc @@ -40,7 +40,9 @@ #include #include // for isspace() #include +#ifdef HAVE_UNISTD_H #include // for stat() and open() and getcwd() +#endif #include #include // for logging #include // for indenting in Dump() @@ -78,6 +80,9 @@ namespace { // Mutexes protecting the globals below. First protects g_use_current_dict // and template_root_directory_, second protects g_template_cache. // Third protects vars_seen in WriteOneHeaderEntry, below. +// Lock priority invariant: you should never acquire a Template::mutex_ +// while holding one of these mutexes. +// TODO(csilvers): assert this in the codebase. static Mutex g_static_mutex; static Mutex g_cache_mutex; static Mutex g_header_mutex; @@ -267,24 +272,28 @@ struct TemplateToken { static void EmitModifiedString(const ModifierAndNonces& modifiers, const char* in, int inlen, ExpandEmitter* outbuf) { - // If there's more than one modifiers, we need to store the - // intermediate results in a temp-buffer. We use a string. - string scratch; + string result; if (modifiers.size() > 1) { - // We'll assume that each modifier adds about 12% to the input size. We - // should exponentiate by |modifiers| but we just multiply, to save time. - scratch.reserve((inlen + inlen/8) * (modifiers.size()-1) + 16); - StringEmitter scratchbuf(&scratch); - // Each time through, we append the latest version of the string to - // scratch, and keep a pointer pointing to the most recent version. - // Except for (rare!) vars with 3+ modifiers, this loop at most once. - for (ModifierAndNonces::const_iterator it = modifiers.begin(); + // If there's more than one modifiers, we need to store the + // intermediate results in a temp-buffer. We use a string. + // We'll assume that each modifier adds about 12% to the input + // size. + result.reserve((inlen + inlen/8) + 16); + StringEmitter scratchbuf(&result); + modifiers.front().first->Modify(in, inlen, &scratchbuf, + modifiers.front().second); + // Only used when modifiers.size() > 2 + for (ModifierAndNonces::const_iterator it = modifiers.begin()+1; it != modifiers.end()-1; ++it) { - const int startpos = scratch.size(); // where we start appendend - it->first->Modify(in, inlen, &scratchbuf, it->second); - in = scratch.data() + startpos; // point to the new "in" - inlen = scratch.size() - startpos; + string output_of_this_modifier; + output_of_this_modifier.reserve(result.size() + result.size()/8 + 16); + StringEmitter scratchbuf2(&output_of_this_modifier); + it->first->Modify(result.c_str(), result.size(), + &scratchbuf2, it->second); + result.swap(output_of_this_modifier); } + in = result.data(); + inlen = result.size(); } // For the last modifier, we can write directly into outbuf assert(!modifiers.empty()); @@ -626,6 +635,8 @@ class SectionTemplateNode : public TemplateNode { // section, or template to the list of nodes contained in this // section. Returns true iff we really added a node and didn't just // end a section or hit a syntax error in the template file. + // You should hold a write-lock on my_template->mutex_ when calling this. + // (unless you're calling it from a constructor). bool AddSubnode(Template *my_template); // Expands a section node as follows: @@ -932,6 +943,8 @@ string *Template::template_root_directory_ = NULL; // inappropriate characters in a name, not finding the closing curly // braces, etc.) an error message is logged, the error state of the // template is set, and a NULL token is returned. Updates parse_state_. +// You should hold a write-lock on my_template->mutex_ when calling this +// (unless you're calling it from a constructor). TemplateToken SectionTemplateNode::GetNextToken(Template *my_template) { Template::ParseState* ps = &my_template->parse_state_; // short abbrev. const char* token_start = ps->bufstart; @@ -1120,9 +1133,11 @@ TemplateToken SectionTemplateNode::GetNextToken(Template *my_template) { // Template::~Template() // Template::AssureGlobalsInitialized() // Template::GetTemplate() -// Calls ReloadIfChanged to load the template the first time. -// The constructor is private; GetTemplate() is the factory -// method used to actually construct a new template if needed. +// Calls ReloadIfChanged to load the template the first time. The +// constructor is private; GetTemplate() is the factory method +// used to actually construct a new template if needed -- it's the +// only thing that calls the template constructor -- and where we +// actually call ReloadIfChanged() (based on state_ == TS_EMPTY). // ---------------------------------------------------------------------- Template::Template(const string& filename, Strip strip) @@ -1134,10 +1149,7 @@ Template::Template(const string& filename, Strip strip) // of calling Expand() or other Template classes that access globals. AssureGlobalsInitialized(); - // phase_ indicates what type of thing we expect next during tokenization. - // We start off expecting text, hence the initial value is GETTING_TEXT - - VLOG(2) << endl << "Constructing Template for " << template_file() << endl; + VLOG(2) << "Constructing Template for " << template_file(); // Preserve whitespace in Javascript files because carriage returns // can convey meaning for comment termination and closures @@ -1145,8 +1157,6 @@ Template::Template(const string& filename, Strip strip) !strcmp(filename.c_str() + filename.length() - 3, ".js") ) { strip = STRIP_BLANK_LINES; } - - ReloadIfChanged(); } Template::~Template() { @@ -1173,26 +1183,33 @@ Template *Template::GetTemplate(const string& filename, Strip strip) { // No need to have the cache-mutex acquired for this step string abspath(PathJoin(template_root_directory(), filename)); - MutexLock ml(&g_cache_mutex); - if (g_template_cache == NULL) - g_template_cache = new TemplateCache; + Template* tpl = NULL; + { + MutexLock ml(&g_cache_mutex); + if (g_template_cache == NULL) + g_template_cache = new TemplateCache; - Template *tpl = (*g_template_cache)[pair(abspath, strip)]; - if (tpl) { - // Note: if the status is TS_ERROR here, we don't attempt - // to reload the template file, but we don't return - // the template object either - if (tpl->state() == TS_RELOAD) { - tpl->ReloadIfChanged(); + tpl = (*g_template_cache)[pair(abspath, strip)]; + if (!tpl) { + tpl = new Template(abspath, strip); + (*g_template_cache)[pair(abspath, strip)] = tpl; } - } else { - tpl = new Template(abspath, strip); - (*g_template_cache)[pair(abspath, strip)] = tpl; } - // if the statis is not TS_READY, then it is TS_ERROR at this - // point. If it is TS_ERROR, we leave the state as is, but return - // NULL. We won't try to load the template file again until the + // Even though we only read state() here, not write it, we acquire + // the lock in write-mode in case we have to call ReloadIfChanged. + WriterMutexLock ml(tpl->mutex_); + + // Note: if the status is TS_ERROR here, we don't attempt to reload + // the template file, but we don't return the template object + // either. If the state is TS_EMPTY, it means tpl was just constructed + // and doesn't have *any* content yet, so we should certainly reload. + if (tpl->state() == TS_RELOAD || tpl->state() == TS_EMPTY) { + tpl->ReloadIfChangedLocked(); + } + + // If the state is TS_ERROR, we leave the state as is, but return + // NULL. We won't try to load the template file again until the // state gets changed to TS_RELOAD by another call to // ReloadAllIfChanged. if (tpl->state() != TS_READY) { @@ -1214,6 +1231,8 @@ Template *Template::GetTemplate(const string& filename, Strip strip) { // NOTE: BuildTree takes over ownership of input_buffer, and will delete it. // It should have been created via new[]. +// You should hold a write-lock on mutex_ before calling this +// (unless you're calling it from a constructor). bool Template::BuildTree(const char* input_buffer, const char* input_buffer_end) { // Assign an arbitrary name to the top-level node @@ -1331,6 +1350,7 @@ const char *Template::template_file() const { // ---------------------------------------------------------------------- // Template::ReloadIfChanged() +// Template::ReloadIfChangedLocked() // Template::ReloadAllIfChanged() // If one template, try immediately to reload it from disk. If // all templates, just set all their statuses to TS_RELOAD, so @@ -1341,15 +1361,9 @@ const char *Template::template_file() const { // and parsed it. It never returns true if filename_ is "". // ---------------------------------------------------------------------- -bool Template::ReloadIfChanged() { +bool Template::ReloadIfChangedLocked() { if (filename_.empty()) return false; - // This entire routine is protected by mutex_ so when it's called - // from different threads, they don't stomp on tree_ and state_. - // This is still not perfect, since set_filename() could stomp - // on filename_ while we're reading it, but it's good enough. - WriterMutexLock ml(mutex_); - struct stat statbuf; if (stat(filename_.c_str(), &statbuf) != 0) { LOG(WARNING) << "Unable to stat file " << filename_ << endl; @@ -1403,35 +1417,62 @@ bool Template::ReloadIfChanged() { } } +bool Template::ReloadIfChanged() { + // ReloadIfChanged() is protected by mutex_ so when it's called from + // different threads, they don't stomp on tree_ and state_. + WriterMutexLock ml(mutex_); + return ReloadIfChangedLocked(); +} + void Template::ReloadAllIfChanged() { - MutexLock ml(&g_cache_mutex); // this protects the static g_template_cache - if (g_template_cache == NULL) { - return; + // This is slightly annoying: we copy all the template-pointers to + // a vector, so we don't have to hold g_cache_mutex while messing + // with the templates (which would violate our lock invariant). + vector templates_in_cache; + { + MutexLock ml(&g_cache_mutex); // this protects the static g_template_cache + if (g_template_cache == NULL) { + return; + } + for (TemplateCache::const_iterator iter = g_template_cache->begin(); + iter != g_template_cache->end(); + ++iter) { + templates_in_cache.push_back(iter->second); + } } - for (TemplateCache::const_iterator iter = g_template_cache->begin(); - iter != g_template_cache->end(); + for (vector::iterator iter = templates_in_cache.begin(); + iter != templates_in_cache.end(); ++iter) { - (*iter).second->set_state(TS_RELOAD); + WriterMutexLock ml((*iter)->mutex_); + (*iter)->set_state(TS_RELOAD); } } // ---------------------------------------------------------------------- // Template::ClearCache() -// Deletes all the objects in the template cache +// Deletes all the objects in the template cache. Note: it's +// dangerous to clear the cache if other threads are still +// referencing the templates that are stored in it! // ---------------------------------------------------------------------- void Template::ClearCache() { - MutexLock ml(&g_cache_mutex); // this protects the static g_template_cache - if (g_template_cache == NULL) { - return; + // We clear the cache by swapping it with an empty cache. This lets + // us delete the items in the cache at our leisure without needing + // to hold g_cache_mutex. + TemplateCache tmp_cache; + { + MutexLock ml(&g_cache_mutex); // this protects the static g_template_cache + if (g_template_cache == NULL) { + return; + } + g_template_cache->swap(tmp_cache); // now g_template_cache is empty } - for (TemplateCache::const_iterator iter = g_template_cache->begin(); - iter != g_template_cache->end(); + // Now delete everything we've removed from the cache. + for (TemplateCache::const_iterator iter = tmp_cache.begin(); + iter != tmp_cache.end(); ++iter) { - delete (*iter).second; + delete iter->second; } - delete g_template_cache; - g_template_cache = NULL; } // ---------------------------------------------------------------------- diff --git a/src/template_from_string.cc b/src/template_from_string.cc index 270fdf2..0f70a94 100644 --- a/src/template_from_string.cc +++ b/src/template_from_string.cc @@ -41,6 +41,9 @@ _START_GOOGLE_NAMESPACE_ +// Lock priority invariant: you should never acquire a +// TemplateFromString::mutex_ while holding this mutex. +// TODO(csilvers): assert this in the codebase. static Mutex g_cache_mutex; using std::string; @@ -53,11 +56,11 @@ using HASH_NAMESPACE::hash_map; // the template text is taken from the second parameter. After that, the // object is identical to a Template object, except that it cannot be // "reloaded." -TemplateFromString::TemplateFromString(const string& template_name, +TemplateFromString::TemplateFromString(const string& cache_key, const string& template_text, Strip strip) : Template("", strip) { - filename_ = template_name; // for cache and reporting purposes only + filename_ = cache_key; // for cache and reporting purposes only // We know that InsertFile never writes more output than it gets input. // While we allocate buffer here, BuildTree takes ownership and deletes it. @@ -94,27 +97,30 @@ static TemplateFromStringCache *g_template_from_string_cache = NULL; // TemplateFromString::GetTemplate // Makes sure the template cache has been created and then tries to -// retrieve a TemplateFromString object from it via the template_name. -TemplateFromString *TemplateFromString::GetTemplate(const string& template_name, +// retrieve a TemplateFromString object from it via the cache_key. +TemplateFromString *TemplateFromString::GetTemplate(const string& cache_key, const string& template_text, Strip strip) { - // Only perform this method when you have the lock so multiple threads - // don't conflict over inserting and retrieving into the cache - MutexLock ml(&g_cache_mutex); - if (g_template_from_string_cache == NULL) { - g_template_from_string_cache = new TemplateFromStringCache; + TemplateFromString *tpl = NULL; + if (cache_key.empty()) { // user doesn't want to use the cache + tpl = new TemplateFromString(cache_key, template_text, strip); + } else { + MutexLock ml(&g_cache_mutex); + if (g_template_from_string_cache == NULL) { + g_template_from_string_cache = new TemplateFromStringCache; + } + // If the object isn't really a TemplateFromString this will be a cache miss + tpl = (*g_template_from_string_cache)[pair(cache_key, strip)]; + + // If we didn't find one, then create one and store it in the cache + if (!tpl) { + tpl = new TemplateFromString(cache_key, template_text, strip); + (*g_template_from_string_cache)[pair(cache_key, strip)] = + tpl; + } } - // If the object isn't really a TemplateFromString, this will be a cache miss - TemplateFromString *tpl = - (*g_template_from_string_cache)[pair(template_name, strip)]; - - // If we didn't find one, then create one and store it in the cache - if (!tpl) { - tpl = new TemplateFromString(template_name, template_text, strip); - (*g_template_from_string_cache)[pair(template_name, strip)] = - tpl; - } + WriterMutexLock ml(tpl->mutex_); // to access state() // state_ can be TS_RELOAD if ReloadAllIfChanged() touched this file. // That's fine; we'll just ignore the reload directive for this guy. diff --git a/src/template_namelist.cc b/src/template_namelist.cc index dd72907..910590e 100644 --- a/src/template_namelist.cc +++ b/src/template_namelist.cc @@ -33,7 +33,9 @@ #include "config.h" #include #include +#ifdef HAVE_UNISTD_H #include // for access() +#endif #include // for time_t #include // for stat() #include diff --git a/src/tests/template_from_string_unittest.cc b/src/tests/template_from_string_unittest.cc index 8755878..795b695 100644 --- a/src/tests/template_from_string_unittest.cc +++ b/src/tests/template_from_string_unittest.cc @@ -39,13 +39,17 @@ #include #include #include +#ifdef HAVE_UNISTD_H #include +#endif #include #include #include #include // for sort #include +#ifdef HAVE_DIRENT_H #include // for readdir +#endif #include #include @@ -133,6 +137,8 @@ class TemplateFromStringUnittest { } static void TestGetTemplate() { + TemplateDictionary dict("dict"); + // Tests the cache const char* const tpltext = "{This is perfectly valid} yay!"; const char* const tpltext2 = "This will be ignored"; @@ -148,6 +154,22 @@ class TemplateFromStringUnittest { ASSERT(tpl1 == tpl2); ASSERT(tpl3 == tpl4); ASSERT(tpl1 != tpl3); + AssertExpandIs(tpl1, &dict, tpltext); + AssertExpandIs(tpl2, &dict, tpltext); + AssertExpandIs(tpl3, &dict, tpltext); + AssertExpandIs(tpl4, &dict, tpltext); + + // Tests our mechanism for ignoring the cache (first arg is empty-string) + Template* tpl1b = TemplateFromString::GetTemplate( + "", tpltext, DO_NOT_STRIP); + Template* tpl2b = TemplateFromString::GetTemplate( + "", tpltext2, DO_NOT_STRIP); + ASSERT(tpl1b != tpl2b); + AssertExpandIs(tpl1b, &dict, tpltext); + AssertExpandIs(tpl2b, &dict, tpltext2); + // When you don't cache the template, you have to delete it! + delete tpl1b; + delete tpl2b; // Tests that syntax errors cause us to return NULL Template* tpl5 = StringToTemplate("{{This has spaces in it}}", DO_NOT_STRIP); diff --git a/src/tests/template_unittest.cc b/src/tests/template_unittest.cc index 2ed8afb..d1e1ec5 100644 --- a/src/tests/template_unittest.cc +++ b/src/tests/template_unittest.cc @@ -289,6 +289,24 @@ class TemplateUnittest { // Check we don't allow modifiers on sections tpl = StringToTemplate("hi {{#VAR:h}} lo {{/VAR}}", STRIP_WHITESPACE); ASSERT(tpl == NULL); + + // Test when expanded grows by more than 12% per modifier. + dict.SetValue("VAR", "http://a.com?b=c&d=e&f=g&q=a>b"); + tpl = StringToTemplate("{{VAR:u:j:h}}", + STRIP_WHITESPACE); + AssertExpandIs(tpl, &dict, + "http%3A//a.com%3Fb%3Dc%26d%3De%26f%3Dg%26q%3Da%3Eb", + true); + + // As above with 4 modifiers. + dict.SetValue("VAR", "http://a.com?b=c&d=e&f=g&q=a>b"); + tpl = StringToTemplate("{{VAR:u:j:h:h}}", + STRIP_WHITESPACE); + AssertExpandIs(tpl, &dict, + "http%3A//a.com%3Fb%3Dc%26d%3De%26f%3Dg%26q%3Da%3Eb", + true); + + } static void TestSection() {