Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
Author[]Andy Green <andy@warmcat.com> 2025-08-13 16:26 UTC
Committer[]Andy Green <andy@warmcat.com> 2025-08-14 19:26 UTC
Treefa8b1601e4d77118d9271c70163b2bee81ba25da   Raw Patch
 
builder: Replace libgit2 with git CLI for performance
builder: Replace libgit2 with git CLI for performance

This essentially removes libgit2 + threadpool to make blocking libgit2 work
async, and replaces it with spawning cli git for speed.

Co-developed-by: Gemini 2.5 Pro
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8eef913..338079f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,9 +136,6 @@ require_lws_config(LWS_ROLE_WS 1 requirements) if (SAI_SERVER) require_lws_config(LWS_WITH_JOSE 1 requirements) endif() -if (SAI_BUILDER) - require_lws_config(LWS_WITH_THREADPOOL 1 requirements) -endif() #require_lws_config(LWS_WITH_PLUGINS_BUILTIN 1 requirements) diff --git a/README.md b/README.md index ae2822d..60ddec8 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,7 @@ Feature|dependency ---|--- either|libwebsockets server|libsqlite3 -builder|libgit2 pthreads +builder|pthreads jig (linux only)|libgpiod #### Unix / Linux @@ -346,17 +346,10 @@ $ sudo systemctl enable sai-builder #### Windows builder only -Build libgit2 via vcpkg, this takes <10mins - -``` -> vcpkg install libgit2:x64-windows -``` - You have to make git2.dll and some deps visible, in /windows/system32 or similar ``` > sudo cp "\Users\<user>\vcpkg\installed\x64-windows\bin\pcre.dll" "\windows\system32" -> sudo cp "\Users\<user>\vcpkg\libgit2_x64-windows\bin\git2.dll" "\windows\system32" ``` Build lws the same way as for unix, except with @@ -369,7 +362,7 @@ Build lws the same way as for unix, except with For sai it's also very similar to unix, but with ``` -> cmake .. -DSAI_SERVER=0 -DSAI_LWS_INC_PATH="\Users\<user>\libwebsockets\build\include" -DSAI_LWS_LIB_PATH="\Users\<user>\libwebsockets\build\lib\Debug\websockets.lib" -DSAI_GIT2_INC_PATH="\Users\<user>\vcpkg\packages\libgit2_x64-windows\include" -DSAI_GIT2_LIB_PATH="\Users\<user>\vcpkg\packages\libgit2_x64-windows\lib\git2.lib" -DSAI_EXT_PTHREAD_INCLUDE_DIR="C:\Program Files (x86)\pthreads\include" -DSAI_EXT_PTHREAD_LIBRARIES="C:\Program Files (x86)\pthreads\lib\x64\libpthreadGC2.a" +> cmake .. -DSAI_SERVER=0 -DSAI_LWS_INC_PATH="\Users\<user>\libwebsockets\build\include" -DSAI_LWS_LIB_PATH="\Users\<user>\libwebsockets\build\lib\Debug\websockets.lib" -DSAI_EXT_PTHREAD_INCLUDE_DIR="C:\Program Files (x86)\pthreads\include" -DSAI_EXT_PTHREAD_LIBRARIES="C:\Program Files (x86)\pthreads\lib\x64\libpthreadGC2.a" > cmake --build . --config DEBUG > sudo cmake --install . --config DEBUG ``` diff --git a/src/builder/CMakeLists.txt b/src/builder/CMakeLists.txt index d5866cd..82eaff7 100644 --- a/src/builder/CMakeLists.txt +++ b/src/builder/CMakeLists.txt @@ -37,54 +37,12 @@ if (requirements) link_directories("/usr/local/lib") endif() - # - # libgit2 paths (builder) - # if (SAI_BUILDER) - # - # libgit2 paths - # - find_path( SAI_GIT2_INC_PATH NAMES "git2.h") - find_library(SAI_GIT2_LIB_PATH NAMES "git2") - - if (SAI_GIT2_INC_PATH AND SAI_GIT2_LIB_PATH) - set(SAI_DEPLIBS "${SAI_GIT2_LIB_PATH}" ${SAI_DEPLIBS}) - include_directories(BEFORE "${SAI_GIT2_INC_PATH}") - else() - message(FATAL_ERROR " Unable to find libgit2") - endif() - - target_link_libraries(${SUB} ${SAI_GIT2_LIB_PATH}) - if (APPLE) target_link_libraries(sai-builder "-framework IOKit") endif() endif() - set(CMAKE_REQUIRED_LIBRARIES ${SAI_GIT2_LIB_PATH}) - - CHECK_C_SOURCE_COMPILES("#include <git2.h> - int main(void) { - const git_error *e = git_error_last(); - return !!e->message; - }" SAI_HAVE_LIBGIT2_GIT_ERROR) - - if (SAI_HAVE_LIBGIT2_GIT_ERROR) - set(CMAKE_C_FLAGS "-DSAI_HAVE_LIBGIT2_GIT_ERROR ${CMAKE_C_FLAGS}") - endif() - - CHECK_C_SOURCE_COMPILES(" - #include <git2.h> - int main(int argc, char **argv) { - git_proxy_options o; - git_proxy_options_init(&o, GIT_PROXY_OPTIONS_VERSION); - return 0; - }" LIBGIT2_HAVE_GIT_PROXY_OPTIONS_INIT) - - if (LIBGIT2_HAVE_GIT_PROXY_OPTIONS_INIT) - set(CMAKE_C_FLAGS "-DLIBGIT2_HAVE_GIT_PROXY_OPTIONS_INIT ${CMAKE_C_FLAGS}") - endif() - #configure_file("${PROJECT_SOURCE_DIR}/cmake/sai_config_private.h.in" # "${PROJECT_BINARY_DIR}/sai_config_private.h") # diff --git a/src/builder/b-comms.c b/src/builder/b-comms.c index aac38f5..8f0fd49 100644 --- a/src/builder/b-comms.c +++ b/src/builder/b-comms.c @@ -51,157 +51,6 @@ saib_m_rx(void *userobj, const uint8_t *buf, size_t len, int flags) } /* - * We come here for every platform's threadpool sync - */ - -static int -tp_sync_check(struct lws_dll2 *d, void *user) -{ - sai_plat_t *sp = lws_container_of(d, sai_plat_t, sai_plat_list); - struct sai_plat_server *spm = (struct sai_plat_server *)user; - struct sai_nspawn *ns; - int n, soe; - void *vp; - - /* - * Let's look into every nspawn for each platform then... - */ - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - sp->nspawn_owner.head) { - - ns = lws_container_of(d, struct sai_nspawn, list); - soe = ns->state; - - /* - * We can't deal with nspawns bound to a different server or - * nspawns not with an active threadpool task - */ - - if (!ns->tp_task || spm != ns->spm) - goto next; - - /* - * We may not be the only threadpool task that wants - * to sync... so bear in mind we want to loop after - * handling this particular one - */ - - // lwsl_notice("%s: tp svc, state %d '%s'\n", __func__, - // ns->state, ns->pending_mirror_log); - - /* - * We got here by threadpool sync... logify the - * >saib> message from the thread... - */ - - if (ns->pending_mirror_log[0]) { - lwsl_notice("%s: logging %s\n", __func__, - ns->pending_mirror_log); - saib_log_chunk_create(ns, ns->pending_mirror_log, - strlen(ns->pending_mirror_log), 3); - ns->pending_mirror_log[0] = 0; - } - - //(soe == NSSTATE_WAIT_REMOTE_MIRROR || - // soe == NSSTATE_FAILED || - // soe == NSSTATE_CHECKOUT || - // soe == NSSTATE_CHECKEDOUT) && - - n = (int)lws_threadpool_task_status(ns->tp_task, &vp); - lwsl_info("%s: WRITEABLE: ss=%p: " - "task %p, priv %p, status %d\n", __func__, spm->ss, - ns->tp_task, vp, n); - switch (n) { - case LWS_TP_STATUS_FINISHED: - case LWS_TP_STATUS_STOPPED: - case LWS_TP_STATUS_QUEUED: - case LWS_TP_STATUS_RUNNING: - case LWS_TP_STATUS_STOPPING: - goto next; - - case LWS_TP_STATUS_SYNCING: - /* - * This is what the threadpool thread wants to hear from - * us in order to continue on. The choice in the second - * arg is whether to ask the thread to stop or not. - * - * MIRROR: let thread continue on to CHECKOUT - * WAIT_REMOTE_MIRROR: continue to wait - * CHECKOUT: go back into CHECKOUT - * FAILED or CHECKEDOUT: we're done, stop the thread - * - * This wakes the stalled task, we can't read its - * state after this - */ - - lws_threadpool_task_sync(ns->tp_task, - soe == NSSTATE_CHECKEDOUT || - soe == NSSTATE_FAILED); - - if (soe != NSSTATE_CHECKEDOUT && - soe != NSSTATE_FAILED) { - lwsl_notice("%s: task still going, status %d, " - "sp = %p, sp->ss = %p\n", - __func__, n, sp, ns->spm->ss); - goto next; - } - /* - * We asked for the task to stop... let's move on - * while that's happening - */ - break; - - default: - return 1; - } - - /* - * The thread is over... either FAILED... - */ - - if (soe == NSSTATE_FAILED) { - lwsl_notice("%s: thread over with FAILED\n", - __func__); - goto failer; - } - - /* - * ...or we did the mirror and let's spawn the actual task now - */ - - lwsl_notice("%s: Destroying checkout thread, spawning task\n", - __func__); - ns->tp_task = NULL; - - lws_sul_cancel(&ns->sul_task_cancel); - saib_set_ns_state(ns, NSSTATE_BUILD); - - n = saib_spawn(ns); - if (n) { - lwsl_err("%s: spawn failed: %d\n", __func__, n); -failer: - saib_set_ns_state(ns, NSSTATE_FAILED); - saib_task_grace(ns); - saib_queue_task_status_update(ns->sp, ns->spm, NULL); - } - -next: - - if (ns->artifact_owner.head) { - /* - * This nspawn has an outstanding artifact to upload - */ - } - - lwsl_debug("%s: next tp sync\n", __func__); - - } lws_end_foreach_dll_safe(d, d1); - - return 0; -} - -/* * We cover requested tx for any instance of a platform that can takes tasks * from the same server... it means just by coming here, no particular * platform / sai_plat is implied... @@ -223,14 +72,6 @@ saib_m_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, int n = 0; /* - * We are the ss / wsi that any threadpool instances on any platform - * with tasks for this server are trying to sync to. We need to handle - * and resume them all. - */ - - lws_dll2_foreach_safe(&builder.sai_plat_owner, spm, tp_sync_check); - - /* * Any builder state updates / rejections to process? */ @@ -436,7 +277,27 @@ saib_m_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, size_t *len, star = walk; ns = lws_container_of(walk, struct sai_nspawn, list); - if (spm != ns->spm || !ns->chunk_cache.count || !ns->chunk_cache.tail) + if (spm != ns->spm) + continue; + + if (ns->state_changed) { + ns->state_changed = 0; + + switch (ns->state) { + case NSSTATE_CHECKEDOUT: + saib_set_ns_state(ns, NSSTATE_BUILD); + if (saib_spawn(ns)) { + lwsl_err("%s: saib_spawn failed\n", + __func__); + saib_set_ns_state(ns, NSSTATE_FAILED); + } + break; + default: + break; + } + } + + if (!ns->chunk_cache.count || !ns->chunk_cache.tail) continue; /* @@ -530,12 +391,12 @@ cleanup_on_ss_destroy(struct lws_dll2 *d, void *user) struct sai_nspawn *ns = lws_container_of(d, struct sai_nspawn, list); - if (ns->spm == spm && ns->tp) { - lwsl_notice("%s: calling threadpool_destroy\n", __func__); - lws_threadpool_finish(ns->tp); - lws_threadpool_destroy(ns->tp); - ns->tp = NULL; - ns->tp_task = NULL; + if (ns->spm == spm) { + /* + * This pss is about to go away, make sure the ns + * can't reference it any more no matter what happens + */ + ns->spm = NULL; } } lws_end_foreach_dll_safe(d, d1); diff --git a/src/builder/b-mirror.c b/src/builder/b-mirror.c index ac32dc8..5e4e768 100644 --- a/src/builder/b-mirror.c +++ b/src/builder/b-mirror.c @@ -1,7 +1,7 @@ /* * sai-builder com-warmcat-sai client protocol implementation * - * Copyright (C) 2019 - 2020 Andy Green <andy@warmcat.com> + * Copyright (C) 2019 - 2021 Andy Green <andy@warmcat.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,659 +23,301 @@ #include <string.h> #include <signal.h> #include <assert.h> +#include <fcntl.h> #include "b-private.h" -#include <git2.h> - -enum { - SRFS_REQUESTING, - SRFS_PROCESSING, - SRFS_FAILED, - SRFS_SUCCEEDED, -}; - -/* - * Request to do the remote -> local mirror action - */ - -typedef struct sai_mirror_req { - lws_dll2_t list; - - struct sai_nspawn *ns; - - char path[100]; /* local mirror path */ - char url[100]; - char hash[130]; - char ref[96]; - - int state; -} sai_mirror_req_t; - -static void -sai_mirror_req_state_set(sai_mirror_req_t *req, int n) -{ - lwsl_notice("%s: req %p: %d -> %d\n", __func__, req, req->state, n); - req->state = n; -} - -enum { - SAIB_CHECKOUT_OK, - SAIB_CHECKOUT_NOT_IN_LOCAL_MIRROR, - SAIB_CHECKOUT_CHECKOUT_FAILED, -}; - -static int -sai_mirror_local_checkout(struct sai_nspawn *ns) -{ - git_checkout_options co_opts = GIT_CHECKOUT_OPTIONS_INIT; - git_fetch_options opts = GIT_FETCH_OPTIONS_INIT; - git_repository *git_repo_build_specific = NULL; - char dp[512], spec[256], *paths[] = { spec }, inp[512]; - git_strarray rfs = { paths, 1 }; - git_object *treeish; - git_remote *remote; - int n, tries = 2; - - lws_strncpy(inp, ns->inp, sizeof(inp) - 1); - if (inp[strlen(inp) - 1] == '\\') - inp[strlen(inp) - 1] = '\0'; - - /* - * Remove anything that was already in the build-specific dir - */ - - lwsl_notice("%s: rm -rf %s\n", __func__, inp); - lws_dir(inp, NULL, lws_dir_rm_rf_cb); - - /* - * Make sure the build-specific dir itself is left standing in there. - * - * We can only create files and dirs using the global sai:nobody - * credentials since we have dropped root long ago - */ - - if (mkdir(inp, 0755)) - lwsl_notice("%s: mkdir %s failed\n", __func__, ns->inp); - - /* - * Create the build-specific git dir and init it - */ - - n = git_repository_init(&git_repo_build_specific, inp, 0); - if (n) { -#if defined(SAI_HAVE_LIBGIT2_GIT_ERROR) - const git_error *e = git_error_last(); - - lwsl_err("%s: unable to init temp repo %s: %d %s\n", - __func__, ns->inp, n, e ? e->message : "?"); +static const char * const git_helper_sh = + "#!/bin/bash\n" +#if defined(__APPLE__) + "export PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin\n" #else - lwsl_err("%s: unable to init temp repo %s: %d\n", - __func__, ns->inp, n); + "export PATH=/usr/local/bin:$PATH\n" #endif + "set -e\n" + "echo \"git_helper_sh: starting\"\n" + "OPERATION=$1\n" + "shift\n" + "if [ \"$OPERATION\" == \"mirror\" ]; then\n" + " REMOTE_URL=$1\n" + " REF=$2\n" + " HASH=$3\n" + " MIRROR_PATH=$4\n" + " mkdir -p \"$MIRROR_PATH\"\n" + " if [ ! -d \"$MIRROR_PATH/.git\" ]; then\n" + " git init --bare \"$MIRROR_PATH\"\n" + " fi\n" + " REFSPEC=\"$REF:ref-$HASH\"\n" + " git -C \"$MIRROR_PATH\" fetch \"$REMOTE_URL\" \"$REFSPEC\"\n" + "elif [ \"$OPERATION\" == \"checkout\" ]; then\n" + " MIRROR_PATH=$1\n" + " BUILD_DIR=$2\n" + " HASH=$3\n" + " if [ ! -d \"$BUILD_DIR/.git\" ]; then\n" + " rm -rf \"$BUILD_DIR\"\n" + " mkdir -p \"$BUILD_DIR\"\n" + " git -C \"$BUILD_DIR\" init\n" + " fi\n" + " if ! git -C \"$BUILD_DIR\" fetch \"$MIRROR_PATH\" \"ref-$HASH\"; then\n" + " exit 2\n" + " fi\n" + " git -C \"$BUILD_DIR\" checkout -f \"$HASH\"\n" + " git -C \"$BUILD_DIR\" clean -fdx\n" + "else\n" + " exit 1\n" + "fi\n" + "echo \">>> Git helper script finished.\"\n" + "exit 0\n"; - return SAIB_CHECKOUT_CHECKOUT_FAILED; - } - - /* - * Attempt to fetch the ref we are interested in from our local mirror - * - * Create a temp remote against the destination repo - */ - - if (git_remote_create_anonymous(&remote, git_repo_build_specific, - ns->path)) { - lwsl_err("%s: cant find remote %s\n", __func__, - ns->git_repo_url); - - git_repository_free(git_repo_build_specific); - return SAIB_CHECKOUT_CHECKOUT_FAILED; - } +#if defined(WIN32) +static const char * const git_helper_bat = + "@echo on\n" + "setlocal EnableDelayedExpansion\n" + "echo \"git_helper_bat: starting\"\n" + "set \"OPERATION=%~1\"\n" + "echo \"OPERATION: !OPERATION!\"\n" + "if /i \"!OPERATION!\"==\"mirror\" (\n" + " set \"REMOTE_URL=%~2\"\n" + " set \"REF=%~3\"\n" + " set \"HASH=%~4\"\n" + " set \"MIRROR_PATH=%~5\"\n" + " echo \"REMOTE_URL: !REMOTE_URL!\"\n" + " echo \"REF: !REF!\"\n" + " echo \"HASH: !HASH!\"\n" + " echo \"MIRROR_PATH: !MIRROR_PATH!\"\n" + " if not exist \"!MIRROR_PATH!\\.\" (\n" + " mkdir \"!MIRROR_PATH!\"\n" + " )\n" + " if not exist \"!MIRROR_PATH!\\.git\" (\n" + " git init --bare \"!MIRROR_PATH!\"\n" + " if errorlevel 1 exit /b 1\n" + " )\n" + " set \"REFSPEC=!REF!:ref-!HASH!\"\n" + " echo \"REFSPEC: !REFSPEC!\"\n" + " git -C \"!MIRROR_PATH!\" fetch \"!REMOTE_URL!\" \"!REFSPEC!\" 2>&1\n" + " if !ERRORLEVEL! neq 0 (\n" + " echo \"git fetch failed with errorlevel !ERRORLEVEL!\"\n" + " exit /b 1\n" + " )\n" + " exit /b 0\n" + ")\n" + "if /i \"!OPERATION!\"==\"checkout\" (\n" + " set \"MIRROR_PATH=%~2\"\n" + " set \"BUILD_DIR=%~3\"\n" + " set \"HASH=%~4\"\n" + " echo \"MIRROR_PATH: !MIRROR_PATH!\"\n" + " echo \"BUILD_DIR: !BUILD_DIR!\"\n" + " echo \"HASH: !HASH!\"\n" + " if not exist \"!BUILD_DIR!\\.git\" (\n" + " if exist \"!BUILD_DIR!\\\" rmdir /s /q \"!BUILD_DIR!\"\n" + " mkdir \"!BUILD_DIR!\"\n" + " git -C \"!BUILD_DIR!\" init\n" + " if errorlevel 1 exit /b 1\n" + " )\n" + " git -C \"!BUILD_DIR!\" fetch \"!MIRROR_PATH!\" \"ref-!HASH!\"\n" + " if errorlevel 1 exit /b 2\n" + " git -C \"!BUILD_DIR!\" checkout -f \"!HASH!\"\n" + " if errorlevel 1 exit /b 1\n" + " echo \">>> Git helper script finished.\"\n" + " exit /b 0\n" + ")\n" + "exit /b 1\n"; +#endif - lws_snprintf(spec, sizeof(spec), "ref-%s:ref-%s", ns->hash, ns->hash); +static void sai_git_mirror_reap_cb(void *opaque, lws_usec_t *accounting, + siginfo_t *si, int we_killed_him); - lwsl_notice("%s: Attempting to fetch %s from mirror to %s\n", - __func__, spec, ns->inp); +static void +sai_git_checkout_reap_cb(void *opaque, lws_usec_t *accounting, siginfo_t *si, + int we_killed_him) +{ + struct saib_opaque_spawn *op = (struct saib_opaque_spawn *)opaque; + struct sai_nspawn *ns = op->ns; + int exit_code = -1; - /* - * Fetch the tree from the local mirror to our local build repo - * This may take an open-ended amount of time - */ + lwsl_notice("%s: mirror reap callback started\n", __func__); -#if defined(LIBGIT2_HAVE_GIT_PROXY_OPTIONS_INIT) - git_proxy_options_init(&opts.proxy_opts, GIT_PROXY_OPTIONS_VERSION); -#endif + lwsl_warn("%s: reap at %llu: we_killed_him: %d\n", + __func__, (unsigned long long)lws_now_usecs(), + we_killed_him); - n = git_remote_fetch(remote, &rfs, &opts, "fetch"); - git_remote_free(remote); - if (n) { - lwsl_notice("%s: git_remote_fetch() says %d\n", __func__, n); -#if defined(SAI_HAVE_LIBGIT2_GIT_ERROR) - const git_error *e = git_error_last(); + if (we_killed_him) + goto fail; - lwsl_err("%s: git_remote_fetch libgit err: %d %s\n", - __func__, n, e ? e->message : "?"); +#if !defined(WIN32) + if (si->si_code == CLD_EXITED) + exit_code = si->si_status; +#else + exit_code = si->retcode & 0xff; #endif - git_repository_free(git_repo_build_specific); - return SAIB_CHECKOUT_NOT_IN_LOCAL_MIRROR; - } - /* - * Check out the commit we fetched into the ephemeral local build dir... - * this should be a formality since we already retreived the ref into - * ephemeral local build dir's repo. - */ - - lws_snprintf(spec, sizeof(spec), "ref-%s", ns->hash); - n = git_revparse_single(&treeish, git_repo_build_specific, spec); - if (n) { - lwsl_notice("%s: revparse %s failed: %d\n", __func__, spec, n); - git_repository_free(git_repo_build_specific); - return SAIB_CHECKOUT_NOT_IN_LOCAL_MIRROR; + if (exit_code == 0) { + saib_set_ns_state(ns, NSSTATE_CHECKEDOUT); + goto onward; } -again: - co_opts.checkout_strategy = GIT_CHECKOUT_FORCE; - n = git_checkout_tree(git_repo_build_specific, treeish, &co_opts); - git_object_free(treeish); - if (n) { - if (n == GIT_EUNBORNBRANCH && tries--) { - lwsl_warn("%s: git checkout says HEAD is empty branch\n", - __func__); - - /* - * It's telling us we need to delete the local mirror - * HEAD and retry - */ - - lws_snprintf(dp, sizeof(dp), "%s/HEAD", ns->inp); - unlink(dp); - - goto again; - } - goto co_failed; + if (exit_code == 2) { + saib_start_mirror(ns); + goto onward; } - lws_snprintf(spec, sizeof(spec), "refs/heads/ref-%s", ns->hash); - git_repository_set_head(git_repo_build_specific, spec); - - lwsl_notice("%s: checkout OK\n", __func__); - - git_repository_free(git_repo_build_specific); - - return SAIB_CHECKOUT_OK; - -co_failed: - lwsl_err("%s: git checkout failed: %d\n", __func__, n); - git_repository_free(git_repo_build_specific); - - return SAIB_CHECKOUT_CHECKOUT_FAILED; +fail: + saib_set_ns_state(ns, NSSTATE_FAILED); +onward: + ns->op = NULL; + free(op); } - -/* - * We are a threadpool thread handling queueing on remote mirror "thread_repo", - * and afterwards checking out a ref from the local copy. - * - * The main git2 process just goes away and does the fetch atomically. Wrapping - * it in an extra threadpool thread lets us continue to be resposive while we're - * waiting for the remote mirror fetch. - */ - -enum lws_threadpool_task_return -saib_mirror_task(void *user, enum lws_threadpool_task_status s) +static void +sai_git_mirror_reap_cb(void *opaque, lws_usec_t *accounting, siginfo_t *si, + int we_killed_him) { - struct sai_nspawn *ns = (struct sai_nspawn *)user; - sai_mirror_instance_t *mi; - sai_mirror_req_t *req; - int n, m; - - lwsl_warn("%s: entry: state %d\n", __func__, ns->state); - - mi = &builder.mi; - - if (s == LWS_TP_STATUS_STOPPING) - return LWS_TP_RETURN_STOPPED; - - switch (ns->state) { - case NSSTATE_INIT: - case NSSTATE_MOUNTING: - - case NSSTATE_STARTING_MIRROR: - lwsl_notice("%s: NSSTATE_STARTING_MIRROR\n", __func__); - ns->state = NSSTATE_CHECKOUT_SPEC; - /* fallthru */ - - case NSSTATE_CHECKOUT_SPEC: - /* - * First attempt to checkout from local mirror, in case we - * already fetched it to the local mirror for an earlier task - */ - - lwsl_notice("%s: NSSTATE_CHECKOUT_SPEC\n", __func__); - switch (sai_mirror_local_checkout(ns)) { - case SAIB_CHECKOUT_OK: - /* - * oh we have it then... do a final sync and have the - * foreground handler STOP us when we resume - */ - - lwsl_notice("%s: syncing checkout %s done\n", __func__, - ns->inp); - ns->state = NSSTATE_CHECKEDOUT; - - lws_snprintf(ns->pending_mirror_log, - sizeof(ns->pending_mirror_log), - ">saib> Local mirror checkout %s\n", ns->hash); - - return LWS_TP_RETURN_SYNC; - - case SAIB_CHECKOUT_NOT_IN_LOCAL_MIRROR: - /* - * Basically it means we have to get it from the remote - * repo into the local mirror first... clear down the - * local build dir - */ - - lws_dir(ns->inp, NULL, lws_dir_rm_rf_cb); - - /* - * Make sure the dir itself is left standing in there - */ - - if (mkdir(ns->inp, 0755)) - lwsl_notice("%s: mkdir %s failed\n", __func__, ns->inp); - - /* - * Form a request, kick the remoting thread and then - * wait on the condition in the request - */ - - req = malloc(sizeof(*req)); - if (!req) - goto fail; - - /* - * The request needs to exist with a standalone - * lifetime regardless of what's happening to the ns or - * threadpool thread while it waits, or proceeds - * asynchronously - */ - - lwsl_notice("%s: NOT_IN_LOCAL_MIRROR %s\n", __func__, ns->ref); - memset(req, 0, sizeof(*req)); - req->state = SRFS_REQUESTING; - req->ns = ns; - lws_strncpy(req->url, ns->git_repo_url, sizeof(req->url)); - lws_strncpy(req->hash, ns->hash, sizeof(req->hash)); - lws_strncpy(req->ref, ns->ref, sizeof(req->ref)); - lws_strncpy(req->path, ns->path, sizeof(req->path)); - - pthread_mutex_lock(&mi->mut); - lws_dll2_add_tail(&req->list, &mi->pending_req); - - pthread_cond_broadcast(&mi->cond); - pthread_mutex_unlock(&mi->mut); - - ns->state = NSSTATE_WAIT_REMOTE_MIRROR; - ns->mirror_wait_budget = 600; - - lws_snprintf(ns->pending_mirror_log, - sizeof(ns->pending_mirror_log), - ">saib> Starting REMOTE MIRROR FETCH\n"); - - return LWS_TP_RETURN_SYNC; - - case SAIB_CHECKOUT_CHECKOUT_FAILED: - - lws_snprintf(ns->pending_mirror_log, - sizeof(ns->pending_mirror_log), - ">saib> Checkout failed...\n"); - - goto fail; - } - - break; - - case NSSTATE_CHECKOUT: - /* - * We come here after the remote -> local mirror operation - * seemed to go well, to complete the flow by checking out - * the newly-fetched commit. We can only go OK or fail. - */ - - lwsl_notice("%s: NSSTATE_CHECKOUT\n", __func__); - m = sai_mirror_local_checkout(ns); - lwsl_notice("%s: sai_mirror_local_checkout() says %d\n", __func__, m); - switch (m) { - case SAIB_CHECKOUT_OK: - /* - * oh we have it then... do a final sync and have the - * foreground handler STOP us when we resume - */ - - lwsl_notice("%s: syncing checkout %s done\n", __func__, - ns->inp); - ns->state = NSSTATE_CHECKEDOUT; - - lws_snprintf(ns->pending_mirror_log, - sizeof(ns->pending_mirror_log), - ">saib> Remote->Local->Checked out %s\n", ns->hash); + struct saib_opaque_spawn *op = (struct saib_opaque_spawn *)opaque; + struct sai_nspawn *ns = op->ns; + int exit_code = -1; - return LWS_TP_RETURN_SYNC; + // lwsl_warn("%s: reap at %llu: we_killed_him: %d, si_code: %d, si_status: %d\n", + // __func__, (unsigned long long)lws_now_usecs(), + // we_killed_him, si->si_code, si->si_status); - case SAIB_CHECKOUT_NOT_IN_LOCAL_MIRROR: - /* fallthru */ - lwsl_notice("%s: checkout not in local mirror\n", __func__); - case SAIB_CHECKOUT_CHECKOUT_FAILED: - - lws_snprintf(ns->pending_mirror_log, - sizeof(ns->pending_mirror_log), - ">saib> Checkout failed after mirror...\n"); - lwsl_notice("%s: checkout failed\n", __func__); + if (we_killed_him) + goto fail; - goto fail; - } - break; - - case NSSTATE_CHECKEDOUT: - case NSSTATE_BUILD: - case NSSTATE_DONE: - case NSSTATE_FAILED: - break; - - case NSSTATE_WAIT_REMOTE_MIRROR: - - /* - * We are waiting for our request to be handled... we need to - * watch the req completed list at intervals. Anything on the - * completed_req owner has either completed or failed. - */ - - lwsl_notice("%s: NSSTATE_WAIT_REMOTE_MIRROR\n", __func__); - pthread_mutex_lock(&mi->mut); - - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - mi->completed_req.head) { - sai_mirror_req_t *r = lws_container_of(d, - sai_mirror_req_t, list); - - if (r->ns == ns) { - - /* - * It's our request that has completed - */ - - n = r->state; - - lws_dll2_remove(&r->list); - pthread_mutex_unlock(&mi->mut); - free(r); - - if (n == SRFS_FAILED) { - lwsl_notice("%s: mirror req failed\n", - __func__); - goto fail; - } - - /* - * Leave in NSSTATE_CHECKOUT and come back to - * continue with checking out after we have - * sync'd with comms to server - */ - - ns->state = NSSTATE_CHECKOUT; - lws_snprintf(ns->pending_mirror_log, - sizeof(ns->pending_mirror_log), - ">saib> Remote -> Local OK\n"); - - return LWS_TP_RETURN_SYNC; - } - - } lws_end_foreach_dll_safe(d, d1); - - pthread_mutex_unlock(&mi->mut); /* --- mi->mut unlock */ - -#if defined(WIN32) - Sleep(1000); +#if !defined(WIN32) + if (si->si_code == CLD_EXITED) + exit_code = si->si_status; #else - sleep(1); + exit_code = si->retcode & 0xff; #endif - if (!--ns->mirror_wait_budget) { - lws_snprintf(ns->pending_mirror_log, sizeof(ns->pending_mirror_log), - ">saib> timed out waiting for mirror\n"); - lwsl_notice("%s: timed out waiting for mirror\n", __func__); - - goto fail; - } - - lws_snprintf(ns->pending_mirror_log, sizeof(ns->pending_mirror_log), - ">saib> Waiting on MIRROR...\n"); - - return LWS_TP_RETURN_SYNC; - } - - return LWS_TP_RETURN_SYNC; + if (exit_code == 0) { + /* mirror succeeded, now try checkout again */ + lwsl_notice("%s: mirror success, starting checkout\n", __func__); + saib_start_checkout(ns); + goto onward; + } else + lwsl_warn("%s: exit code 0x%x == failure\n", __func__, (unsigned int)exit_code); fail: - - /* - * If we have an incomplete request on the mirror thread, remove it - */ - - pthread_mutex_lock(&mi->mut); - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - mi->pending_req.head) { - sai_mirror_req_t *r = lws_container_of(d, - sai_mirror_req_t, list); - - if (r->ns == ns) { - lws_dll2_remove(&r->list); - free(r); - } - - } lws_end_foreach_dll_safe(d, d1); - pthread_mutex_unlock(&mi->mut); - - lwsl_err("%s: failed\n", __func__); - ns->retcode = SAISPRF_EXIT | 253; - - return LWS_TP_RETURN_SYNC; + saib_set_ns_state(ns, NSSTATE_FAILED); +onward: + ns->op = NULL; + free(op); } -/* - * This thread serially handles requests for remote mirrors, since there's a - * write lock in the filesystem copy when someone is updating the mirror. - * - * It picks takes queued requests from the head - */ - -void * -thread_repo(void *d) +static int +saib_spawn_git_helper(struct sai_nspawn *ns, const char *operation) { - sai_mirror_instance_t *mi = (sai_mirror_instance_t *)d; - sai_mirror_req_t *req, rcopy; - int new_state; - int n; - - lwsl_notice("%s: repo thread start\n", __func__); - - git_libgit2_init(); - - while (!mi->finish) { - git_fetch_options opts = GIT_FETCH_OPTIONS_INIT; - char spec[96], *paths[] = { spec }; - git_strarray rfs = { paths, 1 }; - git_repository *repo_mirror; - git_remote *remote; - - pthread_mutex_lock(&mi->mut); - - /* we sleep if there are no requests pending or ongoing */ - - while (!mi->pending_req.count && !mi->finish) - pthread_cond_wait(&mi->cond, &mi->mut); - - if (mi->finish) { - pthread_mutex_unlock(&mi->mut); - break; - } - - /* - * Starting from the head, look for the first request that's in - * a state we should start the remote for it - */ - - req = NULL; - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - mi->pending_req.head) { - sai_mirror_req_t *r = lws_container_of(d, - sai_mirror_req_t, list); - - if (r->state == SRFS_REQUESTING) { - req = r; - lwsl_notice("%s: setting processing\n", - __func__); - sai_mirror_req_state_set(req, SRFS_PROCESSING); - break; - } - - } lws_end_foreach_dll_safe(d, d1); - - //req = lws_dll2_get_head(&bi->requests); - - //lws_dll2_remove(&->overall_list); - - /* - * The request object on the list might be removed and destroyed - * while we do this long-term mirroring action. So take a temp - * copy of it to set the action up, before releasing the mutex. - */ - if (req) - rcopy = *req; - - pthread_mutex_unlock(&mi->mut); - if (!req) - continue; - - /* - * ... we cannot dereference req after releasing the mutex. - * We made a temp copy of it in rcopy, to set the transaction - * up we can use that so we can be sure it's around for that. - */ - - /* - * git init --bare <path> - */ - - new_state = SRFS_FAILED; - repo_mirror = NULL; - if (git_repository_init(&repo_mirror, rcopy.path, 1)) { - fprintf(stderr, "%s: unable to init sticky repo %s, errno %d\n", - __func__, rcopy.path, errno); - - goto fail_out; - } - - /* - * Fetch over the branch or tag we're interested in from the - * remote repo and into our local sticky mirror - * - * git fetch <remote repo> +<ref>:<ref> - */ - - if (git_remote_create_anonymous(&remote, repo_mirror, rcopy.url)) { - fprintf(stderr, "%s: cant find remote %s\n", __func__, - rcopy.url); - - git_repository_free(repo_mirror); - goto fail_out; - } - - lws_snprintf(spec, sizeof(spec), "%s:ref-%s", rcopy.ref, rcopy.hash); - fprintf(stderr, "%s: fetching %s %s\n", __func__, rcopy.url, spec); - -#if defined(LIBGIT2_HAVE_GIT_PROXY_OPTIONS_INIT) - git_proxy_options_init(&opts.proxy_opts, GIT_PROXY_OPTIONS_VERSION); + struct lws_spawn_piped_info info; + struct saib_opaque_spawn *op; + char script_path[1024], inp[512]; + const char *pargs[9]; + const char **env = NULL; +#if defined(__APPLE__) + const char *env_array[2]; + char path_env[256]; +#endif + ssize_t n; + int fd, count = 0; + +#if defined(__APPLE__) + lws_snprintf(path_env, sizeof(path_env), + "PATH=/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/sbin:/usr/sbin"); + env_array[0] = path_env; + env_array[1] = NULL; + env = (const char **)env_array; #endif - /* - * This may take an open-ended amount of time - */ +#if defined(WIN32) + lws_snprintf(script_path, sizeof(script_path), "%s\\sai-git-helper-%d.bat", + builder.home, ns->instance_idx); +#else + lws_snprintf(script_path, sizeof(script_path), "%s/sai-git-helper-%d.sh", + builder.home, ns->instance_idx); +#endif - n = git_remote_fetch(remote, &rfs, &opts, "fetch"); -#if defined(SAI_HAVE_LIBGIT2_GIT_ERROR) - if (n) { - const git_error *e = git_error_last(); + fd = open(script_path, O_CREAT | O_TRUNC | O_WRONLY, 0755); + if (fd < 0) + return -1; - if (e) - fprintf(stderr, "%s: git error %s\n", __func__, - e->message); - } +#if defined(WIN32) + n = write(fd, git_helper_bat, strlen(git_helper_bat)); +#else + n = write(fd, git_helper_sh, strlen(git_helper_sh)); #endif - - git_remote_free(remote); - git_repository_free(repo_mirror); - - if (n) { - fprintf(stderr, "%s: failed to fetch %s, n: %d\n", - __func__, spec, n); - goto fail_out; - } - - fprintf(stderr, "%s: syncing mirror fetch %s %s successful\n", - __func__, rcopy.url, rcopy.hash); - - new_state = SRFS_SUCCEEDED; - -fail_out: - /* - * We notify the threadpool monitoring thread - */ - - pthread_mutex_lock(&mi->mut); - - /* - * The requestor and its request may have gone. And, it's - * possible we got several queued requests that were actually - * waiting for the same mirror transaction which has now - * succeeded or failed the same for all of them. - * - * Let's take the approach to lock the queue and tell everybody - * at once who's asking for the same thing what the outcome was. - */ - lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, - mi->pending_req.head) { - sai_mirror_req_t *r = lws_container_of(d, - sai_mirror_req_t, list); - - if ((r->state == SRFS_REQUESTING || - r->state == SRFS_PROCESSING) && - !strcmp(r->url, rcopy.url) && - !strcmp(r->hash, rcopy.hash) && - !strcmp(r->path, rcopy.path) && - !strcmp(r->ref, rcopy.ref)) { - /* - * Change the state of matching guys and put - * them on to the completed_req list owner - */ - fprintf(stderr, "%s: setting req state %d\n", - __func__, new_state); - sai_mirror_req_state_set(r, new_state); - lws_dll2_remove(&r->list); - lws_dll2_add_tail(&r->list, &mi->completed_req); - } - - } lws_end_foreach_dll_safe(d, d1); - - pthread_mutex_unlock(&mi->mut); + close(fd); + + if (n < 0) + return -1; + + pargs[count++] = script_path; + pargs[count++] = operation; + + if (!strcmp(operation, "mirror")) { + pargs[count++] = ns->git_repo_url; + pargs[count++] = ns->ref; + pargs[count++] = ns->hash; + pargs[count++] = ns->path; + } else { /* checkout */ + lws_strncpy(inp, ns->inp, sizeof(inp) - 1); + if (inp[strlen(inp) - 1] == '\\') + inp[strlen(inp) - 1] = '\0'; + pargs[count++] = ns->path; + pargs[count++] = inp; + pargs[count++] = ns->hash; + } + pargs[count++] = NULL; + + memset(&info, 0, sizeof(info)); + info.vh = builder.vhost; + info.exec_array = pargs; + info.env_array = env; + info.protocol_name = "sai-stdxxx"; + info.timeout_us = 5 * 60 * LWS_US_PER_SEC; + + if (!strcmp(operation, "mirror")) + info.reap_cb = sai_git_mirror_reap_cb; + else + info.reap_cb = sai_git_checkout_reap_cb; + + op = malloc(sizeof(*op)); + if (!op) + return -1; + memset(op, 0, sizeof(*op)); + + op->ns = ns; + ns->op = op; + + info.opaque = op; + info.owner = &builder.lsp_owner; + info.plsp = &op->lsp; + + // lwsl_warn("%s: spawning git-helper for %s at %llu\n", __func__, + // operation, (unsigned long long)lws_now_usecs()); + + if (lws_spawn_piped(&info) == NULL) { + lwsl_err("%s: lws_spawn_piped for %s failed\n", __func__, + operation); + ns->op = NULL; + /* op is attached to wsi and will be freed later */ + return -1; } - lwsl_notice("%s: repo thread exiting\n", __func__); + if (!strcmp(operation, "mirror")) + saib_set_ns_state(ns, NSSTATE_WAIT_REMOTE_MIRROR); - git_libgit2_shutdown(); - pthread_exit(NULL); + return 0; +} - return NULL; +int +saib_start_mirror(struct sai_nspawn *ns) +{ + return saib_spawn_git_helper(ns, "mirror"); +} + +int +saib_start_checkout(struct sai_nspawn *ns) +{ + lwsl_notice("%s: starting checkout\n", __func__); + return saib_spawn_git_helper(ns, "checkout"); } diff --git a/src/builder/b-nspawn.c b/src/builder/b-nspawn.c index fca417e..70612b4 100644 --- a/src/builder/b-nspawn.c +++ b/src/builder/b-nspawn.c @@ -78,22 +78,36 @@ static int callback_sai_stdwsi(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { - struct sai_nspawn *ns = (struct sai_nspawn *)lws_get_opaque_user_data(wsi); + struct saib_opaque_spawn *op = + (struct saib_opaque_spawn *)lws_get_opaque_user_data(wsi); + struct sai_nspawn *ns = op ? op->ns : NULL; uint8_t buf[600]; int ilen; + // lwsl_warn("%s: reason %d\n", __func__, reason); + switch (reason) { case LWS_CALLBACK_RAW_CLOSE_FILE: - lwsl_user("%s: RAW_CLOSE_FILE wsi %p: fd: %d, stdfd: %d\n", - __func__, wsi, lws_get_socket_fd(wsi), - lws_spawn_get_stdfd(wsi)); + lwsl_warn("%s: RAW_CLOSE_FILE at %llu, wsi %p: fd: %d, stdfd: %d\n", + __func__, (unsigned long long)lws_now_usecs(), wsi, + lws_get_socket_fd(wsi), lws_spawn_get_stdfd(wsi)); ilen = lws_snprintf((char *)buf, sizeof(buf), "Stdwsi %d close\n", lws_spawn_get_stdfd(wsi)); - saib_log_chunk_create(ns, buf, (size_t)ilen, 3); + if (ns) { + saib_log_chunk_create(ns, buf, (size_t)ilen, 3); + if (ns->spm) + if (lws_ss_request_tx(ns->spm->ss)) + lwsl_warn("%s: lws_ss_request_tx failed\n", + __func__); + } - if (ns->lsp) - lws_spawn_stdwsi_closed(ns->lsp, wsi); + lwsl_wsi_err(wsi, "CLOSING: op %p, op->lsp %p", op, op ? op->lsp : NULL); + if (op && op->lsp) { + lws_spawn_stdwsi_closed(op->lsp, wsi); + if (ns) + lws_cancel_service(ns->builder->context); + } break; case LWS_CALLBACK_RAW_RX_FILE: @@ -116,15 +130,15 @@ callback_sai_stdwsi(struct lws *wsi, enum lws_callback_reasons reason, len = (unsigned int)ilen; - if (!ns->spm) { + if (!op || !op->ns || !op->ns->spm) { printf("%s: (%d) %.*s\n", __func__, (int)lws_spawn_get_stdfd(wsi), (int)len, buf); return -1; } - if (!saib_log_chunk_create(ns, buf, len, lws_spawn_get_stdfd(wsi))) + if (!saib_log_chunk_create(op->ns, buf, len, lws_spawn_get_stdfd(wsi))) return -1; - return lws_ss_request_tx(ns->spm->ss) ? -1 : 0; + return lws_ss_request_tx(op->ns->spm->ss) ? -1 : 0; default: break; @@ -141,7 +155,11 @@ static void sai_lsp_reap_cb(void *opaque, lws_usec_t *accounting, siginfo_t *si, int we_killed_him) { - struct sai_nspawn *ns = (struct sai_nspawn *)opaque; + struct saib_opaque_spawn *op = (struct saib_opaque_spawn *)opaque; + struct sai_nspawn *ns = op ? op->ns : NULL; + + // lwsl_warn("%s: reap at %llu: we_killed_him: %d\n", __func__, + // (unsigned long long)lws_now_usecs(), we_killed_him); saib_log_chunk_create(ns, ">saib> Reaping build process\n", 29, 3); @@ -199,6 +217,10 @@ ok: lwsl_notice("%s: finished, waiting to drain logs (this ns %d, spm in flight %d)\n", __func__, ns->chunk_cache.count, ns->spm ? ns->spm->logs_in_flight : -99); + + if (ns) + ns->op = NULL; + free(op); } #if defined(WIN32) @@ -211,6 +233,7 @@ static const char * const runscript = "set SAI_LOGPROXY_TTY1=%s\n" "set HOME=%s\n" "cd %s\\jobs\\%s\\%s &&" + " rmdir /s /q build & " "%s" ; @@ -234,7 +257,7 @@ static const char * const runscript = "export SAI_LOGPROXY_TTY1=%s\n" "set -e\n" "cd %s/jobs/$SAI_OVN/$SAI_PROJECT\n" - + "rm -rf build\n" "%s\n" "exit $?\n" ; @@ -245,6 +268,7 @@ int saib_spawn(struct sai_nspawn *ns) { struct lws_spawn_piped_info info; + struct saib_opaque_spawn *op; char args[290], st[2048], *p; const char *respath = "unk"; const char * cmd[] = { @@ -252,7 +276,7 @@ saib_spawn(struct sai_nspawn *ns) NULL }; const char *env[] = { - "PATH=/usr/bin:/bin", + "PATH=/usr/local/bin:/usr/bin:/bin", "LANG=en_US.UTF-8", NULL }; @@ -336,20 +360,41 @@ saib_spawn(struct sai_nspawn *ns) info.max_log_lines = 10000; info.timeout_us = 30 * 60 * LWS_US_PER_SEC; info.reap_cb = sai_lsp_reap_cb; - info.opaque = ns; - info.plsp = &ns->lsp; #if defined(__linux__) info.cgroup_name_suffix = cgroup; info.p_cgroup_ret = &in_cgroup; #endif - ns->lsp = lws_spawn_piped(&info); - if (!ns->lsp) { + op = malloc(sizeof(*op)); + if (!op) + return 1; + memset(op, 0, sizeof(*op)); + + op->ns = ns; + ns->op = op; + + info.opaque = op; + info.owner = &builder.lsp_owner; + info.plsp = &op->lsp; + + // lwsl_warn("%s: spawning build script at %llu\n", __func__, + // (unsigned long long)lws_now_usecs()); + + lws_spawn_piped(&info); + if (!op->lsp) { + /* + * op is attached to wsi and will be freed in reap cb, + * we can't free it here + */ + ns->op = NULL; lwsl_err("%s: failed\n", __func__); return 1; } + // lwsl_warn("%s: build script spawn returned at %llu\n", __func__, + // (unsigned long long)lws_now_usecs()); + #if defined(__linux__) lwsl_notice("%s: lws_spawn_piped started (cgroup: %d)\n", __func__, in_cgroup); #endif diff --git a/src/builder/b-private.h b/src/builder/b-private.h index 5baa4b3..67d0823 100644 --- a/src/builder/b-private.h +++ b/src/builder/b-private.h @@ -31,13 +31,18 @@ #define mkdir(x,y) _mkdir(x) #define rmdir _rmdir #define unlink _unlink -#define HAVE_STRUCT_TIMESPEC #if defined(pid_t) #undef pid_t #endif #endif #include <pthread.h> -#include <git2.h> + +struct lws_spawn_piped; + +struct saib_opaque_spawn { + struct sai_nspawn *ns; + struct lws_spawn_piped *lsp; +}; #define SAI_LOAD_REPORT_US (1 * LWS_US_PER_SEC) #define SAI_IDLE_GRACE_US (30 * LWS_US_PER_SEC) @@ -78,19 +83,6 @@ enum { -typedef struct sai_mirror_instance { - pthread_mutex_t mut; - pthread_cond_t cond; - - pthread_t repo_thread; - - lws_dll2_owner_t pending_req; - lws_dll2_owner_t completed_req; - - uint8_t finish; -} sai_mirror_instance_t; - - /* * This represents this builder process as a whole */ @@ -99,6 +91,7 @@ struct sai_builder { lws_dll2_owner_t sai_plat_owner; /* list of platforms we offer */ lws_dll2_owner_t sai_plat_server_owner; /* servers we connect to */ lws_dll2_owner_t devices_owner; /* sai_serial_t */ + lws_dll2_owner_t lsp_owner; /* list of lws_spawn_piped */ struct lws_ss_handle *ss_stay; @@ -128,8 +121,6 @@ struct sai_builder { const char *host; /* prepended before hostname */ - sai_mirror_instance_t mi; - char path[256]; #if defined(__linux__) || defined(__APPLE__) @@ -221,9 +212,6 @@ rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde); extern const struct lws_protocols protocol_logproxy, protocol_resproxy; void * -thread_repo(void *d); - -void * thread_suspend(void *d); @@ -244,3 +232,6 @@ int saib_get_system_cpu(struct sai_builder *b); int saib_get_cpu_count(void); + +int saib_start_mirror(struct sai_nspawn *ns); +int saib_start_checkout(struct sai_nspawn *ns); diff --git a/src/builder/b-sai.c b/src/builder/b-sai.c index faf0680..626afcd 100644 --- a/src/builder/b-sai.c +++ b/src/builder/b-sai.c @@ -726,7 +726,6 @@ void sigint_handler(int sig) void sai_ns_destroy(struct sai_nspawn *ns) { - lws_dll2_remove(&ns->list); free(ns); } @@ -741,7 +740,6 @@ int main(int argc, const char **argv) #endif struct stat sb; const char *p; - void *retval; #if !defined(WIN32) @@ -986,28 +984,6 @@ int main(int argc, const char **argv) } #endif - pthread_mutex_init(&builder.mi.mut, NULL); - pthread_cond_init(&builder.mi.cond, NULL); - - - /* - * Our approach is to split off a thread to do the git remote handling - * in a serialized way blocking the related threadpool threads until it - * completes, without blocking the main (lws) event loop thread. - * - * It has to be segregated because there is a repo write lock with one - * owner at a time for remote -> local mirror write operation. - * - * local mirror -> build-specific checkout doesn't need the write lock - * and can happen concurrently at the threadpool threads. - */ - - if (pthread_create(&builder.mi.repo_thread, NULL, thread_repo, - &builder.mi)) { - lwsl_err("%s: repo thread creation failed\n", __func__); - return 1; - } - while (!lws_service(builder.context, 0) && !interrupted) ; @@ -1057,20 +1033,6 @@ bail: lws_sul_cancel(&builder.sul_idle); - /* - * Clean up after the spawn threads - */ - - pthread_mutex_lock(&builder.mi.mut); - builder.mi.finish = 1; - pthread_cond_broadcast(&builder.mi.cond); - pthread_mutex_unlock(&builder.mi.mut); - - pthread_join(builder.mi.repo_thread, &retval); - - pthread_mutex_destroy(&builder.mi.mut); - pthread_cond_destroy(&builder.mi.cond); - lws_context_destroy(builder.context); return 0; diff --git a/src/builder/b-task.c b/src/builder/b-task.c index f7848ff..e479f18 100644 --- a/src/builder/b-task.c +++ b/src/builder/b-task.c @@ -153,17 +153,8 @@ saib_task_destroy(struct sai_nspawn *ns) return; } - if (ns->tp) { - ns->tp_task = NULL; - lwsl_notice("%s: calling threadpool_finish\n", __func__); - lws_threadpool_finish(ns->tp); - lwsl_notice("%s: calling threadpool_destroy\n", __func__); - lws_threadpool_destroy(ns->tp); - ns->tp = NULL; - } if (ns->task && ns->task->told_ongoing) { - /* * Account that we're not doing this task any more */ @@ -423,13 +414,13 @@ saib_sul_task_cancel(struct lws_sorted_usec_list *sul) char s[64]; int n; - if (!ns->lsp) + if (!ns->op || !ns->op->lsp) return; n = lws_snprintf(s, sizeof(s), ">saib> Cancelling...\n"); saib_log_chunk_create(ns, s, (size_t)n, 3); - lws_spawn_piped_kill_child_process(ns->lsp); + lws_spawn_piped_kill_child_process(ns->op->lsp); if (!--ns->term_budget) return; @@ -440,8 +431,6 @@ saib_sul_task_cancel(struct lws_sorted_usec_list *sul) int saib_ws_json_rx_builder(struct sai_plat_server *spm, const void *in, size_t len) { - struct lws_threadpool_create_args tca; - struct lws_threadpool_task_args tpa; sai_plat_t *sp = NULL; struct sai_nspawn *ns; sai_resource_t *reso; @@ -529,9 +518,13 @@ saib_ws_json_rx_builder(struct sai_plat_server *spm, const void *in, size_t len) sp->nspawn_owner.head) { struct sai_nspawn *xns = lws_container_of(d, struct sai_nspawn, list); + char found = 0; n++; - if (!xns->task) { + if (!xns->task) + found = 1; + + if (found) { ns = xns; break; } @@ -556,13 +549,6 @@ saib_ws_json_rx_builder(struct sai_plat_server *spm, const void *in, size_t len) // lwsl_hexdump_warn(task->build, strlen(task->build)); - /* create a taskqueue just for preparing this specific spawn */ - - memset(&tpa, 0, sizeof(tpa)); - tca.threads = 1; - tca.max_queue_depth = 1; - ns->tp = lws_threadpool_create(builder.context, &tca, "nsp-%s", - task->uuid); lws_strncpy(ns->fsm.distro, task->platform, sizeof(ns->fsm.distro)); @@ -692,21 +678,11 @@ saib_ws_json_rx_builder(struct sai_plat_server *spm, const void *in, size_t len) saib_set_ns_state(ns, NSSTATE_STARTING_MIRROR); - memset(&tpa, 0, sizeof(tpa)); - tpa.ss = spm->ss; - tpa.user = ns; - tpa.name = "nsp"; - tpa.task = saib_mirror_task; - ns->user_cancel = 0; ns->spins = 0; - lwsl_warn("%s: enqueuing mirror thread\n", __func__); - - ns->tp_task = lws_threadpool_enqueue(ns->tp, &tpa, "tptask-%s", - task->uuid); - if (!ns->tp_task) { - lwsl_err("%s: threadpool enqueue failed\n", __func__); + if (saib_start_mirror(ns)) { + lwsl_err("%s: saib_start_mirror failed\n", __func__); goto bail; } @@ -716,8 +692,6 @@ saib_ws_json_rx_builder(struct sai_plat_server *spm, const void *in, size_t len) * after that needs to adjust sp->ongoing accordingly */ - lwsl_warn("%s: enqueued mirror thread, ns->tp_task %p\n", __func__, ns->tp_task); - lwsl_notice("%s: ongoing %d -> %d\n", __func__, sp->ongoing, sp->ongoing + 1); sp->ongoing++; diff --git a/src/common/include/private.h b/src/common/include/private.h index 6d0e62d..0475e3d 100644 --- a/src/common/include/private.h +++ b/src/common/include/private.h @@ -21,6 +21,10 @@ * structs common to builder and server */ +#if defined(WIN32) +#define HAVE_STRUCT_TIMESPEC +#endif + //#include <sai_config_private.h> #if defined(__linux__) @@ -33,6 +37,7 @@ struct sai_plat; struct sai_builder; +struct saib_opaque_spawn; typedef enum { SAIES_WAITING = 0, @@ -155,13 +160,11 @@ struct sai_nspawn { lws_dll2_t list; /* sai_plat owner lists sai_nspawns */ struct sai_builder *builder; struct lws_fsmount fsm; - struct lws_spawn_piped *lsp; + struct saib_opaque_spawn *op; sai_task_t *task; lws_dll2_owner_t artifact_owner; /* struct artifact_path */ - struct lws_threadpool *tp; - struct lws_threadpool_task *tp_task; lws_sorted_usec_list_t sul_cleaner; lws_sorted_usec_list_t sul_task_cancel; @@ -191,6 +194,7 @@ struct sai_nspawn { uint8_t finished_when_logs_drained:1; uint8_t state_changed:1; uint8_t user_cancel:1; + }; /* diff --git a/src/server/s-websrv.c b/src/server/s-websrv.c index f904009..93d63d6 100644 --- a/src/server/s-websrv.c +++ b/src/server/s-websrv.c @@ -587,7 +587,7 @@ websrvss_ws_tx(void *userobj, lws_ss_tx_ordinal_t ord, uint8_t *buf, *flags = (som ? LWSSS_FLAG_SOM : 0) | (eom ? LWSSS_FLAG_EOM : 0); *len = (size_t)used; - lwsl_warn("%s: srv -> web: len %d flags %d\n", __func__, (int)*len, (int)*flags); + // lwsl_warn("%s: srv -> web: len %d flags %d\n", __func__, (int)*len, (int)*flags); if (m->bltx) return lws_ss_request_tx(m->ss); diff --git a/src/server/s-ws-builder.c b/src/server/s-ws-builder.c index d9f3cea..7bd2f01 100644 --- a/src/server/s-ws-builder.c +++ b/src/server/s-ws-builder.c @@ -1092,7 +1092,7 @@ sais_ws_json_tx_builder(struct vhd *vhd, struct pss *pss, uint8_t *buf, } - if (!pss->issue_task_owner.count) + if (!pss->issue_task_owner.count || !pss->issue_task_owner.head) return 0; /* nothing to send */ /*
Page fetched 0s ago, creation time: 10ms (vhost etag hits: 0%, cache hits: 0%)