From: Michael Wallner Date: Mon, 3 Dec 2018 08:15:55 +0000 (+0100) Subject: * threaded parser X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=f76cbab7698c988217d56a96f3ef68005cddd5ec;p=m6w6%2Fext-psi * threaded parser * simplify configure --- diff --git a/config.m4 b/config.m4 index f63f52d..5ea9c37 100644 --- 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 diff --git a/m4/psi/psi.m4 b/m4/psi/psi.m4 index 9701afc..c534ed4 100644 --- a/m4/psi/psi.m4 +++ b/m4/psi/psi.m4 @@ -1,16 +1,104 @@ - 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__ - &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_ 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_ -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_ -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_ 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_ 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__. -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 diff --git a/php_psi_cpp.h.in b/php_psi_cpp.h.in index 3069ba4..c1554b6 100644 --- a/php_psi_cpp.h.in +++ b/php_psi_cpp.h.in @@ -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 diff --git a/src/call.c b/src/call.c index 57b57f0..ec9d49b 100644 --- a/src/call.c +++ b/src/call.c @@ -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); diff --git a/src/context.c b/src/context.c index ecccdb0..4dcd920 100644 --- a/src/context.c +++ b/src/context.c @@ -44,8 +44,13 @@ # endif #endif +#include #include +#if PSI_THREADED_PARSER +# include +#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); } diff --git a/src/cpp.c b/src/cpp.c index 9daca1b..d703bda 100644 --- 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; } diff --git a/src/data.c b/src/data.c index 5d7be99..57b6f4d 100644 --- a/src/data.c +++ b/src/data.c @@ -28,10 +28,40 @@ #include "data.h" #include "php_globals.h" +#include "zend_types.h" #include #include +#if PSI_THREADED_PARSER +# include + +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) { diff --git a/src/data.h b/src/data.h index 9f41936..29d452c 100644 --- a/src/data.h +++ b/src/data.h @@ -44,6 +44,14 @@ # 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; diff --git a/src/parser.c b/src/parser.c index 2402816..cf06f6a 100644 --- a/src/parser.c +++ b/src/parser.c @@ -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("", strlen(""), 1); + sb->file = psi_string_init_interned("", strlen(""), 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); } diff --git a/src/plist.c b/src/plist.c index 048305a..b749f73 100644 --- a/src/plist.c +++ b/src/plist.c @@ -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) { diff --git a/src/plist.h b/src/plist.h index e119ead..94fd498 100644 --- a/src/plist.h +++ b/src/plist.h @@ -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); diff --git a/src/token.c b/src/token.c index 1b4cc5e..832b040 100644 --- a/src/token.c +++ b/src/token.c @@ -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 diff --git a/src/types/decl_abi.c b/src/types/decl_abi.c index a92c8ad..02fdc50 100644 --- a/src/types/decl_abi.c +++ b/src/types/decl_abi.c @@ -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; } diff --git a/src/types/decl_extvar.c b/src/types/decl_extvar.c index 24ff39a..3427328 100644 --- a/src/types/decl_extvar.c +++ b/src/types/decl_extvar.c @@ -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;