OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
-#include "php_psi_stdinc.h"
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
#include "php.h"
# 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"
#include "token.h"
#include "parser.h"
-#include "php_psi_posix.h"
-
+PHP_MINIT_FUNCTION(psi_context);
PHP_MINIT_FUNCTION(psi_context)
{
unsigned flags = 0;
return SUCCESS;
}
+PHP_MSHUTDOWN_FUNCTION(psi_context);
PHP_MSHUTDOWN_FUNCTION(psi_context)
{
if (psi_check_env("PSI_DUMP")) {
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 {
+#if PSI_THREADED_PARSER
+ pthread_t tid;
+#endif
+ struct psi_parser parser;
+ struct psi_parser_input *input;
+ char psi_file[PATH_MAX];
+};
+
+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 (PATH_MAX <= slprintf(w->psi_file, PATH_MAX, "%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) {
+ char cwd[PATH_MAX];
+ C->error(PSI_DATA(C), NULL, PSI_WARNING,
+ "Failed to scan PSI directory '%s%s%s': %s",
+ *ptr == '/' ? "" : getcwd(cwd, PATH_MAX),
+ *ptr != '/' && *ptr != '.' ? "/" : "",
+ ptr, 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);
+ PSI_DEBUG_PRINT(C, "PSI: init worker(%p) for %s/%s\n",
+ worker, 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)) {
+ PSI_DEBUG_PRINT(C, "PSI: starting worker %p\n", 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_DEBUG_PRINT(C, "PSI: collecting worker %p\n", 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_DEBUG_PRINT(C, "PSI: starting worker %p\n", worker);
+ if (psi_context_build_worker_exec(worker)) {
+ running = psi_plist_add(running, &worker);
+ }
+ } else {
+ --active;
+ }
+ }
+ }
+ }
+ psi_plist_free(running);
+ }
+ psi_plist_free(workers);
+
psi_context_compile(C);
}
return psi_plist_eles(*eles);
}
-/*
-void psi_context_decl_func_array_elements(struct psi_context *C,
- struct psi_decl *fn, struct psi_plist **els)
-{
- void *type;
- size_t i;
-
- if (fn->func->var->pointer_level > 1) {
- type = C->ops->typeof_decl(C, PSI_T_POINTER);
- } else {
- type = psi_context_decl_type(C, fn->func->type);
- }
-
- for (i = 0; i < fn->func->var->array_size; ++i) {
- void *copy = C->ops->copyof_type(C, type);
- *els = psi_plist_add(*els, ©);
- }
-}
-
-void *psi_context_decl_func_type(struct psi_context *C, struct psi_decl *fn)
-{
- struct psi_decl_arg *darg = fn->func;
-
- if (darg->engine.type) {
- return darg->engine.type;
- }
-
- if (darg->var->pointer_level) {
- if (!darg->var->array_size) {
- return C->ops->typeof_decl(C, PSI_T_POINTER);
- } else {
- C->ops->composite_init(C, darg);
- return darg->engine.type;
- }
- }
-
- return psi_context_decl_type(C, darg->type);
-}
-*/
-
void psi_context_compile(struct psi_context *C)
{
psi_context_consts_init(C);
EG(current_module) = NULL;
}
-ZEND_RESULT_CODE psi_context_call(struct psi_context *C, zend_execute_data *execute_data, zval *return_value, struct psi_impl *impl)
+bool psi_context_call(struct psi_context *C, zend_execute_data *execute_data, zval *return_value, struct psi_impl *impl)
{
struct psi_call_frame *frame;
frame = psi_call_frame_init(C, impl->decl, impl);
- if (SUCCESS != psi_call_frame_parse_args(frame, execute_data)) {
+ if (!psi_call_frame_parse_args(frame, execute_data)) {
psi_call_frame_free(frame);
- return FAILURE;
+ return false;
}
psi_call_frame_enter(frame);
- if (SUCCESS != psi_call_frame_do_let(frame)) {
+ if (!psi_call_frame_do_let(frame)) {
psi_call_frame_do_return(frame, return_value);
psi_call_frame_free(frame);
- return FAILURE;
+ return false;
}
- if (SUCCESS != psi_call_frame_do_assert(frame, PSI_ASSERT_PRE)) {
+ if (!psi_call_frame_do_assert(frame, PSI_ASSERT_PRE)) {
psi_call_frame_do_return(frame, return_value);
psi_call_frame_free(frame);
- return FAILURE;
+ return false;
}
if (psi_call_frame_num_var_args(frame)) {
C->ops->call(frame);
}
- if (SUCCESS != psi_call_frame_do_assert(frame, PSI_ASSERT_POST)) {
+ if (!psi_call_frame_do_assert(frame, PSI_ASSERT_POST)) {
psi_call_frame_do_return(frame, return_value);
psi_call_frame_free(frame);
- return FAILURE;
+ return false;
}
psi_call_frame_do_return(frame, return_value);
psi_call_frame_do_free(frame);
psi_call_frame_free(frame);
- return SUCCESS;
+ return true;
}