travis: update
[m6w6/ext-psi] / src / context.c
index ecccdb0546d88e16bc8122fb68eb5a820e38106f..5776e9fbd997c09eb1aec6b92718f1ddd4b74f2e 100644 (file)
@@ -23,7 +23,9 @@
  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"
@@ -56,8 +63,7 @@
 #include "token.h"
 #include "parser.h"
 
-#include "php_psi_posix.h"
-
+PHP_MINIT_FUNCTION(psi_context);
 PHP_MINIT_FUNCTION(psi_context)
 {
        unsigned flags = 0;
@@ -95,6 +101,7 @@ PHP_MINIT_FUNCTION(psi_context)
        return SUCCESS;
 }
 
+PHP_MSHUTDOWN_FUNCTION(psi_context);
 PHP_MSHUTDOWN_FUNCTION(psi_context)
 {
        if (psi_check_env("PSI_DUMP")) {
@@ -129,14 +136,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 +153,229 @@ static bool psi_context_add(struct psi_context *C, struct psi_parser *P)
        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);
 }
 
@@ -524,6 +691,7 @@ void **psi_context_composite_type_elements(struct psi_context *C,
        struct psi_decl_type *dtype;
        struct psi_decl_arg *tmp;
        void *type, *copy;
+       size_t i;
 
        dtype = psi_decl_type_get_real(darg->type);
 
@@ -540,7 +708,7 @@ void **psi_context_composite_type_elements(struct psi_context *C,
                break;
        default:
                type = psi_context_decl_arg_type(C, darg);
-               for (size_t i = 0; i < darg->var->array_size; ++i) {
+               for (i = 0; i < darg->var->array_size; ++i) {
                        copy = C->ops->copyof_type(C, type);
                        *eles = psi_plist_add(*eles, &copy);
                }
@@ -549,46 +717,6 @@ void **psi_context_composite_type_elements(struct psi_context *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, &copy);
-       }
-}
-
-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);
@@ -604,32 +732,32 @@ void psi_context_compile(struct psi_context *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)) {
@@ -638,11 +766,11 @@ ZEND_RESULT_CODE psi_context_call(struct psi_context *C, zend_execute_data *exec
                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);
@@ -650,7 +778,7 @@ ZEND_RESULT_CODE psi_context_call(struct psi_context *C, zend_execute_data *exec
        psi_call_frame_do_free(frame);
        psi_call_frame_free(frame);
 
-       return SUCCESS;
+       return true;
 }