Hard edges, small keys

Joanna Stern reviews the MacBook Air as a Windows laptop.

I enjoyed this—I’ve considered in the past whether the MacBook Air would be a suitable laptop for me even though most of the time I don’t run OS/X. (Conclusion: probably not any more, though it might have seemed that way once.)

She does highlight the thing I’ve always found most painful about the MacBook Air and MacBook Pro: that vicious sharp edge along the front. (sucks teeth in recollection of past pain)

But she likes the trackpad. I’m not keen on the Mac trackpad, finding it too easy to operate by accident and too hard to “click” reliably. Perhaps it’s just that the PC vendors’ attempts at the Crazy New-Era Big Trackpad are worse.

The review is of a machine with a US keyboard, so although there are some quibbles about keyboard layout, there’s nothing to compare to the difficulties presented to the UK programmer—most obviously the lack of a hash sign (#) anywhere on the keyboard, and the tiny, tiny Return key (right).

The Return key is hard to hit on every current Mac and MacBook UK keyboard, even where there’s plenty of room to spare in the chassis.

It just feels gratuitously punitive to me. And that’s surely the way it is, given that Apple did it perfectly well in their older keyboards. They do know how to make a big Return key. They have learned the technology. They just think it’s not quite appropriate to accommodate whims like that from us.

Wrapping a C++ library with JNI, part 3

In this series…

  • Introduction, outlining the general steps from starting with a C++ library to being able to build and run simple tests on some JNI wrappers;
  • Part 1, in which I design some simple Java classes and generate the stub wrapper code;
  • Part 2, in which I add just enough of the implementation to be able to do a test build;
  • Part 3 (this post), discussing object lifecycles in C++ and Java;
  • Part 4, the final episode covering a few remaining points of interest.

My previous post on this topic ended with a very simple test program built and run successfully, wrapping a small subset of the C++ library I was referring to.

That’s all I want to go through step-by-step, but there are still a couple more things to explain. The first one is…

Disposing of C++ objects

Java objects are garbage-collected: you don’t have to delete them when you’ve finished with them.

C++ objects are not. Unless they’re locally scoped objects allocated on the stack (i.e. created without a call to new), you have to delete them explicitly when you’ve finished with them, or else you’re creating a resource leak.

The wrapper code I’ve been describing uses Java objects that “own” their corresponding C++ objects. My Plugin object for example contains an opaque nativeObject handle which, on the C++ side, gets interpreted as a pointer to the C++ object that provides the plugin implementation. This object belongs to the Java Plugin wrapper, and it should have a corresponding lifetime which we need to manage. Nothing else is going to delete it if we forget to.

This means we need the Java object to delete its C++ object when it is itself deleted. But a Java object isn’t deleted explicitly, and it has no C++-style destructor function we could make this happen from.

We have two options:

  1. Add a method to the Java class that deletes the C++ object, and insist that users of our library remember to call it;
  2. Delete the C++ object from the Java class’s finalize method, which is called by the JVM when the Java object is garbage collected.

The second option, using finalize, is attractive because it reduces the burden on our users. Deleting the object would be automatic. If we take the first option, any method we add to delete our object will be one that isn’t required for most Java classes, and that also doesn’t exist in the same form in the C++ library we’re wrapping—so developers coming from either Java or C++ will quite likely overlook it.

But there are several problems with using finalize for this.

One problem is that, because the JVM only controls memory and resources within the Java heap, it can’t take into account any resources allocated in the native layer when determining which Java objects it needs to garbage-collect: our Java objects will seem smaller than they effectively are. So it might not finalize our objects quickly enough to prevent memory pressure.

A second problem is that we can’t guarantee which thread the finalize method will be called from. It’s quite important that our C++ object is deleted from the same thread that allocated it.

Finally, it’s possible that the C++ library we are wrapping might expect objects to be deleted in some particular order relative to one another, and we can’t enforce that if we leave the garbage collector to do it. When in C++, we should do as C++ programmers do.

And that means we need to get the user of our library to tell us when to delete objects: we must at least take our option 1. That is, we add a method called dispose to the Java class: when it’s called, it deletes the underlying native object. But, of course, our user—the programmer writing to our library—must remember to call it.

(Why “dispose”? Well, it’s a good name, and it’s the name used by some other libraries that rely on native code, such as the SWT widget toolkit.)

In our Plugin class, the dispose call would be simply

    public native void dispose();

and the implementation in Plugin.cpp, using the native handle helper method referred to in my earlier post, would be along the lines of

void
Java_org_vamp_1plugins_Plugin_dispose(JNIEnv *env, jobject obj)
{
    Plugin *p = getHandle(env, obj);
    setHandle(env, obj, 0);
    delete p;
}

In principle we could also make the finalize method call dispose in case the user forgets. But that’s probably unwise, as the library’s behaviour could become quite unpredictable in subtle ways if the pattern and order of deallocations depended on whether the user had remembered to dispose an object or had left it to the garbage collector.

Another possibility might be to test within finalize whether the object has been disposed, and print a warning to System.err if it hadn’t.

Coming next

In my next (and final) post on this subject, I’ll give an illustration of a function with rather more complex argument and return types than the ones we’ve seen so far.

Operating system updates

Google have released a version of their Chrome web browser for Android, and it seems to be rather good—but it only runs on the very latest version of Android, version 4. Which is a bit of an annoyance, because hardly anybody has that version.

Glancing at the 12 most popular Android phones on the Expansys site, I can see only two—the two variants of Google’s Galaxy Nexus—supplied with Android 4. That’ll change, but in the mean time there hasn’t been much of a rush to provide updates for older phones.

I’m generally ambivalent about operating system updates. I believe that both phones and PCs are bought on the basis of the way they look and work at the time, not in the expectation that updates will change anything significant.

As a long-term Linux user I’m all too familiar with the concept of the Ubuntu update that breaks everything, but I think my uncertainty about the wisdom of updates is common among people using most kinds of computer.

I updated my Galaxy Tab from Android 2.2 to 2.3: it improved battery life a bit, stopped cut and paste working in some applications, and provided no obvious interface improvements—not a big net positive. I know iPhone users who complain about Apple persuading them to install updates that slow down their previously perfectly good phones. My wife updated her WP7 phone recently, grumbling about the amount of time and laptop disc space used by the installer, only to find the update made no detectable change at all. OS/X 10.7 had a decidedly mixed response from users of earlier versions.

Why would anyone want to update anyway?

What drives updates is application support. The only time most users will start hunting for an operating system update—as opposed to installing one that’s thrust into their face by the device itself—is when they find they can’t run applications they care about because their OS is too old.

Even then, they’ll probably resent having to update to do it.

I don’t really care about Android 4 on my own device, which is fortunate because it doesn’t appear to be available. But I would like to try out Chrome. (There are several OK browsers for Android, but no really good ones—and the one I like best in principle, Firefox, itself gets less stable for me with every update.)

I wonder how fundamental Chrome’s dependency on Android 4 really is. Perhaps the only reason it didn’t exist before was that it needed some quite basic OS support that earlier versions couldn’t provide.

Or perhaps the dependency is seen as a serendipitous one, and the release of Chrome as a way to encourage users like me, and phone manufacturers, to update as soon as possible.

Wisława Szymborska

The Polish poet Wisława Szymborska died this week.

I can’t read a word of Polish, but in English translation she was one of my favourite poets. She wrote about large and sometimes terrifying themes using small subjects and a friendly, light ironic touch. She had perspective.

It’s gratifying that I can always
wake up before dying.

As soon as war breaks out,
I roll over on my other side.

I’m a child of my age,
but I don’t have to be.

A few years ago
I saw two suns.

And the night before last a penguin,
clear as day.

(from In Praise of Dreams, trans. Stanisław Barańczak & Clare Cavanagh)

The other Internet

Facebook aims to go public

So far I’ve mostly thought of Facebook as the other Internet.

It’s an Internet that contains all the stuff I don’t really want to know. A site with the handy property that almost everything on it requires you to log in to see it, meaning that I never see any of it and so can’t worry about it. Their login policy provides peril-sensitive sunglasses for the Internet user.

As the reach of their user database and APIs increases, the number of other sites needing a Facebook login increases too, and the proportion available without one diminishes.

But the Internet was inevitably going to fragment and warp. When I started using it, it was still possible to think that the set of one’s peers and potential friends on the Internet was “all the other people who use the Internet”. Now everyone uses it, and subsets of users can expect to use subsets of the Internet. The Internet-that-doesn’t-involve-Facebook keeps growing, even as its proportion of the whole shrinks.