[ --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], [ ], [ ])
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
-
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 \
`
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(/"/, "\\\\\"");
}' \
`
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
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.
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.
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
/* 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
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);
# 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"
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;
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);
}
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;
}
#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)
{
# 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;
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;
}
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;
}
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);
}
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) {
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);
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
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;
}
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);
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;
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;