* threaded parser
authorMichael Wallner <mike@php.net>
Mon, 3 Dec 2018 08:15:55 +0000 (09:15 +0100)
committerMichael Wallner <mike@php.net>
Tue, 4 Dec 2018 11:13:50 +0000 (12:13 +0100)
* simplify configure

14 files changed:
config.m4
m4/psi/psi.m4
php_psi_cpp.h.in
src/call.c
src/context.c
src/cpp.c
src/data.c
src/data.h
src/parser.c
src/plist.c
src/plist.h
src/token.c
src/types/decl_abi.c
src/types/decl_extvar.c

index f63f52d..5ea9c37 100644 (file)
--- a/config.m4
+++ b/config.m4
@@ -19,6 +19,9 @@ if test "$PHP_PSI" != no; then
        [  --enable-psi-maintainer-mode
                           PSI: enable maintainer mode
                            . make dependencies], [no], [no])
+    PHP_ARG_ENABLE(psi-threaded-parser, whether to enable threaded parser,
+    [  --enable-psi-threaded-parser
+                          PSI: enable threads for startup parsing], [yes], [no])
 
        PHP_ARG_WITH(psi-libjit, where to find libjit,
        [  --with-psi-libjit=DIR   PSI: path to libjit], [ ], [ ])
@@ -28,84 +31,33 @@ if test "$PHP_PSI" != no; then
 
        AC_HEADER_ASSERT dnl # adds --disable-assert to define NDEBUG
 
+       PSI_CONFIG_INIT
+       
+       PSI_CHECK_MAINTAINER_MODE
+       PSI_CHECK_THREADED_PARSER
+
        dnl we cannot use AC_USE_SYSTEM_EXTENSIONS here, because we're way too late
        dnl in the game, and we're currently only targeting _GNU_SOURCE for now
-       AC_MSG_CHECKING([for _GNU_SOURCE])
-       AC_EGREP_CPP([gnu_source_not_defined], [
-               #ifndef _GNU_SOURCE
-               gnu_source_not_defined
-               #endif
-       ], [
-               AC_MSG_RESULT([needs define])
-               AC_DEFINE([_GNU_SOURCE], [1], [ ])
-       ], [
-               AC_MSG_RESULT([already defined])
-       ])
-
-       AC_MSG_CHECKING([psi source dir])
-       PHP_PSI_SRCDIR=PHP_EXT_SRCDIR(psi)
-       AC_MSG_RESULT([$PHP_PSI_SRCDIR])
+       PSI_CHECK_GNU_SOURCE
+       PSI_CHECK_CPP
+       PSI_CHECK_STD_TYPES
        
-       AC_MSG_CHECKING([psi build dir])
-       case "PHP_EXT_BUILDDIR(psi)" in
-       ""|.) PHP_PSI_BUILDDIR=$PHP_PSI_SRCDIR
-               ;;
-       *)    PHP_PSI_BUILDDIR=PHP_EXT_BUILDDIR(psi)
-               ;;
-       esac
-       AC_MSG_RESULT([$PHP_PSI_BUILDDIR])
-
-       PSI_CHECK_LIBJIT
-       PSI_CHECK_LIBFFI
-
        AC_FUNC_FNMATCH
        AC_FUNC_MMAP
        AC_CHECK_FUNCS([mknodat eaccess])
 
-       PSI_CONFIG_INIT
-       PSI_CHECK_STD_TYPES
-       PSI_CONFIG_DONE
+       PSI_CHECK_PTHREAD
+       PSI_CHECK_LIBJIT
+       PSI_CHECK_LIBFFI
 
-       PHP_SUBST(PSI_SHARED_LIBADD)
+       PSI_CONFIG_DONE
 
        AC_DEFINE_UNQUOTED(PHP_PSI_SHLIB_SUFFIX, ["$SHLIB_SUFFIX_NAME"], DL suffix)
 
-       PHP_ADD_INCLUDE($PHP_PSI_SRCDIR)
-       PHP_ADD_INCLUDE($PHP_PSI_SRCDIR/src)
-       PHP_ADD_INCLUDE($PHP_PSI_SRCDIR/src/calc)
-       PHP_ADD_INCLUDE($PHP_PSI_SRCDIR/src/types)
-       PHP_ADD_INCLUDE($PHP_PSI_BUILDDIR)
-       PHP_ADD_BUILD_DIR($PHP_PSI_BUILDDIR/src)
-       PHP_ADD_BUILD_DIR($PHP_PSI_BUILDDIR/src/types)
-
-       PHP_PSI_HEADERS=" \
-               src/calc/basic.h src/calc/bin.h src/calc/bool.h src/calc/cast.h \
-               src/calc/unary.h src/calc/cmp.h src/calc/oper.h \
-               `(cd $PHP_PSI_SRCDIR/src && ls *.h types/*.h)` \
-       "
-       # parser_* should come first
-       PHP_PSI_SOURCES=" \
-               src/parser_proc.c src/parser_scan.c \
-               `(cd $PHP_PSI_SRCDIR && ls src/*.c src/types/*.c \
-                       | $EGREP -v '^src/parser_' \
-               )` \
-       "
-       PHP_PSI_GENERATED=" \
-               src/parser_proc.c src/parser_scan.c \
-               src/calc/basic.h src/calc/bin.h src/calc/bool.h src/calc/cast.h \
-               src/calc/unary.h src/calc/cmp.h src/calc/oper.h \
-       "
-
+       PHP_SUBST(PSI_SHARED_LIBADD)
        PHP_NEW_EXTENSION(psi, $PHP_PSI_SOURCES, $ext_shared)
        PHP_INSTALL_HEADERS(ext/psi, php_psi.h $PHP_PSI_HEADERS)
 
-       PHP_SUBST(PHP_PSI_GENERATED)
-       PHP_SUBST(PHP_PSI_HEADERS)
-       PHP_SUBST(PHP_PSI_SOURCES)
-
-       PHP_SUBST(PHP_PSI_SRCDIR)
-       PHP_SUBST(PHP_PSI_BUILDDIR)
-
        PHP_ADD_MAKEFILE_FRAGMENT
        AC_MSG_RESULT()
 fi
index 9701afc..c534ed4 100644 (file)
-
 dnl PSI_CONFIG_INIT()
 dnl Creates stubs of the headers with pre-defined types etc.
-dnl These headers are included by src/context.c.
-dnl This macro must be called prior any checks for a type, struct, decl etc.
 AC_DEFUN(PSI_CONFIG_INIT, [
        psi_save_LIBS=$LIBS
        LIBS=
 
        ac_includes_default="AC_INCLUDES_DEFAULT"
        
-       AC_PROG_AWK
+       AC_MSG_CHECKING([psi source dir])
+       PHP_PSI_SRCDIR=PHP_EXT_SRCDIR(psi)
+       AC_MSG_RESULT([$PHP_PSI_SRCDIR])
+       
+       AC_MSG_CHECKING([psi build dir])
+       case "PHP_EXT_BUILDDIR(psi)" in
+       ""|.) PHP_PSI_BUILDDIR=$PHP_PSI_SRCDIR
+               ;;
+       *)    PHP_PSI_BUILDDIR=PHP_EXT_BUILDDIR(psi)
+               ;;
+       esac
+       AC_MSG_RESULT([$PHP_PSI_BUILDDIR])
+       
+       PSI_STDTYPES=
+       PSI_CONSTS=
+       
+       AC_CONFIG_FILES(
+               [$PHP_PSI_BUILDDIR/php_psi_stdinc.h:$PHP_PSI_SRCDIR/php_psi_stdinc.h.in]
+               [$PHP_PSI_BUILDDIR/php_psi_posix.h:$PHP_PSI_SRCDIR/php_psi_posix.h.in]
+               [$PHP_PSI_BUILDDIR/php_psi_cpp.h:$PHP_PSI_SRCDIR/php_psi_cpp.h.in]
+       )
+
+])
+
+dnl PSI_CONFIG_DONE()
+dnl Finish the headers with the pre-defined types etc.
+AC_DEFUN(PSI_CONFIG_DONE, [
+       psi_eval_LIBS=$LIBS
+       LIBS=$psi_save_LIBS
+       PHP_EVAL_LIBLINE($psi_eval_LIBS, PSI_SHARED_LIBADD)
        
+       [PSI_INCLUDES]="PSI_INCLUDES"
+       AC_SUBST([PSI_INCLUDES])
+       AC_SUBST([PSI_STDTYPES])
+       AC_SUBST([PSI_CONSTS])
+       AC_SUBST([PSI_CPP_SEARCH])
+       AC_SUBST([PSI_CPP_PREDEF])
+       
+       PHP_ADD_INCLUDE($PHP_PSI_SRCDIR)
+       PHP_ADD_INCLUDE($PHP_PSI_SRCDIR/src)
+       PHP_ADD_INCLUDE($PHP_PSI_SRCDIR/src/calc)
+       PHP_ADD_INCLUDE($PHP_PSI_SRCDIR/src/types)
+       PHP_ADD_INCLUDE($PHP_PSI_BUILDDIR)
+       PHP_ADD_BUILD_DIR($PHP_PSI_BUILDDIR/src)
+       PHP_ADD_BUILD_DIR($PHP_PSI_BUILDDIR/src/types)
+
+       PHP_PSI_HEADERS=" \
+               src/calc/basic.h src/calc/bin.h src/calc/bool.h src/calc/cast.h \
+               src/calc/unary.h src/calc/cmp.h src/calc/oper.h \
+               `(cd $PHP_PSI_SRCDIR/src && ls *.h types/*.h)` \
+       "
+       # parser_* should come first
+       PHP_PSI_SOURCES=" \
+               src/parser_proc.c src/parser_scan.c \
+               `(cd $PHP_PSI_SRCDIR && ls src/*.c src/types/*.c \
+                       | $EGREP -v '^src/parser_' \
+               )` \
+       "
+       PHP_PSI_GENERATED=" \
+               src/parser_proc.c src/parser_scan.c \
+               src/calc/basic.h src/calc/bin.h src/calc/bool.h src/calc/cast.h \
+               src/calc/unary.h src/calc/cmp.h src/calc/oper.h \
+       "
+
+       PHP_SUBST(PHP_PSI_GENERATED)
+       PHP_SUBST(PHP_PSI_HEADERS)
+       PHP_SUBST(PHP_PSI_SOURCES)
+
+       PHP_SUBST(PHP_PSI_SRCDIR)
+       PHP_SUBST(PHP_PSI_BUILDDIR)
+])
+
+dnl PSI_CHECK_GNU_SOURCE
+dnl Check whether _GNU_SOURCE is already defined.
+AC_DEFUN([PSI_CHECK_GNU_SOURCE], [
+       AC_MSG_CHECKING([for _GNU_SOURCE])
+       AC_EGREP_CPP([gnu_source_not_defined], [
+               #ifndef _GNU_SOURCE
+               gnu_source_not_defined
+               #endif
+       ], [
+               AC_MSG_RESULT([needs define])
+               AC_DEFINE([_GNU_SOURCE], [1], [ ])
+       ], [
+               AC_MSG_RESULT([already defined])
+       ])
+])
+
+dnl PSI_CHECK_CPP
+dnl Queries the preprocessor about predefined macros and include search paths
+AC_DEFUN([PSI_CHECK_CPP], [
+       AC_PROG_AWK
+
        AC_MSG_CHECKING(for preprocessor defaults)
        psi_cpp_predef=`$CPP -Wp,-dM $CPPFLAGS -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -U__BLOCKS__ - </dev/null`
        psi_cpp_search=`$CPP -Wp,-v $CPPFLAGS -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 - </dev/null 2>&1 >/dev/null \
@@ -30,7 +118,9 @@ AC_DEFUN(PSI_CONFIG_INIT, [
        `
        psi_cpp_predef_count=`printf %s "$psi_cpp_predef" | wc -l`
        psi_cpp_search_count=`printf %s "$psi_cpp_search" | wc -l`
+       
        AC_MSG_RESULT([$psi_cpp_predef_count predefined macros, and $psi_cpp_search_count search paths])
+       
        PSI_CPP_PREDEF=`printf "%s\n" "$psi_cpp_predef" | \
                $AWK '{
                        gsub(/"/, "\\\\\"");
@@ -38,62 +128,56 @@ AC_DEFUN(PSI_CONFIG_INIT, [
                }' \
        `
        PSI_CPP_SEARCH=`printf %s "$psi_cpp_search" | \
-               $AWK '{
-                       if (i) printf ":";
-                       gsub(/^@<:@@<:@:space:@:>@@:>@+/,"");
-                       gsub(/@<:@@<:@:space:@:>@@:>@+$/,"");
-                       printf "%s", $[]0;
-                       ++i
-               }' \
+               $AWK '
+                       {
+                               if (i) printf "\":\" ";
+                               else   printf "    ";
+                               gsub(/^@<:@@<:@:space:@:>@@:>@+/,"");
+                               gsub(/@<:@@<:@:space:@:>@@:>@+$/,"");
+                               printf "\"%s\"\n", $[]0;
+                               ++i
+                       }' \
        `
+])
 
+dnl PSI_CHECK_MAINTAINER_MODE
+dnl Check for --enable-psi-maintainer-mode
+dnl Enables Makefile dependencies and extra compile warnings
+AC_DEFUN([PSI_CHECK_MAINTAINER_MODE], [
        if test "$PHP_PSI_MAINTAINER_MODE" = "yes"; then
                PSI_DEPS=true
                PHP_SUBST(PSI_DEPS)
-               EXTRA_CFLAGS="-Wall -Wextra $EXTRA_CFLAGS"
+               CFLAGS="$CFLAGS -Wall -Wextra"
        else
                PSI_DEPS=false
        fi
-       
-       PSI_STDTYPES=
-       PSI_CONSTS=
-       
-       AC_CONFIG_FILES(
-               [$PHP_PSI_BUILDDIR/php_psi_stdinc.h:$PHP_PSI_SRCDIR/php_psi_stdinc.h.in]
-               [$PHP_PSI_BUILDDIR/php_psi_posix.h:$PHP_PSI_SRCDIR/php_psi_posix.h.in]
-               [$PHP_PSI_BUILDDIR/php_psi_cpp.h:$PHP_PSI_SRCDIR/php_psi_cpp.h.in]
-       )
-
 ])
 
-dnl PSI_CONFIG_DONE()
-dnl Finish the headers with the pre-defined types etc.
-AC_DEFUN(PSI_CONFIG_DONE, [
-       psi_eval_LIBS=$LIBS
-       LIBS=$psi_save_LIBS
-       PHP_EVAL_LIBLINE($psi_eval_LIBS, PSI_SHARED_LIBADD)
-       
-       [PSI_INCLUDES]="PSI_INCLUDES"
-       AC_SUBST([PSI_INCLUDES])
-       AC_SUBST([PSI_STDTYPES])
-       AC_SUBST([PSI_CONSTS])
-       AC_SUBST([PSI_CPP_SEARCH])
-       AC_SUBST([PSI_CPP_PREDEF])
+dnl PSI_CHECK_THREADED_PARSER
+dnl Check for --enable-psi-threaded-parser
+dnl Enables threaded parser if HAVE_PTHREAD && HAVE_ASPRINTF
+AC_DEFUN([PSI_CHECK_THREADED_PARSER], [
+       if test "$PHP_PSI_THREADED_PARSER" = "yes"; then
+               AC_DEFINE([PSI_THREADED_PARSER], [HAVE_PTHREAD && HAVE_ASPRINTF],
+                       [whether to enable the threaded parser])
+       fi
 ])
 
 AC_DEFUN(PSI_PTHREAD_ONCE, [
        AX_PTHREAD([
                LIBS="$PTHREAD_LIBS $LIBS"
                CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+               AC_DEFINE([HAVE_PTHREAD],[1],
+                       [Define if you have POSIX threads libraries and header files.])
        ])
 ])
-AC_DEFUN(PSI_PTHREAD, [
+AC_DEFUN(PSI_CHECK_PTHREAD, [
        AC_REQUIRE([PSI_PTHREAD_ONCE])
 ])
 
 dnl PSI_INCLUDES()
 dnl Expands to a complete list of include statements including
-dnl autoconf's defaults.
+dnl autoconf defaults.
 AC_DEFUN(PSI_INCLUDES, [
 #ifndef _GNU_SOURCE
 # define _GNU_SOURCE
@@ -211,22 +295,10 @@ dnl PSI_SH_SIZEOF(type)
 dnl expand to shell variable $ac_cv_sizeof_<TYPE>
 AC_DEFUN([PSI_SH_SIZEOF], [$AS_TR_SH([ac_cv_sizeof_]$1)])
 
-dnl PSI_SH_OFFSETOF(type)
-dnl Expand to shell variable $ac_cv_offsetof_<TYPE>
-AC_DEFUN([PSI_SH_OFFSETOF], [$AS_TR_SH([ac_cv_offsetof_]$1)])
-
-dnl PSI_SH_ALIGNOF(type)
-dnl Expand to shell variable $ac_cv_offsetof_<TYPE>
-AC_DEFUN([PSI_SH_ALIGNOF], [$AS_TR_SH([ac_cv_alignof_]$1)])
-
 dnl PSI_SH_TEST_SIZEOF(type)
 dnl `if` condition to test if $ac_cv_sizeof_$1 is greater than 0.
 AC_DEFUN([PSI_SH_TEST_SIZEOF], [test -n "$AS_TR_SH([ac_cv_sizeof_]$1)" && test "$AS_TR_SH([ac_cv_sizeof_]$1)" -gt 0])
 
-dnl PSI_SH_TEST_ALIGNOF(type)
-dnl `if` condition to test if $ac_cv_alignof_$1 is greater than 0.
-AC_DEFUN([PSI_SH_TEST_ALIGNOF], [test -n "$AS_TR_SH([ac_cv_alignof_]$1)" && test "$AS_TR_SH([ac_cv_alignof_]$1)" -gt 0])
-
 dnl PSI_CHECK_SIZEOF(type, special-includes)
 dnl AC_CHECK_SIZEOF wrapper with PSI_INCLUDES
 dnl Defines psi\\SIZEOF_<TYPE> pre-defined constant in $PSI_CONSTS_H.
@@ -238,50 +310,6 @@ AC_DEFUN(PSI_CHECK_SIZEOF, [
        fi
 ])
 
-dnl PSI_CHECK_ALIGNOF(type, special-includes)
-dnl AC_CHECK_ALIGNOF wrapper with PSI_INCLUDES
-dnl Defines psi\\ALIGNOF_<TYPE> pre-defined constant in $PSI_CONSTS_H.
-AC_DEFUN(PSI_CHECK_ALIGNOF, [
-       AC_CHECK_ALIGNOF($1, PSI_INCLUDES
-               $2)
-       if PSI_SH_TEST_ALIGNOF($1); then
-               psi_add_int_const "AS_TR_CPP([ALIGNOF_]$1)" "$AS_TR_SH([ac_cv_alignof_]$1)"
-       fi
-])
-
-dnl PSI_CHECK_OFFSETOF(struct, element)
-dnl Check the offset of a struct element, implemented in the similar manner
-dnl like AC_CHECK_SIZEOF.
-dnl AC_DEFINEs OFFSETOF_<STRUCT>_<ELEMENT>.
-AC_DEFUN(PSI_CHECK_OFFSETOF, [
-       _AC_CACHE_CHECK_INT(
-               [offset of $2 in $1],
-               [AS_TR_SH([ac_cv_offsetof_$1_$2])],
-               [(long int) (offsetof ($1, $2))],
-               PSI_INCLUDES,
-               [AC_MSG_FAILURE([cannot compute offsetof ($1, $2)])]
-       )
-       AC_DEFINE_UNQUOTED(
-               AS_TR_CPP(offsetof_$1_$2),
-               $AS_TR_SH([ac_cv_offsetof_$1_$2]),
-               [The offset of `$2' in `$1', as computed by offsetof.]
-       )
-])
-
-
-dnl PSI_COMPUTE_STR(variable, string or expression)
-dnl Compute a string constant value in a similar manner like AC_COMPUTE_INT.
-AC_DEFUN(PSI_COMPUTE_STR, [
-       AC_TRY_RUN(
-               PSI_INCLUDES
-               [int main() {
-                       return EOF == fputs($2, fopen("conftest.out", "w"));
-               }
-       ], [
-               eval $1=\\\"`cat conftest.out`\\\"
-       ])
-])
-
 dnl PSI_CHECK_LIBJIT()
 dnl Check for libjit in $PHP_PSI_LIBJIT or standard locations
 dnl AC_DEFINEs HAVE_LIBJIT.
@@ -331,8 +359,8 @@ AC_DEFUN(PSI_CHECK_LIBFFI, [
                AC_MSG_CHECKING(for libffi)
                psi_cv_libffi_dir=`$PKG_CONFIG --variable=prefix libffi`
                AC_MSG_RESULT($psi_cv_libffi_dir)
-               PHP_EVAL_INCLINE(`$PKG_CONFIG --cflags libffi`)
-               PHP_EVAL_LIBLINE(`$PKG_CONFIG --libs libffi`, PSI_SHARED_LIBADD)
+               PHP_EVAL_INCLINE([`$PKG_CONFIG --cflags libffi`])
+               PHP_EVAL_LIBLINE([`$PKG_CONFIG --libs libffi`], PSI_SHARED_LIBADD) dnl `
                AC_DEFINE(HAVE_LIBFFI, 1, Have libffi)
                AC_DEFINE_UNQUOTED([PHP_PSI_LIBFFI_VERSION], ["`$PKG_CONFIG --modversion libffi`"], [libffi version])
        else
index 3069ba4..c1554b6 100644 (file)
@@ -26,7 +26,9 @@
 /* generated by configure */
 
 #ifdef PSI_CPP_SEARCH
-static const char psi_cpp_search[] = "@PSI_CPP_SEARCH@";
+static const char psi_cpp_search[] = ""
+@PSI_CPP_SEARCH@
+;
 #endif
 
 #ifdef PSI_CPP_PREDEF
index 57b57f0..ec9d49b 100644 (file)
@@ -198,7 +198,7 @@ zval *psi_call_frame_sub_argument(struct psi_call_frame *frame,
        if (!iarg) {
                struct psi_call_frame_argument *frame_arg;
                impl_val empty_val = {0};
-               zend_string *type_str = zend_string_init_interned(ZEND_STRL("mixed"), 1);
+               zend_string *type_str = psi_string_init_interned(ZEND_STRL("mixed"), 1);
                struct psi_impl_arg *carg_spec = psi_impl_arg_init(
                                psi_impl_type_init(PSI_T_MIXED, type_str),
                                psi_impl_var_copy(inner_var), NULL);
index ecccdb0..4dcd920 100644 (file)
 # endif
 #endif
 
+#include <unistd.h>
 #include <fnmatch.h>
 
+#if PSI_THREADED_PARSER
+# include <pthread.h>
+#endif
+
 #include "php_scandir.h"
 #include "php_psi.h"
 #include "calc.h"
@@ -129,14 +134,6 @@ struct psi_context *psi_context_init(struct psi_context *C, struct psi_context_o
        return C;
 }
 
-static int psi_select_dirent(const struct dirent *entry)
-{
-#ifndef FNM_CASEFOLD
-# define FNM_CASEFOLD 0
-#endif
-       return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
-}
-
 static bool psi_context_add(struct psi_context *C, struct psi_parser *P)
 {
        bool valid;
@@ -154,61 +151,214 @@ static bool psi_context_add(struct psi_context *C, struct psi_parser *P)
        return valid;
 }
 
+struct psi_context_build_worker {
+       pthread_t tid;
+       struct psi_parser parser;
+       struct psi_parser_input *input;
+       char psi_file[MAXPATHLEN];
+};
+
+static struct psi_context_build_worker *psi_context_build_worker_init(
+               struct psi_context *C, const char *dir, const char *file)
+{
+       struct psi_context_build_worker *w = pecalloc(1, sizeof(*w), 1);
+
+       if (MAXPATHLEN <= slprintf(w->psi_file, MAXPATHLEN, "%s/%s", dir, file)) {
+               C->error(PSI_DATA(C), NULL, PSI_WARNING, "Path to PSI file too long: %s/%s",
+                       dir, file);
+               pefree(w, 1);
+               return NULL;
+       }
+       if (!psi_parser_init(&w->parser, C->error, C->flags)) {
+               C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to init PSI parser (%s): %s",
+                       w->psi_file, strerror(errno));
+               pefree(w, 1);
+               return NULL;
+       }
+       return w;
+}
+
+#if PSI_THREADED_PARSER
+static void *psi_context_build_worker_thread(void *thread_ptr)
+{
+       struct psi_context_build_worker *thread = thread_ptr;
+       psi_parser_parse(&thread->parser, thread->input);
+       return NULL;
+}
+
+static bool psi_context_build_worker_thread_start(
+               struct psi_context_build_worker *w)
+{
+       unsigned tries = 0;
+       int rc;
+
+again: ;
+       rc = pthread_create(&w->tid, NULL, psi_context_build_worker_thread, w);
+
+       switch (rc) {
+       case 0:
+               return true;
+       case EAGAIN:
+               if (tries++ < 10) {
+                       goto again;
+               }
+               /* no break */
+       default:
+               w->parser.error(PSI_DATA(&w->parser), NULL, PSI_WARNING,
+                               "Failed to start parser thread: %s", strerror(rc));
+               w->tid = 0;
+               return false;
+       }
+}
+#endif
+
+static bool psi_context_build_worker_exec(struct psi_context_build_worker *w)
+{
+       if (!(w->input = psi_parser_open_file(&w->parser, w->psi_file, true))) {
+               w->parser.error(PSI_DATA(&w->parser), NULL, PSI_WARNING,
+                               "Failed to open PSI file (%s): %s", w->psi_file, strerror(errno));
+               return false;
+       }
+#if PSI_THREADED_PARSER
+       return psi_context_build_worker_thread_start(w);
+#else
+       return psi_parser_parse(&w->parser, w->input);
+#endif
+}
+
+static bool psi_context_build_worker_done(struct psi_context_build_worker *w)
+{
+#if PSI_THREADED_PARSER
+       void *rval = NULL;
+
+       if (!w->tid) {
+               return true;
+       }
+
+# if HAVE_PTHREAD_TRYJOIN_NP
+       if (0 == pthread_tryjoin_np(w->tid, &rval)) {
+               w->tid = 0;
+               return true;
+       }
+# else
+       if (0 == pthread_join(w->tid, &rval)) {
+               w->tid = 0;
+               return true;
+       }
+# endif
+       return false;
+#else
+       return true;
+#endif
+}
+
+static void psi_context_build_worker_dtor(struct psi_context_build_worker *w)
+{
+#if PSI_THREADED_PARSER
+       if (w->tid) {
+               void *rval;
+               int rc = pthread_join(w->tid, &rval);
+
+               if (rc) {
+                       w->parser.error(PSI_DATA(&w->parser), NULL, PSI_WARNING,
+                                       "Failed to finish parser thread: %s", strerror(errno));
+               }
+       }
+#endif
+       psi_parser_input_free(&w->input);
+       psi_parser_dtor(&w->parser);
+}
+
+static void psi_context_build_worker_free(struct psi_context_build_worker **w)
+{
+       if (*w) {
+               psi_context_build_worker_dtor(*w);
+               pefree(*w, 1);
+               *w = NULL;
+       }
+}
+
+static int psi_select_dirent(const struct dirent *entry)
+{
+#ifndef FNM_CASEFOLD
+# define FNM_CASEFOLD 0
+#endif
+       return 0 == fnmatch("*.psi", entry->d_name, FNM_CASEFOLD);
+}
+
 void psi_context_build(struct psi_context *C, const char *paths)
 {
-       int i, n;
        char *sep = NULL, *cpy = strdup(paths), *ptr = cpy;
-       struct dirent **entries;
+       struct psi_context_build_worker *worker;
+       struct psi_plist *workers = psi_plist_init(
+                       (psi_plist_dtor) psi_context_build_worker_free);
 
        do {
-               sep = strchr(ptr, ':');
+               struct dirent **entries = NULL;
+               int i, n;
 
-               if (sep) {
+               if ((sep = strchr(ptr, ':'))) {
                        *sep = 0;
                }
 
-               entries = NULL;
                n = php_scandir(ptr, &entries, psi_select_dirent, alphasort);
 
-               if (n > 0) {
+               if (n < 0) {
+                       C->error(PSI_DATA(C), NULL, PSI_WARNING,
+                                       "Failed to scan PSI directory '%s':%s", strerror(errno));
+               } else {
                        for (i = 0; i < n; ++i) {
-                               char psi[MAXPATHLEN];
-                               struct psi_parser P;
-                               struct psi_parser_input *I;
-
-                               if (MAXPATHLEN <= slprintf(psi, MAXPATHLEN, "%s/%s", ptr, entries[i]->d_name)) {
-                                       C->error(PSI_DATA(C), NULL, PSI_WARNING, "Path to PSI file too long: %s/%s",
-                                               ptr, entries[i]->d_name);
-                               }
-                               if (!psi_parser_init(&P, C->error, C->flags)) {
-                                       C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to init PSI parser (%s): %s",
-                                               psi, strerror(errno));
-                                       continue;
-                               }
-                               if (!(I = psi_parser_open_file(&P, psi, true))) {
-                                       C->error(PSI_DATA(C), NULL, PSI_WARNING, "Failed to open PSI file (%s): %s",
-                                               psi, strerror(errno));
-                                       continue;
+                               worker = psi_context_build_worker_init(C, ptr, entries[i]->d_name);
+                               if (worker) {
+                                       workers = psi_plist_add(workers, &worker);
                                }
-                               psi_parser_parse(&P, I);
-                               psi_context_add(C, &P);
-                               psi_parser_dtor(&P);
-                               psi_parser_input_free(&I);
-                       }
-               }
-
-               if (entries) {
-                       for (i = 0; i < n; ++i) {
                                free(entries[i]);
                        }
                        free(entries);
                }
-
                ptr = sep + 1;
        } while (sep);
 
        free(cpy);
 
+       if (psi_plist_count(workers)) {
+               struct psi_plist *running = psi_plist_init(
+                               (psi_plist_dtor) psi_context_build_worker_free);
+               long active = 0;
+#ifdef _SC_NPROCESSORS_ONLN
+               long pool = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+               long pool = 4;
+#endif
+
+               while (psi_plist_count(workers) && active < pool) {
+                       if (psi_plist_pop(workers, &worker)) {
+                               if (psi_context_build_worker_exec(worker)) {
+                                       running = psi_plist_add(running, &worker);
+                                       ++active;
+                               }
+                       }
+               }
+               while (active) {
+                       size_t i = 0;
+
+                       while (psi_plist_get(running, i++, &worker)) {
+                               if (psi_context_build_worker_done(worker)) {
+                                       psi_context_add(C, &worker->parser);
+
+                                       psi_plist_del(running, --i, NULL);
+                                       psi_context_build_worker_free(&worker);
+
+                                       if (psi_plist_pop(workers, &worker)) {
+                                               psi_plist_add(running, &worker);
+                                       } else {
+                                               --active;
+                                       }
+                               }
+                       }
+               }
+       }
+
        psi_context_compile(C);
 }
 
index 9daca1b..d703bda 100644 (file)
--- a/src/cpp.c
+++ b/src/cpp.c
@@ -202,7 +202,7 @@ static bool psi_cpp_stage1(struct psi_cpp *cpp)
 
                                        no_ws->type = PSI_T_NO_WHITESPACE;
                                        zend_string_release(no_ws->text);
-                                       no_ws->text = zend_string_init_interned("\xA0", 1, 1);
+                                       no_ws->text = psi_string_init_interned("\xA0", 1, 1);
                                        psi_cpp_tokiter_add(cpp, no_ws);
                                        continue;
                                }
index 5d7be99..57b6f4d 100644 (file)
 #include "data.h"
 
 #include "php_globals.h"
+#include "zend_types.h"
 
 #include <dlfcn.h>
 #include <ctype.h>
 
+#if PSI_THREADED_PARSER
+# include <pthread.h>
+
+pthread_mutex_t psi_string_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+zend_string *psi_string_init_interned(const char *buf, size_t len, int p)
+{
+       zend_string *str;
+
+       pthread_mutex_lock(&psi_string_mutex);
+       str = zend_string_init_interned(buf, len, p);
+       pthread_mutex_unlock(&psi_string_mutex);
+
+       return str;
+}
+
+zend_string *psi_new_interned_string(zend_string *str)
+{
+       zend_string *new_str;
+
+       pthread_mutex_lock(&psi_string_mutex);
+       new_str = zend_new_interned_string(str);
+       pthread_mutex_unlock(&psi_string_mutex);
+
+       return new_str;
+}
+
+#endif
+
 static void psi_data_ctor_internal(struct psi_data *data,
                psi_error_cb error, unsigned flags)
 {
index 9f41936..29d452c 100644 (file)
 # define RTLD_DEFAULT ((void *) 0)
 #endif
 
+#if PSI_THREADED_PARSER
+zend_string *psi_string_init_interned(const char *buf, size_t len, int p);
+zend_string *psi_new_interned_string(zend_string *str);
+#else
+# define psi_string_init_interned zend_string_init_interned
+# define psi_new_interned_string zend_new_interned_string
+#endif
+
 static inline void *psi_dlsym(struct psi_plist *dllist, const char *name, const char *redir)
 {
        void *dl, *sym = NULL;
index 2402816..cf06f6a 100644 (file)
@@ -98,7 +98,7 @@ struct psi_parser_input *psi_parser_open_file(struct psi_parser *P,
        memset(fb->buffer + sb.st_size, 0, psi_parser_maxfill());
 
        fb->length = sb.st_size;
-       fb->file = zend_string_init_interned(filename, strlen(filename), 1);
+       fb->file = psi_string_init_interned(filename, strlen(filename), 1);
 
        return fb;
 }
@@ -119,7 +119,7 @@ struct psi_parser_input *psi_parser_open_string(struct psi_parser *P,
        memset(sb->buffer + length, 0, psi_parser_maxfill());
 
        sb->length = length;
-       sb->file = zend_string_init_interned("<stdin>", strlen("<stdin>"), 1);
+       sb->file = psi_string_init_interned("<stdin>", strlen("<stdin>"), 1);
 
        return sb;
 }
@@ -150,23 +150,44 @@ static inline zend_string *macro_to_constant(struct psi_parser *parser,
        size_t i = 0;
        struct psi_token *tok;
 
+#if HAVE_ASPRINTF
+       int persistent = 1;
+
+       smart_str_appendl_ex(&str, ZEND_STRL("const psi\\"), 1);
+       smart_str_append_ex(&str, name, 1);
+       smart_str_appendl_ex(&str, ZEND_STRL(" = "), 1);
+#else
+       int persistent = 0;
+
        smart_str_append_printf(&str, "const psi\\%s = ", name->val);
+#endif
        if (scope->macro->exp) {
+#if HAVE_ASPRINTF
+               char *astr = NULL;
+               struct psi_dump dump = {{.hn = &astr},
+                               .fun = (psi_dump_cb) asprintf};
+#else
                struct psi_dump dump = {{.hn = &str},
                                .fun = (psi_dump_cb) smart_str_append_printf};
+#endif
 
                psi_num_exp_dump(&dump, scope->macro->exp);
+
+#if HAVE_ASPRINTF
+               smart_str_appends_ex(&str, astr, 1);
+               free(astr);
+#endif
        } else while (psi_plist_get(scope->macro->tokens, i++, &tok)) {
                if (tok->type == PSI_T_QUOTED_STRING) {
-                       smart_str_appendc(&str, '"');
+                       smart_str_appendc_ex(&str, '"', persistent);
                }
-               smart_str_append(&str, tok->text);
+               smart_str_append_ex(&str, tok->text, persistent);
                if (tok->type == PSI_T_QUOTED_STRING) {
-                       smart_str_appendc(&str, '"');
+                       smart_str_appendc_ex(&str, '"', persistent);
                }
-               smart_str_appendc(&str, ' ');
+               smart_str_appendc_ex(&str, ' ', persistent);
        }
-       smart_str_appendl(&str, ";\n", 2);
+       smart_str_appendl_ex(&str, ";\n", 2, persistent);
        return smart_str_extract(&str);
 }
 
index 048305a..b749f73 100644 (file)
@@ -148,6 +148,14 @@ bool psi_plist_unset(struct psi_plist *list, size_t index) {
        return false;
 }
 
+bool psi_plist_set(struct psi_plist *list, size_t index, void *ptr) {
+       if (list && list->count > index) {
+               PLIST_CPY(list, PLIST_ELE(list, index), ptr);
+               return true;
+       }
+       return false;
+}
+
 bool psi_plist_del(struct psi_plist *list, size_t index, void *ptr) {
        if (list && list->count > index) {
                if (ptr) {
index e119ead..94fd498 100644 (file)
@@ -47,6 +47,7 @@ bool psi_plist_del_r(struct psi_plist *list, size_t offset_start, size_t offset_
 struct psi_plist *psi_plist_ins(struct psi_plist *list, size_t index, void *ptr);
 struct psi_plist *psi_plist_ins_r(struct psi_plist *list, size_t offset_start, size_t num_eles, void **eles);
 
+bool psi_plist_set(struct psi_plist *list, size_t index, void *ptr);
 bool psi_plist_unset(struct psi_plist *list, size_t index);
 
 bool psi_plist_shift(struct psi_plist *list, void *ptr);
index 1b4cc5e..832b040 100644 (file)
@@ -46,7 +46,7 @@ struct psi_token *psi_token_init(token_t token_typ, const char *token_txt,
        T->col = col;
        T->line = line;
        T->file = zend_string_copy(file);
-       T->text = zend_string_init_interned(token_txt, token_len, 1);
+       T->text = psi_string_init_interned(token_txt, token_len, 1);
 #if PSI_DEBUG_TOKEN_ALLOC
        PSI_DEBUG_PRINT(cpp->parser, "PSI: token_init %p\n", T);
 #endif
index a92c8ad..02fdc50 100644 (file)
@@ -31,7 +31,7 @@ struct psi_decl_abi *psi_decl_abi_init(zend_string *convention)
        struct psi_decl_abi *abi = pecalloc(1, sizeof(*abi), 1);
        abi->convention = convention
                        ? zend_string_copy(convention)
-                       : zend_string_init_interned(ZEND_STRL("default"), 1);
+                       : psi_string_init_interned(ZEND_STRL("default"), 1);
        return abi;
 }
 
index 24ff39a..3427328 100644 (file)
@@ -100,7 +100,7 @@ void psi_decl_extvar_dump(struct psi_dump *dump, struct psi_decl_extvar *evar)
 
 struct psi_decl *psi_decl_extvar_setter(struct psi_decl_extvar *evar)
 {
-       zend_string *type_str = zend_string_init_interned(ZEND_STRS("void"), 1);
+       zend_string *type_str = psi_string_init_interned(ZEND_STRS("void"), 1);
        struct psi_decl_type *func_type = psi_decl_type_init(PSI_T_VOID, type_str);
        struct psi_decl_var *func_var = psi_decl_var_copy(evar->arg->var);
        struct psi_decl_arg *func = psi_decl_arg_init(func_type, func_var);
@@ -119,7 +119,7 @@ struct psi_decl *psi_decl_extvar_setter(struct psi_decl_extvar *evar)
        smart_str_append_ex(&name, func_var->name, 1);
        smart_str_appendl_ex(&name, ZEND_STRL("_set"), 1);
        zend_string_release(func_var->name);
-       func_var->name = zend_new_interned_string(smart_str_extract(&name));
+       func_var->name = psi_new_interned_string(smart_str_extract(&name));
 
        decl->extvar = 1;
 
@@ -142,7 +142,7 @@ struct psi_decl *psi_decl_extvar_getter(struct psi_decl_extvar *evar)
        smart_str_append_ex(&name, func_var->name, 1);
        smart_str_appendl_ex(&name, ZEND_STRL("_get"), 1);
        zend_string_release(func_var->name);
-       func_var->name = zend_new_interned_string(smart_str_extract(&name));
+       func_var->name = psi_new_interned_string(smart_str_extract(&name));
 
        decl->extvar = 1;