How to increase recursion depth limit (stack size limit)?

Most of the programming language has feature to increase limit of recursion depth (python => sys.setrecursionlimit, ruby => RUBY_THREAD_VM_STACK_SIZE). How to do this with Crystal?

Specifically, I want this code to be compiled and run without error.

def foobar(depth)
  return true if depth == 5000000
  foobar(depth + 1)
end
p foobar(0) # => true

https://play.crystal-lang.org/#/r/b21o

1 Like

Hi!

Do you know of any language that compiles to native code that allows this?

1 Like

There is no explicit recursion depth limit. Your code simply exceeds the stack size (which is 8MB) and there’s no more room on the stack for the program to continue further.
Changing the stack size is technically possible, but I don’t think it’s a good idea because it affects all fibers and thus heavily increases the overall memory use. It’s probably better to refactor the code to avoid such deep recursion.

2 Likes

I believe (plz someone correct mt if I’m wrong) this is set by the OS, Crystal runtime just catch a signal and translate into an exception, so this post can help you:

TL;DR;
ulimit -s shows the stack default size
ulimit -s 16384 set the stack size to 16M.

4 Likes

FWIW in release mode this runs just fine because LLVM recognizes this can be tail-recursion:

$ crystal run --release tmp.cr
true
3 Likes

Crystal’s runtime allocates a stack for each fiber. And that stack size is managed by crystal, not the OS.

1 Like

But I don’t think the main stack is managed by us. And in the OP example it’s the main stack.

Yeah, indeed. The stack size of the main fiber can be adjusted by ulimit -s.

3 Likes

ulimit -s solved my problem. Thank you very much.

1 Like

The stack size for the main fiber is determined by OS settings, but the stack size for fibers other than the main fiber is fixed at 8MiB.

This is purely out of curiosity, but where did this number 8MiB come from?

According to DeepWiki, changing the stack size requires modifying Crystal’s source code and recompiling it. This is a rare limitation within Crystal, where many things are configurable.

I haven’t encountered a real-world need to increase or decrease the stack size. Still, I found it interesting.

I guess that simply no one found a need for it :person_shrugging:

I believe we chose 8MB because it’s the default stack size on Linux at least. Some BSD have much smaller stack sizes by default, though (e.g. OpenBSD).

It’s also virtual memory, and most of the stacks are only allocated when needed. That being said, there’s a limit on virtual memory, so having the ability to set a smaller stack size could be interesting (less virt => more fibers), and maybe fallback to use ulimit by default.

Related: Stack size of non-main threads on POSIX · Issue #15045 · crystal-lang/crystal · GitHub

The recursion limit is configurable on Python and Ruby, because those apply to their virtual machines, not the underlying interpreter process itself. This would suggest that a similar method could exist for Crystal’s interpreter too.