From 2b9d38130161116081d24630b99d1a437fa3bdb2 Mon Sep 17 00:00:00 2001 From: Sebastian Ullrich Date: Sun, 5 Jun 2022 22:45:37 +0200 Subject: [PATCH 1/5] Fix nested flake input overrides --- src/libexpr/flake/flake.cc | 47 ++++++++++++++++++++++++++---------- tests/flakes/follow-paths.sh | 30 +++++++++++++++++++++++ 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 105e76bc6..81762a0af 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -90,11 +90,11 @@ static void expectType(EvalState & state, ValueType type, static std::map parseFlakeInputs( EvalState & state, Value * value, const PosIdx pos, - const std::optional & baseDir, InputPath lockRootPath); + const std::optional & baseDir, InputPath lockRootPath, unsigned depth); static FlakeInput parseFlakeInput(EvalState & state, const std::string & inputName, Value * value, const PosIdx pos, - const std::optional & baseDir, InputPath lockRootPath) + const std::optional & baseDir, InputPath lockRootPath, unsigned depth) { expectType(state, nAttrs, *value, pos); @@ -118,7 +118,7 @@ static FlakeInput parseFlakeInput(EvalState & state, expectType(state, nBool, *attr.value, attr.pos); input.isFlake = attr.value->boolean; } else if (attr.name == sInputs) { - input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath); + input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath, depth + 1); } else if (attr.name == sFollows) { expectType(state, nString, *attr.value, attr.pos); auto follows(parseInputPath(attr.value->string.s)); @@ -163,7 +163,11 @@ static FlakeInput parseFlakeInput(EvalState & state, input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake); } - if (!input.follows && !input.ref) + if (!input.follows && !input.ref && depth == 0) + // in `input.nixops.inputs.nixpkgs.url = ...`, we assume `nixops` is from + // the flake registry absent `ref`/`follows`, but we should not assume so + // about `nixpkgs` (where `depth == 1`) as the `nixops` flake should + // determine its default source input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", inputName}}); return input; @@ -171,7 +175,7 @@ static FlakeInput parseFlakeInput(EvalState & state, static std::map parseFlakeInputs( EvalState & state, Value * value, const PosIdx pos, - const std::optional & baseDir, InputPath lockRootPath) + const std::optional & baseDir, InputPath lockRootPath, unsigned depth) { std::map inputs; @@ -184,7 +188,8 @@ static std::map parseFlakeInputs( inputAttr.value, inputAttr.pos, baseDir, - lockRootPath)); + lockRootPath, + depth)); } return inputs; @@ -230,7 +235,7 @@ static Flake getFlake( auto sInputs = state.symbols.create("inputs"); if (auto inputs = vInfo.attrs->get(sInputs)) - flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath); + flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath, 0); auto sOutputs = state.symbols.create("outputs"); @@ -313,6 +318,19 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup return getFlake(state, originalRef, allowLookup, flakeCache); } +/* Recursively merge `overrides` into `overrideMap` */ +static void updateOverrides(std::map & overrideMap, const FlakeInputs & overrides, + const InputPath & inputPathPrefix) +{ + for (auto & [id, input] : overrides) { + auto inputPath(inputPathPrefix); + inputPath.push_back(id); + // Do not override existing assignment from outer flake + overrideMap.insert({inputPath, input}); + updateOverrides(overrideMap, input.overrides, inputPath); + } +} + /* Compute an in-memory lock file for the specified top-level flake, and optionally write it to file, if the flake is writable. */ LockedFlake lockFlake( @@ -375,12 +393,9 @@ LockedFlake lockFlake( /* Get the overrides (i.e. attributes of the form 'inputs.nixops.inputs.nixpkgs.url = ...'). */ for (auto & [id, input] : flakeInputs) { - for (auto & [idOverride, inputOverride] : input.overrides) { - auto inputPath(inputPathPrefix); - inputPath.push_back(id); - inputPath.push_back(idOverride); - overrides.insert_or_assign(inputPath, inputOverride); - } + auto inputPath(inputPathPrefix); + inputPath.push_back(id); + updateOverrides(overrides, input.overrides, inputPath); } /* Check whether this input has overrides for a @@ -415,6 +430,12 @@ LockedFlake lockFlake( // Respect the “flakeness” of the input even if we // override it i->second.isFlake = input2.isFlake; + if (!i->second.ref) + i->second.ref = input2.ref; + if (!i->second.follows) + i->second.follows = input2.follows; + // Note that `input.overrides` is not used in the following, + // so no need to merge it here (already done by `updateOverrides`) } auto & input = hasOverride ? i->second : input2; diff --git a/tests/flakes/follow-paths.sh b/tests/flakes/follow-paths.sh index 19cc1bafa..e6751b06e 100644 --- a/tests/flakes/follow-paths.sh +++ b/tests/flakes/follow-paths.sh @@ -148,3 +148,33 @@ git -C $flakeFollowsA add flake.nix nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid'" nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid2'" + +# Test nested flake overrides + +cat < $flakeFollowsD/flake.nix +{ outputs = _: {}; } +EOF +cat < $flakeFollowsC/flake.nix +{ + inputs.D.url = "path:nosuchflake"; + outputs = _: {}; +} +EOF +cat < $flakeFollowsB/flake.nix +{ + inputs.C.url = "path:$flakeFollowsC"; + outputs = _: {}; +} +EOF +cat < $flakeFollowsA/flake.nix +{ + inputs.B.url = "path:$flakeFollowsB"; + inputs.D.url = "path:$flakeFollowsD"; + inputs.B.inputs.C.inputs.D.follows = "D"; + outputs = _: {}; +} +EOF + +nix flake lock $flakeFollowsA + +[[ $(jq -c .nodes.C.inputs.D $flakeFollowsA/flake.lock) = '["D"]' ]] From 6f65c117802ef4683eeaf64bc079cc3a8b9608c2 Mon Sep 17 00:00:00 2001 From: Sebastian Ullrich Date: Sun, 28 Aug 2022 11:46:29 +0200 Subject: [PATCH 2/5] Fix overlapping flake follows --- src/libexpr/flake/lockfile.cc | 2 +- tests/flakes/follow-paths.sh | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc index 60b52d578..384ead05b 100644 --- a/src/libexpr/flake/lockfile.cc +++ b/src/libexpr/flake/lockfile.cc @@ -338,7 +338,7 @@ void LockFile::check() for (auto & [inputPath, input] : inputs) { if (auto follows = std::get_if<1>(&input)) { - if (!follows->empty() && !get(inputs, *follows)) + if (!follows->empty() && !findInput(*follows)) throw Error("input '%s' follows a non-existent input '%s'", printInputPath(inputPath), printInputPath(*follows)); diff --git a/tests/flakes/follow-paths.sh b/tests/flakes/follow-paths.sh index e6751b06e..c12dbe0f6 100644 --- a/tests/flakes/follow-paths.sh +++ b/tests/flakes/follow-paths.sh @@ -149,7 +149,7 @@ git -C $flakeFollowsA add flake.nix nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid'" nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid2'" -# Test nested flake overrides +# Test nested flake overrides: A overrides B/C/D cat < $flakeFollowsD/flake.nix { outputs = _: {}; } @@ -178,3 +178,33 @@ EOF nix flake lock $flakeFollowsA [[ $(jq -c .nodes.C.inputs.D $flakeFollowsA/flake.lock) = '["D"]' ]] + +# Test overlapping flake follows: B has D follow C/D, while A has B/C follow C + +cat < $flakeFollowsC/flake.nix +{ + inputs.D.url = "path:$flakeFollowsD"; + outputs = _: {}; +} +EOF +cat < $flakeFollowsB/flake.nix +{ + inputs.C.url = "path:nosuchflake"; + inputs.D.url = "path:nosuchflake"; + inputs.D.follows = "C/D"; + outputs = _: {}; +} +EOF +cat < $flakeFollowsA/flake.nix +{ + inputs.B.url = "path:$flakeFollowsB"; + inputs.C.url = "path:$flakeFollowsC"; + inputs.B.inputs.C.follows = "C"; + outputs = _: {}; +} +EOF + +# bug was not triggered without recreating the lockfile +nix flake lock $flakeFollowsA --recreate-lock-file + +[[ $(jq -c .nodes.B.inputs.D $flakeFollowsA/flake.lock) = '["B","C","D"]' ]] From 85248543b5520e0ac7655b0ce1ebfa2543992498 Mon Sep 17 00:00:00 2001 From: Michael Hoang Date: Mon, 29 Aug 2022 22:17:06 +1000 Subject: [PATCH 3/5] Only push Docker image when Docker secrets are set --- .github/workflows/ci.yml | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 956f81684..86b5dfd2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ permissions: read-all jobs: tests: - needs: [check_cachix] + needs: [check_secrets] strategy: matrix: os: [ubuntu-latest, macos-latest] @@ -22,30 +22,34 @@ jobs: - uses: cachix/install-nix-action@v17 - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - uses: cachix/cachix-action@v10 - if: needs.check_cachix.outputs.secret == 'true' + if: needs.check_secrets.outputs.cachix == 'true' with: name: '${{ env.CACHIX_NAME }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - run: nix --experimental-features 'nix-command flakes' flake check -L - check_cachix: + check_secrets: permissions: contents: none - name: Cachix secret present for installer tests + name: Check Cachix and Docker secrets present for installer tests runs-on: ubuntu-latest outputs: - secret: ${{ steps.secret.outputs.secret }} + cachix: ${{ steps.secret.outputs.cachix }} + docker: ${{ steps.secret.outputs.docker }} steps: - - name: Check for Cachix secret + - name: Check for secrets id: secret env: _CACHIX_SECRETS: ${{ secrets.CACHIX_SIGNING_KEY }}${{ secrets.CACHIX_AUTH_TOKEN }} - run: echo "::set-output name=secret::${{ env._CACHIX_SECRETS != '' }}" + _DOCKER_SECRETS: ${{ secrets.DOCKERHUB_USERNAME }}${{ secrets.DOCKERHUB_TOKEN }} + run: | + echo "::set-output name=cachix::${{ env._CACHIX_SECRETS != '' }}" + echo "::set-output name=docker::${{ env._DOCKER_SECRETS != '' }}" installer: - needs: [tests, check_cachix] - if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true' + needs: [tests, check_secrets] + if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true' runs-on: ubuntu-latest outputs: installerURL: ${{ steps.prepare-installer.outputs.installerURL }} @@ -64,8 +68,8 @@ jobs: run: scripts/prepare-installer-for-github-actions installer_test: - needs: [installer, check_cachix] - if: github.event_name == 'push' && needs.check_cachix.outputs.secret == 'true' + needs: [installer, check_secrets] + if: github.event_name == 'push' && needs.check_secrets.outputs.cachix == 'true' strategy: matrix: os: [ubuntu-latest, macos-latest] @@ -80,11 +84,12 @@ jobs: - run: nix-instantiate -E 'builtins.currentTime' --eval docker_push_image: - needs: [check_cachix, tests] + needs: [check_secrets, tests] if: >- github.event_name == 'push' && github.ref_name == 'master' && - needs.check_cachix.outputs.secret == 'true' + needs.check_secrets.outputs.cachix == 'true' && + needs.check_secrets.outputs.docker == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -94,7 +99,7 @@ jobs: - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV - uses: cachix/cachix-action@v10 - if: needs.check_cachix.outputs.secret == 'true' + if: needs.check_secrets.outputs.cachix == 'true' with: name: '${{ env.CACHIX_NAME }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' From a8b3d777fbdaf0b732f129e5be62cd2a1227674b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Sep 2022 15:26:19 +0200 Subject: [PATCH 4/5] Revert "Merge pull request #6621 from Kha/nested-follows" This reverts commit c530cda345377370c52a616d608de88b9d67cd40, reversing changes made to 4adcdff5c1d5f9f135c4ec61d690890443c19e6a. --- src/libexpr/flake/flake.cc | 47 ++++++++------------------- src/libexpr/flake/lockfile.cc | 2 +- tests/flakes/follow-paths.sh | 60 ----------------------------------- 3 files changed, 14 insertions(+), 95 deletions(-) diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 81762a0af..105e76bc6 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -90,11 +90,11 @@ static void expectType(EvalState & state, ValueType type, static std::map parseFlakeInputs( EvalState & state, Value * value, const PosIdx pos, - const std::optional & baseDir, InputPath lockRootPath, unsigned depth); + const std::optional & baseDir, InputPath lockRootPath); static FlakeInput parseFlakeInput(EvalState & state, const std::string & inputName, Value * value, const PosIdx pos, - const std::optional & baseDir, InputPath lockRootPath, unsigned depth) + const std::optional & baseDir, InputPath lockRootPath) { expectType(state, nAttrs, *value, pos); @@ -118,7 +118,7 @@ static FlakeInput parseFlakeInput(EvalState & state, expectType(state, nBool, *attr.value, attr.pos); input.isFlake = attr.value->boolean; } else if (attr.name == sInputs) { - input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath, depth + 1); + input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath); } else if (attr.name == sFollows) { expectType(state, nString, *attr.value, attr.pos); auto follows(parseInputPath(attr.value->string.s)); @@ -163,11 +163,7 @@ static FlakeInput parseFlakeInput(EvalState & state, input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake); } - if (!input.follows && !input.ref && depth == 0) - // in `input.nixops.inputs.nixpkgs.url = ...`, we assume `nixops` is from - // the flake registry absent `ref`/`follows`, but we should not assume so - // about `nixpkgs` (where `depth == 1`) as the `nixops` flake should - // determine its default source + if (!input.follows && !input.ref) input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", inputName}}); return input; @@ -175,7 +171,7 @@ static FlakeInput parseFlakeInput(EvalState & state, static std::map parseFlakeInputs( EvalState & state, Value * value, const PosIdx pos, - const std::optional & baseDir, InputPath lockRootPath, unsigned depth) + const std::optional & baseDir, InputPath lockRootPath) { std::map inputs; @@ -188,8 +184,7 @@ static std::map parseFlakeInputs( inputAttr.value, inputAttr.pos, baseDir, - lockRootPath, - depth)); + lockRootPath)); } return inputs; @@ -235,7 +230,7 @@ static Flake getFlake( auto sInputs = state.symbols.create("inputs"); if (auto inputs = vInfo.attrs->get(sInputs)) - flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath, 0); + flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath); auto sOutputs = state.symbols.create("outputs"); @@ -318,19 +313,6 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup return getFlake(state, originalRef, allowLookup, flakeCache); } -/* Recursively merge `overrides` into `overrideMap` */ -static void updateOverrides(std::map & overrideMap, const FlakeInputs & overrides, - const InputPath & inputPathPrefix) -{ - for (auto & [id, input] : overrides) { - auto inputPath(inputPathPrefix); - inputPath.push_back(id); - // Do not override existing assignment from outer flake - overrideMap.insert({inputPath, input}); - updateOverrides(overrideMap, input.overrides, inputPath); - } -} - /* Compute an in-memory lock file for the specified top-level flake, and optionally write it to file, if the flake is writable. */ LockedFlake lockFlake( @@ -393,9 +375,12 @@ LockedFlake lockFlake( /* Get the overrides (i.e. attributes of the form 'inputs.nixops.inputs.nixpkgs.url = ...'). */ for (auto & [id, input] : flakeInputs) { - auto inputPath(inputPathPrefix); - inputPath.push_back(id); - updateOverrides(overrides, input.overrides, inputPath); + for (auto & [idOverride, inputOverride] : input.overrides) { + auto inputPath(inputPathPrefix); + inputPath.push_back(id); + inputPath.push_back(idOverride); + overrides.insert_or_assign(inputPath, inputOverride); + } } /* Check whether this input has overrides for a @@ -430,12 +415,6 @@ LockedFlake lockFlake( // Respect the “flakeness” of the input even if we // override it i->second.isFlake = input2.isFlake; - if (!i->second.ref) - i->second.ref = input2.ref; - if (!i->second.follows) - i->second.follows = input2.follows; - // Note that `input.overrides` is not used in the following, - // so no need to merge it here (already done by `updateOverrides`) } auto & input = hasOverride ? i->second : input2; diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc index 384ead05b..60b52d578 100644 --- a/src/libexpr/flake/lockfile.cc +++ b/src/libexpr/flake/lockfile.cc @@ -338,7 +338,7 @@ void LockFile::check() for (auto & [inputPath, input] : inputs) { if (auto follows = std::get_if<1>(&input)) { - if (!follows->empty() && !findInput(*follows)) + if (!follows->empty() && !get(inputs, *follows)) throw Error("input '%s' follows a non-existent input '%s'", printInputPath(inputPath), printInputPath(*follows)); diff --git a/tests/flakes/follow-paths.sh b/tests/flakes/follow-paths.sh index c12dbe0f6..19cc1bafa 100644 --- a/tests/flakes/follow-paths.sh +++ b/tests/flakes/follow-paths.sh @@ -148,63 +148,3 @@ git -C $flakeFollowsA add flake.nix nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid'" nix flake lock $flakeFollowsA 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid2'" - -# Test nested flake overrides: A overrides B/C/D - -cat < $flakeFollowsD/flake.nix -{ outputs = _: {}; } -EOF -cat < $flakeFollowsC/flake.nix -{ - inputs.D.url = "path:nosuchflake"; - outputs = _: {}; -} -EOF -cat < $flakeFollowsB/flake.nix -{ - inputs.C.url = "path:$flakeFollowsC"; - outputs = _: {}; -} -EOF -cat < $flakeFollowsA/flake.nix -{ - inputs.B.url = "path:$flakeFollowsB"; - inputs.D.url = "path:$flakeFollowsD"; - inputs.B.inputs.C.inputs.D.follows = "D"; - outputs = _: {}; -} -EOF - -nix flake lock $flakeFollowsA - -[[ $(jq -c .nodes.C.inputs.D $flakeFollowsA/flake.lock) = '["D"]' ]] - -# Test overlapping flake follows: B has D follow C/D, while A has B/C follow C - -cat < $flakeFollowsC/flake.nix -{ - inputs.D.url = "path:$flakeFollowsD"; - outputs = _: {}; -} -EOF -cat < $flakeFollowsB/flake.nix -{ - inputs.C.url = "path:nosuchflake"; - inputs.D.url = "path:nosuchflake"; - inputs.D.follows = "C/D"; - outputs = _: {}; -} -EOF -cat < $flakeFollowsA/flake.nix -{ - inputs.B.url = "path:$flakeFollowsB"; - inputs.C.url = "path:$flakeFollowsC"; - inputs.B.inputs.C.follows = "C"; - outputs = _: {}; -} -EOF - -# bug was not triggered without recreating the lockfile -nix flake lock $flakeFollowsA --recreate-lock-file - -[[ $(jq -c .nodes.B.inputs.D $flakeFollowsA/flake.lock) = '["B","C","D"]' ]] From f4d7208e235d129ad3b0fe25b518fa36d960ec4d Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Thu, 1 Sep 2022 11:48:50 -0500 Subject: [PATCH 5/5] Update boehmgc-coroutine-sp-fallback.diff for darwin The darwin_stop_world implementation is slightly different. sp goes to altstack_lo instead of lo in this case. Assuming that is an implementation detail. But the fix is the same, when we detect alstack_lo outside of the expected stack range, we reset it to hi - stack_limit. Here stack_limit is calculated with pthread_get_stacksize_np since that is the BSD equivalent to pthread_attr_getstacksize. --- boehmgc-coroutine-sp-fallback.diff | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/boehmgc-coroutine-sp-fallback.diff b/boehmgc-coroutine-sp-fallback.diff index e659bf470..8fdafbecb 100644 --- a/boehmgc-coroutine-sp-fallback.diff +++ b/boehmgc-coroutine-sp-fallback.diff @@ -1,3 +1,35 @@ +diff --git a/darwin_stop_world.c b/darwin_stop_world.c +index 3dbaa3fb..36a1d1f7 100644 +--- a/darwin_stop_world.c ++++ b/darwin_stop_world.c +@@ -352,6 +352,7 @@ GC_INNER void GC_push_all_stacks(void) + int nthreads = 0; + word total_size = 0; + mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ; ++ size_t stack_limit; + if (!EXPECT(GC_thr_initialized, TRUE)) + GC_thr_init(); + +@@ -407,6 +408,19 @@ GC_INNER void GC_push_all_stacks(void) + GC_push_all_stack_sections(lo, hi, p->traced_stack_sect); + } + if (altstack_lo) { ++ // When a thread goes into a coroutine, we lose its original sp until ++ // control flow returns to the thread. ++ // While in the coroutine, the sp points outside the thread stack, ++ // so we can detect this and push the entire thread stack instead, ++ // as an approximation. ++ // We assume that the coroutine has similarly added its entire stack. ++ // This could be made accurate by cooperating with the application ++ // via new functions and/or callbacks. ++ stack_limit = pthread_get_stacksize_np(p->id); ++ if (altstack_lo >= altstack_hi || altstack_lo < altstack_hi - stack_limit) { // sp outside stack ++ altstack_lo = altstack_hi - stack_limit; ++ } ++ + total_size += altstack_hi - altstack_lo; + GC_push_all_stack(altstack_lo, altstack_hi); + } diff --git a/pthread_stop_world.c b/pthread_stop_world.c index 4b2c429..1fb4c52 100644 --- a/pthread_stop_world.c