Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

worker: set stack size for worker threads #26049

Closed
wants to merge 1 commit into from

Conversation

addaleax
Copy link
Member

@addaleax addaleax commented Feb 11, 2019

(The first commit is #26037.)

This is so we can inform V8 about a known limit for the stack.

Otherwise, on some systems recursive functions may lead to
segmentation faults rather than “safe” failures.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines

@addaleax addaleax added blocked PRs that are blocked by other issues or PRs. worker Issues and PRs related to Worker support. labels Feb 11, 2019
@nodejs-github-bot nodejs-github-bot added the lib / src Issues and PRs related to general changes in the lib or src directory. label Feb 11, 2019
@addaleax addaleax removed the lib / src Issues and PRs related to general changes in the lib or src directory. label Feb 11, 2019
@addaleax
Copy link
Member Author

test/parallel/test-worker-stack-overflow.js Outdated Show resolved Hide resolved
src/node_worker.cc Outdated Show resolved Hide resolved
uintptr_t stack_base_;

// Full size of the thread's stack.
static constexpr size_t kStackSize = 4 * 1024 * 1024;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For platforms where 4MB is not the default stack size, does this prove as an added memory pressure, and if so, should we document this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this prove as an added memory pressure

@gireeshpunathil Not on most platforms in their default configuration, I assume – usually, memory pages are only actually committed when first accessed.

I wouldn’t explicitly document this, as it shouldn’t make much of a difference for users.

Worker* w = static_cast<Worker*>(arg);
const uintptr_t stack_top = reinterpret_cast<uintptr_t>(&arg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible that some compilers decide to take this address from the caller thread's stack as opposed to the current thread's stack? I guess it should not, but I am not up-to-date with compiler optimizations.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler doesn’t know whether there are different threads involved here. And if there’s something invalid about this, then V8 already has the same problem in their code (which I assume they don’t).

@gireeshpunathil
Copy link
Member

Should / Could we also expose this into the JS land? say: new Worker(... {stack: <size>});
that way, different workers could be spawned that vary in thread stack size based on the workload that they specializes on? Users can calibrate the values for specific tasks, and thereby optimize memory (in deployments where memory costs).

test/parallel/test-worker-stack-overflow.js Outdated Show resolved Hide resolved
@bnoordhuis
Copy link
Member

Should / Could we also expose this into the JS land? say: new Worker(... {stack: <size>});

Too small or too big stack sizes can be dangerous and the limits depend on the platform and other factors.

There's also the question of how a programmer can know what a reasonable stack size is for a given workload without knowing all about Node's inner workings, or the add-ons they use.

@addaleax
Copy link
Member Author

This is so we can inform V8 about a known limit for the stack.

Otherwise, on some systems recursive functions may lead to
segmentation faults rather than “safe” failures.
@addaleax
Copy link
Member Author

@addaleax addaleax added author ready PRs that have at least one approval, no pending requests for changes, and a CI started. and removed blocked PRs that are blocked by other issues or PRs. labels Feb 13, 2019
@addaleax
Copy link
Member Author

Landed in a80b29b

(If this should get backported to v10.x at some point, it depends on #26037.)

@addaleax addaleax closed this Feb 13, 2019
@addaleax addaleax deleted the worker-stack-overflow branch February 13, 2019 20:40
addaleax added a commit that referenced this pull request Feb 13, 2019
This is so we can inform V8 about a known limit for the stack.

Otherwise, on some systems recursive functions may lead to
segmentation faults rather than “safe” failures.

PR-URL: #26049
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
addaleax added a commit that referenced this pull request Feb 13, 2019
This is so we can inform V8 about a known limit for the stack.

Otherwise, on some systems recursive functions may lead to
segmentation faults rather than “safe” failures.

PR-URL: #26049
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
@targos targos mentioned this pull request Feb 14, 2019
@ulrichb
Copy link

ulrichb commented Jan 30, 2020

So, there is now a hard-coded stack size limit of 4 MB, right?

Is there any possibility to override this limit (e.g. using worker.resourceLimits)?

@addaleax
Copy link
Member Author

@ulrichb There isn’t, currently – as you can see, there was a brief discussion above about that: #26049 (comment)

If you’d like to see a feature like that, could you open a new issue and ideally provide some information about your use case?

@ulrichb
Copy link

ulrichb commented Jan 31, 2020

@addaleax I'm not quite sure why, but it seems that the stack limit can be influenced by the V8 switch --stack-size also for the worker threads. Could you confirm that? (Using Node 13.7)

BTW: My use case is that I'm hosting the TypeScript compiler in a worker thread which does a lot of recursion, and this hit the stack limit for really big files (I have generated code files with ~ 20kLOC.)

@ulrichb
Copy link

ulrichb commented Jan 31, 2020

... oh and I used --stack-size=2048 (2 MB), to solve my RangeError: Maximum call stack size exceeded exceptions. This is strange because it's more than the default for --stack-size, but it's less than the 4 MB of this pull request. (And yes, I'm sure that the error happens within the worker thread.)

@addaleax Any ideas? Do the two values (--stack-size and kStackSize) mean something slightly different? Or corresponds --stack-size to kStackBufferSize (192 kB)?

@addaleax
Copy link
Member Author

@ulrichb So…

  • --stack-size does not set the actual stack size; instead, it tells V8 about what the stack size is, mostly so that it knows when to bail out with a stack overflow exception.
  • kStackSize is the full stack size that is natively allocated for a Worker thread.
  • kStackBufferSize refers to a part of the full stack that is not free for use by V8, and instead only there so that C++ code that does not perform stack checks can still reasonably run when near the stack limit.

--stack-size corresponds to kStackSize - kStackBufferSize, then.

This is strange because it's more than the default for --stack-size, but it's less than the 4 MB of this pull request. (And yes, I'm sure that the error happens within the worker thread.)

I’m not sure what to say except that I fully agree with you – this is strange. I feel like --stack-size shouldn’t even have an effect on Worker threads at all, although it defintiely does. I think that qualifies as a separate bug, I’ll look into it.

One thing that I can tell you from anecdotal experience is that I’ve also been in situations in which lowering --stack-size actually removed stack overflow exceptions surfacing; the reason being that they then were thrown from another part of the program that was part of a try/catch. Something like that may or may not be the case here.

addaleax added a commit to addaleax/node that referenced this pull request Jan 31, 2020
It turns out that using `v8::Locker` undoes the effects of
passing an explicit stack limit as part of the `Isolate`’s
resource constraints.

Therefore, reset the stack limit manually after entering a Locker.

Refs: nodejs#26049 (comment)
addaleax added a commit that referenced this pull request Feb 8, 2020
It turns out that using `v8::Locker` undoes the effects of
passing an explicit stack limit as part of the `Isolate`’s
resource constraints.

Therefore, reset the stack limit manually after entering a Locker.

Refs: #26049 (comment)

PR-URL: #31593
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
codebytere pushed a commit that referenced this pull request Feb 17, 2020
It turns out that using `v8::Locker` undoes the effects of
passing an explicit stack limit as part of the `Isolate`’s
resource constraints.

Therefore, reset the stack limit manually after entering a Locker.

Refs: #26049 (comment)

PR-URL: #31593
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
codebytere pushed a commit that referenced this pull request Mar 15, 2020
It turns out that using `v8::Locker` undoes the effects of
passing an explicit stack limit as part of the `Isolate`’s
resource constraints.

Therefore, reset the stack limit manually after entering a Locker.

Refs: #26049 (comment)

PR-URL: #31593
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
codebytere pushed a commit that referenced this pull request Mar 17, 2020
It turns out that using `v8::Locker` undoes the effects of
passing an explicit stack limit as part of the `Isolate`’s
resource constraints.

Therefore, reset the stack limit manually after entering a Locker.

Refs: #26049 (comment)

PR-URL: #31593
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
codebytere pushed a commit that referenced this pull request Mar 30, 2020
It turns out that using `v8::Locker` undoes the effects of
passing an explicit stack limit as part of the `Isolate`’s
resource constraints.

Therefore, reset the stack limit manually after entering a Locker.

Refs: #26049 (comment)

PR-URL: #31593
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. worker Issues and PRs related to Worker support.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants