The other day I wrote an entry about using bind/trigger on a local variable and what can go wrong if you do this. But why would somebody want to do such a thing? Isn’t this just an obscure corner of the language with a curious behavior?
It turns out that this example came up in actual code, and it caused us quite a debugging headache.
Take a look at the HttpRequest class. It has a fairly complicated state machine. The current state of the object is visible both through state variables (started, connecting, doneConnect, etc.) and also through a series of callbacks (onDone, onConnecting, onDoneConnecting, etc.). Strictly speaking, these are redundant. An earlier version of this API didn’t have the callback interfaces. I’m not completely sure why, but I think that callbacks (like listeners) were viewed as a Java-like construct, and the designers of the API wanted an interface with more of a JavaFX-Script flavor. This is completely understandable; the shape of an API is intimately intertwined with the mechanisms and constructs available in the language.
In Java, writing a class with public fields is poor style. It allows uncontrolled writes to the field, and there is no way for anyone — neither the client nor the class’s implementation — to detect when such a field has been modified. Instead of exposing a field, you have to provide a getter and setter methods. If you want a client to be notified when your object’s state changes, you have to set up a listener of some sort. It’s very common to for classes to use listeners to notify clients of state changes. This has led to a proliferation of listener interfaces in the class library, which in turn has led to a proliferation of little listener methods in client code. Most listeners are very small. They usually just copy a value or call an update method. If you have a one-line listener, it requires half a dozen lines of inner class boilerplate and a fair number of confusing braces and parentheses. I think this has contributed quite a bit to Java’s reputation as a verbose language.
For example, in Java, if you have a Rectangle rect that you want always to be 50 pixels over and 100 pixels down relative to the location of otherRect, you’d do something like this. (This doesn’t correspond to any actual Java class, but you can see the point.)
otherRect.addStateChangeListener(
new StateChangeListener() {
public void stateChanged(Rectangle otherRect) {
rect.setLocation(otherRect.getX() + 50, otherRect.getY() + 100);
}
}
);
By contrast, in JavaFX Script, an object’s variables can be made read-only to the general public using the public-read access modifier. Furthermore, clients can detect changes to another object’s variables by using the bind mechanism. This leads to style of object coupling where objects expose state via publicly-readable variables, and where clients bind to them in order to pick up state changes. Binding works great if your object’s state variables are updated as a function of some other object’s state variables. The rectangle location updating code would look like this in JavaFX Script:
var rect = Rectangle {
x: bind otherRect.x + 50
y: bind otherRect.y + 100
}
This is really cool. I think we’d all agree that the JavaFX Script example is much more concise, powerful, readable, and understandable. Great!
The problem is that, while bind works well when updating values as functions of other values, it doesn’t work so well when you want to take action (that is, perform a procedure) upon certain state changes. Let’s imagine that the HttpRequest object had no onInput callback (as was the case in the past). When the request body becomes available, the input field of the HttpRequest object is set to an InputStream from which the data can be read. In this style of API, instead of callbacks, clients of the HttpRequest class are expected to use bind to detect state changes. Let’s try to write some code that does this.
We want to bind to the request’s input field… but bind can only appear as the initializer of a variable declaration, or as the initializer within an object literal. So we’ll have to cook up another variable upon which to hang the bind:
var req = HttpRequest { ... };
var xyz = bind req.input;
This doesn’t do us much good; all it does is update the xyz variable when req.input changes from null to a valid InputStream. Recall that a bind expression causes re-evaluation of the portions of an expression that are affected by a change to a bound value, including function calls if a bound value is a parameter to a function. So we could try something like this:
var req = HttpRequest { ... };
var xyz = bind handleInput(req.input);
function handleInput(is: InputStream) {
...
}
This doesn’t really work, however. We have to return a value from the handleInput() function, and this has to match the type of the xyz variable. But this value is essentially unused and is merely a distraction:
var req = HttpRequest { ... };
var xyz: Boolean = bind handleInput(req.input);
function handleInput(is: InputStream): Boolean {
...
return true;
}
You can leave off the Boolean type declarations on xyz and for the return value of handleInput(), because the compiler will infer the proper type. Still, it’s a bit clunky that you have to declare a useless variable and return a useless value from the handleInput() function.
Isn’t there a better way? There sure is. JavaFX Script has a trigger mechanism (which is spelled on replace) that allows some arbitrary code to be executed when a variable’s value changes. If we were to use a trigger, it would look something like this:
var req = HttpRequest { ... };
var input = bind req.input on replace {
// read from input here
};
This is quite a bit better. We don’t have to cook up a function with a new name, and we don’t have to declare a useless variable and return a useless value from our function. The on-replace code is tied directly to the new variable, the one that’s bound to the variable we’re interested in. This is pretty concise and powerful.
I’m starting to see this idiom pop up in a lot of code. It’s useful under the following circumstances: a) you want to write code that’s triggered on a readable variable in another object, and b) that other object doesn’t provide a callback function or listener. Ideally, in some sense, you’d want to install a trigger on the variable in the other object. But you can’t do that: you can only install a trigger at the declaration of a new variable. So, you have to declare a new variable of your own, use bind to copy the value of the other object’s variable, and use an on replace trigger to have your code run when the other object’s variable is updated.
This technique reminds me of the Introduce Foreign Method refactoring, where you can’t add a method to another class, so you add it outside and treat it idiomatically as if it were a new method on that class. I’ll therefore call this technique the foreign trigger idiom.
This is all sort-of moot now, since my example is based on the older version of the HttpRequest API that didn’t have callbacks. As of 1.0, the HttpRequest has callbacks, so instead of using a foreign trigger you’d just supply a function as the value of the onInput variable of HttpRequest. But there’s still a need for foreign triggers in other parts of the API. Consider the Image class. This class allows images to be loaded in the background, by setting the backgroundLoading variable. How can you tell when the image is done loading? There’s no callback function, but the progress variable is updated and reaches 100 when the image is finished loading. So you could do something like this:
var img = Image {
backgroundLoading: true
...
};
var progress = bind img.progress on replace {
if (progress == 100) {
// take action now that img is done loading
}
};
You can see this idiom in use in various JavaFX samples, such as the one here.
All well and good. But what does this have to do with the stuff I was talking about earlier, regarding the lifetime of local variables?
If you’re writing a simple script, you typically tend to declare your variables at top level. These variables live as long as your script is running, and objects they refer to aren’t garbage collected for the lifetime of your application. So the foreign trigger idiom works perfectly well for these cases.
Now suppose you’re writing a program where HttpRequest operations are performed repeatedly. For example, you might want to fetch all the photos in a particular photo set, or you might want to fetch all the calendar entries for each day of the month. Clearly, you don’t want to declare separate variables for each of these requests. You’d want to wrap things up in a function, and have this function called repeatedly as often as necessary. The code would look something like this:
function getEntryForDate(date: Date) {
var req = HttpRequest { ... };
var input = bind req.input on replace {
// process input and convert to a calendar entry
}
}
BANG! Can you see the bug? If not, look again!
The problem is that the trigger was declared on a local variable, and this local variable is subject to garbage collection. This code sometimes works and sometimes doesn’t work. In fact, this is the most insidious kind of bug. You can take the code and isolate it into its own script (using script-level variables) and it will work perfectly. If you use the function as-is and call it from a simple test program, it will almost always work. That’s because in a simple test program, not much else is going on, and GC probably won’t occur. But put this into a big application, and call it 30 times to get all the appointments for a month. GC happens, and suddenly and randomly your triggers stop firing.
The consequences are fairly dire for HttpRequest, since it requires the InputStream to be closed to indicate that processing of the request has been completed. This processing is usually handled by a trigger, but if the trigger is GC’d, processing of the request never completes. The HttpRequest implementation has a limit on the number of outstanding requests. Eventually the pending request limit will be reached, no new requests will be issued, and the system will grind to a halt. We tore our hair out for about a week until we figured out what was going on.
Since the foreign trigger idiom is so common, and since it’s so easy and dangerous to use it on local variables, I’ve filed a bug (JFXC-2168) on this problem. The solution isn’t obvious. There’s some discussion about potential solutions in the bug report.
Moreover, the problem isn’t confined to local variables. If you use the foreign trigger idiom on an object’s variables, you have to make sure the object itself doesn’t get garbage collected. If you don’t keep a reference around to the object in question, it’s liable to get collected itself along with your trigger, and you end up with exactly the same problem. More on that later.