Skip to content

Make EvalState::callDepth thread-local #13479

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

edolstra
Copy link
Member

Motivation

This is needed to make it thread-safe.

Context


Add 👍 to pull requests you find important.

The Nix maintainer team uses a GitHub project board to schedule and track reviews.

This is needed to make it thread-safe.
Copy link
Member

@Ericson2314 Ericson2314 left a comment

Choose a reason for hiding this comment

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

Why is it a static? That seems bad if there are multiple interpreters

Copy link
Contributor

@xokdvium xokdvium left a comment

Choose a reason for hiding this comment

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

That seems bad if there are multiple interpreters

Yeah, this is a non-starter fundamentally. This would really conflict with the multi-evaluator goals (cc @roberth). We should make code more reusable and without globals.

I think the idea here is to make the call depth local to an evaluation worker (by abusing thread_local mechanisms). Wouldn't it be better to instead have that concept expressed as an evaluation context of sorts?

@edolstra
Copy link
Member Author

edolstra commented Jul 16, 2025

Why is it a static? That seems bad if there are multiple interpreters

Why? Presumably any thread will have only one interpreter active at any given time. Or, in the unlikely scenario that one interpreter calls another, they should share the same callDepth variable since recursion depth is the sum of all active calls.

This would really conflict with the multi-evaluator goals

What's the use case for that?

@roberth
Copy link
Member

roberth commented Jul 16, 2025

Multithreading creates a situation where callDepth is even less deterministic than it used to be. Imagine thunk A is strict in B, which is strict in C. If thread 1 wants A and thread 2 wants B, and thread A is near its limit, the execution ordering between 1 and 2 will decide whether C is evaluated in 1, where it fails, or 2, where it succeeds.

In other words callDepth will fail nondeterministically, which defeats its purpose.

I'm not a big fan of callDepth anyway. IMO it should behave no differently than any other memory consuming behavior. Nothing in the language semantics dictates this, and it puts unnecessary restrictions on the kinds of code that can be written.
It can be removed in two ways I'm aware of

  • turn thunk evaluation from an if is thunk to while is thunk, so that evaluation work can be resumed on an intermediate thunk after returning, much to the effect of a tail call
  • jump to a new coroutine stack once we're near the end of the current stack space

The prior is more memory efficient but also produces less detailed error traces, as the return terminates the trys. The latter is less invasive in terms of code changes.

That all is in addition to worries that I share about the reduced flexibility. This could affect the unit tests, as well as C API users, and it may make experimentation with the evaluator more difficult, e.g. new caching approaches or agent-style isolated heaps per flake.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants