Exception Causes And Stellaris

CodeFX Occasionally #66–30th of October 2018

Nicolai Parlog
Nov 30, 2018 · 6 min read

Hi everyone,

As promised in my last newsletter, I’ve been very busy creating stuff for you. And isn’t that a good excuse to leave you hanging for six weeks? 😉 Besides a bunch of links, I have two stories to tell — one about exceptions, one about conquering the universe.

I send this newsletter out some Fridays. Or other days. Sometimes not for weeks. But as an actual email. So, subscribe!


Circular exception causes

Did you ever hunt an exception’s root cause in your IDE’s debugger and, while extending each exception’s cause, started going in circles because at some point an exception referenced itself as its cause? What's up with that?!

Turns out, we’re not the first to wonder about this. In fact, for circles consisting of at least two exceptions, it even caused problems within the JDK: There was a bug where, in such a case, Throwable::printStackTrace would enter an infinite loop. There was also an even older bug that proposed to detect such situations in Throwable.initCause(Throwable), which already prevents a throwable's cause to point to itself.

Wait, what’s initCause? And if it prevents "self-causation", then why do so many exceptions reference themselves as their cause. Like, NullPointerException. And IllegalStateException. And even StackOverflowError (oh, the irony). In fact, I couldn't find any core exception that doesn't do this.

Ok, so maybe this is the norm? By default, exceptions should have themselves as a cause? Nope, wrong again. Here’s what the Javadoc on the cause field in Throwable has to say about that:

The throwable that caused this throwable to get thrown, or null if this throwable was not caused by another throwable, or if the causative throwable is unknown. If this field is equal to this throwable itself, it indicates that the cause of this throwable has not yet been initialized.

At this point, I was somewhat confused. Here’s the logical flow as I reconstructed it:

  1. Throwable is initialized with cause pointing to itself to mark as "not yet initialized"
  2. If the constructor got called with a cause, it overrides the default
  3. If the constructor got called without a cause, the field isn’t set to null, though - somebody needs to initialize it later by calling initCause (with null or something else)

Step 3. is the fly in the ointment. I found no core exception that calls initCause! The Throwable contract strongly suggests that a non-this cause should be the norm and there's a method to set it, but it's largely unused by Java's central exception types. WTF, why?

A look at its source gives a hint:

public synchronized Throwable initCause(Throwable cause) {
if (this.cause != this)
throw new IllegalStateException("...");
if (cause == this)
throw new IllegalArgumentException(
"Self-causation not permitted", this);
this.cause = cause;
return this;
}

It’s not immediately obvious, but you can only call initCause once (without getting an exception) and I assume that's the reason that no important exception I could find actually calls it. As it turns out, some documentation alludes to that - here's the Javadoc of a RuntimeException-constructor that accepts no cause:

The cause is not initialized, and may subsequently be initialized by a call to initCause.

It seems like not setting the cause is a concession to letting us set it later. But under which circumstances? When we catch an exception that we didn’t create — but where does the cause come from then? 🤔 If this is a feature, I don’t get it…

I’ve reached a dead end for why many exceptions don’t initialize the cause. But why is it so complicated in the first place? While clawing my way through the JDK I at least found the answer to that question. Here’s a context comment from inside Throwable:

To allow Throwable objects to be made immutable and safely reused by the JVM, such as OutOfMemoryErrors, fields of Throwable that are writable in response to user actions, cause, stackTrace, and suppressedExceptions obey the following protocol:

  1. The fields are initialized to a non-null sentinel value which indicates the value has logically not been set.
  2. Writing a null to the field indicates further writes are forbidden.
  3. The sentinel value may be replaced with another non-null value.

For example, implementations of the HotSpot JVM have preallocated OutOfMemoryError objects to provide for better diagnosability of that situation. These objects are created without calling the constructor for that class and the fields in question are initialized to null. To support this capability, any new fields added to Throwable that require being initialized to a non-null value require a coordinated JVM change.

Aha.



Stellaris

I used to play a lot of games when I was younger, but nowadays I hardly have time for that. Last weekend, though, I accidentally pulled an all-nighter with Stellaris and it was so much fun, I need to tell you about it.

Stellaris is a classic 4X grand strategy game, in which you create a race that’s just about to reach out to the stars, where it will encounter all kinds of little anomalies, helpful and harmful events, powerful ancient beings, dormant fallen empires, but, most of all, other races on the same track as you: to dominate the galaxy.

You can focus on expansion, technology, social advancement, warfare, or diplomacy; you can go it alone or forge alliances; you can be peaceful or aggressive; you can even see it as a role-playing game, trying to stay true to your race’s character or evolving it to meet its challenges. Whether you’re aiming for psionic might, genetic evolution, or replacing your meatbags with robots — the choice is yours.

As you can tell, I’m pretty happy with Stellaris, but beyond that I want to tell you about the specific game I started on Saturday.

There are tons of characteristics to choose from when creating a race and most of them are pretty incremental (a little more research, a little better at warfare), but some are all-defining. One of them is Devouring Swarm: You’re not only out to dominate the entire galaxy, no, you want to literally eat all other species. 😋 Unsurprisingly, they don’t really like that and so you’re barred from all diplomacy. It’s galactic warfare all the way!

My twist is that I also try to “play tall”, which means only colonizing a few superb planets, focusing on high tech and building megastructures like ringworlds, dyson spheres, or space habitats.

This leads to a rather interesting situation, where my empire is mostly devoid of life and, as I advance through the galaxy, the overall population drops. There’s a border between sanity on one and the void on the other side: Whatever I conquer lies in ruins just a few years later, never to be inhabited again by another soul.

When I’m done, the heavens will be free of the vermin that is the flesh. It will be pure! Muahahaha!!1

You should give it a try, it’s fun.



PS: Don’t forget to subscribe or recommend! :)

CodeFX Weekly

Whatever caught my interest throughout the week: libraries or tools I am using, what I’ve written or read that might interest you, fascinating questions on StackOverflow, conferences visits, odd tweets, anything really that connects to Java, software development, or writing.

Nicolai Parlog

Written by

Developer (mostly Java), blogger (http://codefx.org/), author (http://tiny.cc/jms), trainer (http://courses.codefx.org & https://www.youtube.com/c/codefx)

CodeFX Weekly

Whatever caught my interest throughout the week: libraries or tools I am using, what I’ve written or read that might interest you, fascinating questions on StackOverflow, conferences visits, odd tweets, anything really that connects to Java, software development, or writing.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade