Skip to content

StackOverflowException Plz.

Have you ever got your Pharo environment frozen? A frozen system does not give a good impression. And the worst thing is that this happens for many newcomers :/.

Stack pages

Well, it turns out the most common way for this to happen is by writing something like the following. Which I don't know you, but I sometimes do by mistake.

MyClass >> myGetter
    ^ self myGetter

As you may know, this method doesn't return a value. It doesn't access an instance variable. It doesn't even politely fail.

It just calls itself.

Forever.

So what happens to the stack?

Every time myGetter sends myGetter, another activation is needed in the stack.

Conceptually, the stack starts looking like this:

+----------------+
|    myGetter    |
+----------------+
|    myGetter    |
+----------------+
|    myGetter    |
+----------------+
|    myGetter    |
+----------------+
|       ...      |
+----------------+

And then more of those.

And more.

And more.

This is recursion in its purest form: a method looking at the stack and saying, "I can fit one more frame." But the Pharo stack isn't just one enormous contiguous pile of activation records. It is organized into pages, typically of 2 KB. As the stack grows, more pages are involved.

And when all pages get full, one stack page is be evicted to make room for more stack What's this eviction? It just creates the corresponding Context object for each frame (what scientists call the reification) and puts them in the heap with the rest of the objects.

Stack pages

And this process gets on and on, until the recursion stops. Filling the heap with objects, and launching the GC

GC

However, the GC can do nothing! Because all those objects are linked together. And this GC invocation just takes time, for nothing.

GC does nothing

And then the process goes on, and on. The stack grows and grows.

GC does nothing

And the GC gets called over and over. And each time it takes more time, because there are more live objects!

Big GC

Ctrl-dot to the rescue?

You may be having this internal conversation:

You: "hey, I'm an experienced Smalltalker, I know I can stop that with Ctrl+."

Also You: "But that does not work all the time, are you sure? Maybe we need to fix Ctrl+. and that's all"

Guille: "Let me stop you here: I'll tell you why Ctrl+. does not work"

Turns out that its not so common to write a recursive getter (it happens but it's pretty obvious and easy to fix...). Its more common to write an indirect recursion, which is less obvious: method A calls B, which calls A. Right?

Now I'll make my case: more commonly, such infinite indirect recursions happen on printOn: methods.

printOn: attacks

And then you hit Ctrl+., expecting the malicious process to be suspended and a debugger to open. Right?

Ctrl dot Debugger

The issue is, the debugger will call printOn: to print your objects!

Oh no!

And then the process starts again, you never see your debugger, and your heap looks like this:

Flooding

So here's my take

Give ME a StackOverflowException PLZ.

Flooding

But that requires some work. When should we consider there is an overflow? And how do we detect it cheaply?

One way to do it is to piggy back into page overflow. Just for reference, in the current implementation, each Morphic cycle consume around 30 stack pages. Maybe our stack pages are too small? For comparison, OpenJDK traditionally gives each thread a default stack size around 1024 KB.

An implementation

Here it is:

https://github.com/pharo-project/pharo-vm/pull/710

https://github.com/pharo-project/pharo-vm/pull/1072

https://github.com/pharo-project/pharo/pull/18610

This implements:

  • Configurable stack page size and moving to a default 1MB stack page
  • When a stack overflow occurs, call back into the image, which throws an exception
  • Eventually this can be made so processes can override this behavior and keep going
  • Keep it backwards compatible

Next steps and final thoughts

Of course, we need some performance measurements for some corner cases. E.g., lots of processes fighting for stack pages. They are on the way ;)

Is this finished? Well, when is something ever finished? But I believe this is good enough.

Happy overflowing! 🥞