About Philip Chimento

Expect less than the unexpected.

Free software at 40°C

It’s that time of year again, time for a belated reflection on the GUADEC conference!

In August I traveled to Thessaloniki, Greece, to attend first the annual GNOME Foundation board handover day, then the advisory board meeting, then the GUADEC conference and associated unconference days.

The board discussion focused quite a lot on the strategic goals for the GNOME Foundation which you can hear more about in executive director Neil McGovern’s talk. Nuritzi has also blogged about the process of putting together these strategic goals.

One of the goals that’s most important to me personally (and in fact, in a slightly different form it was part of my original candidacy statement the first time I was elected to the GNOME Foundation board) is:

Evaluate and adopt new technologies to stay competitive with proprietary desktops.

In the evening after the board handover I got a chance to talk about this with some GNOME people over gyros and why I think it’s important — but that’s a topic that could fill an entire blog post! And I intend to publish one at some point.

I’d like to describe some of the talks that I thought were highlights during the conference days.

Setting a Positive Voice For GNOME, Britt Yazel (video) — One of a surprisingly large number of talks this year about how a large free software project is made up of humans, something which people tend to forget.

Hack: Embedding Learning Tools in the OS, Meg Ford / Manuel Quiñones (video) — This is a great talk about some of the cool stuff in a project which is quite familiar to me; as I had worked on it for a year. I had not really gotten a chance to take a step back and see how cool it actually is, and this talk brought that home for me.

Environmentally Friendly GNOME, Philip Withnall (video) — Fantastic use of the relatively little data we have, to do quite a lot of order-of-magnitude calculations about GNOME’s environmental footprint, as well as suggestions on how to reduce it.

About Maintainers and Contributors, Georges Basile Stavracas Neto (video) — Another talk about humans, and one of the reasons why I think this conference will be regarded as a turning point in GNOME’s history. Georges talks honestly about the struggles that many of us face when we become the public face of a well-known project.

Free Software / Utopia, Deb Nicholson (video) — A strong closer for the conference, and a strong statement of values for a community that is — sorry if I sound like a broken record — made up of humans. I fully support the idea that we shouldn’t just try to equal proprietary software, but to do better, not only technically, but in our treatment of humans!

There were a lot of difficult decisions to make as to which conference track to follow and I ended up switching back and forth a lot, and in addition I was rehearsing my own talk. This meant I still had to miss some things that I would have liked to attend, like (unfortunately) the workshops on Saturday.

On Saturday there was also the annual meeting of the GNOME Foundation, but I think plenty of other people have blogged about that already.

On Sunday I delievered my talk (video, slides) and started to run out of time near the end, so the video shows how I gradually started talking faster and faster… Happily, a few people expressed interest in taking on some of the projects for GJS that I suggested in the talk, and I hope that when the next round of Outreachy and Google Summer of Code internships come around I will be able to be a mentor again.

During the unconference days I did not actually stick very much to the published schedule; I was mostly occupied with hallway conversations. I spent one day with the travel committee talking about what we could do to improve the travel sponsorship process, and taking advantage of the fact that most of the committee was in the same place to review the open tickets.

I also spent some time hacking on GJS, both by myself and with others: Meg Ford and Marco Trevisan started fixing some GJS bugs.

The last day was filled with a whirlwind tour of the many museums of Thessaloniki — anyone who knows me personally knows that I love museums and could have stayed for many more hours in each museum.

Thanks to the GNOME Foundation for sponsoring my attendance.

The Cause and the Effect

This week Richard Stallman resigned as president of the Free Software Foundation. It is long overdue, and I am grateful to Selam G., the writer of the blog post that sparked it.

I was disappointed to read that Michael Meeks’ post Tuesday on Planet GNOME repeated the excuses I’ve seen on Twitter and Reddit about mob rule and mischaracterization. Michael is of course entitled to that opinion, and unlike most Twitter and Reddit threads I’ve seen, has expressed it thoughtfully (which is why I think I can actually achieve something by writing this in turn.) I personally believe that that opinion does not stand up under scrutiny, and I hope writing a counterpoint might give him or others in the GNOME community food for thought.

I believe that we — especially in the GNOME community where it’s a goal to hold ourselves to high standards of treating each other well — must not let ourselves fall into the trap of saying ‘Stallman was just defending a friend, out come the pitchforks, just for one email, who will they come for next’ and thereby fail to see the whole picture. If it was really just one email and not years of well-documented bad behaviour and refusal to change, we’d be having an entirely different conversation.

Many who are grateful that Stallman has finally left the FSF are nonetheless anxious or grieving in some way: for the ideal of someone who may have been a hero to us before we realized what he was like in person; for trepidation about the future of the free software movement; or even for having to watch Stallman bring himself down in an avoidable, decades-long slow-motion train wreck. This is all understandable, but we should not let grief channel itself into minimizing or excusing or working around bad behaviour, or rules-lawyering about the interpretation of Stallman’s words. These two lines from Leonard Cohen and Sharon Robinson, singing about a different kind of grief, seem oddly fitting here:

Do not choose a coward’s explanation
That hides behind the cause and the effect

I will also refer you to Thomas Bushnell’s reflections from which I’d like to emphasize this paragraph, which is a response (expressed better than I could myself) to anyone who thinks that this one event can be regarded in isolation:

RMS’s loss of MIT privileges and leadership of the FSF are the appropriate responses to a pattern of decades of poor behavior. It does not matter if they are appropriate responses to a single email thread, because they are the right thing in the total situation.

The words of Matt Blaze are also appropriate here:

We will, as always, be treated to much examination of the precise nature and mass of the last straw, with observations that it would not by itself be sufficient to cause spinal damage in camels, and is therefore utterly harmless.


This post is my personal opinion, and is not written on behalf of the GNOME Foundation, its board of directors, nor anyone else.

Why Don’t You Just

If there was one phrase I could snap my fingers to make unutterable forevermore, it would be “why don’t you just …?”1

Someone is talking about a problem, or an achievement, or an idea, and the inevitable dismissive smartass pipes up and asks “Why don’t you just (move to a different apartment / adopt / rewrite it in Rust / eat more vegetables / mount your FTP account locally with curlftpfs, and then use SVN on the mounted filesystem)?”

“Why don’t you just?” is so second nature in my old field, physics, that XKCD made a comic about it: “So why does your field need a whole journal, anyway?”

“Why don’t you just?” means “I’ve thought about what you said for five seconds and I’m so smart that I really believe what I came up with in those five seconds is more valuable than whatever YOU thought of.”

“Why don’t you just?” means “I don’t trust you to do your own thinking.”

Sometimes “Why don’t you just?” even means “let me show you how smart I am by asking you why you didn’t just do something that I know won’t even work, and make YOU explain it to test if you’re as smart as me.”

The only possible answer to “Why don’t you just?” should be “If I could just, then I would just.”

…most of the time.

The tricky part is that sometimes, “Why don’t you just?” is the right question to ask, with the wrong wording. When you’re guiding people who are inexperienced at something, sometimes they do miss the obvious, easy solution. Or even experienced people miss it sometimes.

In those cases, how do you help them out without insulting their intelligence? Because “Why don’t you just?” is all about “me smart, you dumb”. The key is to put aside your ego and accept that this time, you may not get a chance to show that you are smart. First of all, check whether it is really the right time and place to give criticism! It might not be. If it is, then it’s a great rule of thumb to assume that they already tried the obvious thing you are about to suggest. That way, if they didn’t think of it themselves, they feel flattered and can save face. And if they did think of it or try it already, then you allow them to shine by explaining their thought process. Try something like “The way that I might have solved that problem is X. Would that also work in your situation?” Or “Stop me if you tried this already, but the way I’d have tackled it would be X.”

So, why don’t you just stop saying “why don’t you just?”


[1] Well, actually, I’d probably pick well, actually

Taking Out the Garbage

From the title, you might think this post is about household chores. Instead, I’m happy to announce that we may have a path to solving GJS’s “Tardy Sweep Problem”.

For more information about the problem, read The Infamous GNOME Shell Memory Leak by Georges Stavracas. This is going to be a more technical post than my previous post on the topic, which was more about the social effects of writing blog posts about memory leaks. So first I’ll recap what the problem is.

Garbage, garbage, everywhere

Buzz Lightyear meme with the caption "Garbage, Garbage Everywhere"At the root of the GNOME desktop is an object-oriented technology called GObject. GObjects are reference counted, but not garbage collected. As long as their reference count is nonzero, they are “alive”, and when their reference count drops to zero, they are deleted from memory.

GObject reference counting

Graphical user interfaces (such as a large part of GNOME Shell) typically involve lots of GObjects which all increase each other’s reference count. A diagram for a simple GUI window made with GTK might look like this:

A typical GUI would involve many more objects than this, but this is just for illustrating the problem.

Here, each box is an object in the C program.

Note that these references are all non-directional, meaning that they aren’t really implemented as arrows. In reality it looks more like a list of numbers; Window (1); Box (1); etc. Each object “knows” that it has one reference, but it knows nothing about which other objects own those references. This will become important later.

When the app closes, it drops its reference to the window. The window’s reference count becomes zero, so it is erased. As part of that, it drops all the references it owns, so the reference count of the upper box becomes zero as well, and so on down the tree. Everything is erased and all the memory is reclaimed. This all happens immediately. So far, so good.

Javascript objects

To write the same GUI in a Javascript program, we want each GObject in the underlying C code to have a corresponding Javascript object so that we can interact with the GUI from our Javascript code.

Javascript objects are garbage collected, and the garbage collector in the SpiderMonkey JS engine is a “tracing” garbage collector, meaning that on every garbage collection pass it starts out with objects in a “root set” that it knows are not garbage. It “traces” each of those objects, asking it which other objects it refers to, and keeps tracing each new object until it hits a dead end. Any objects that weren’t traced are considered garbage, and are deleted. (For more information, the Wikipedia article is informative: https://en.wikipedia.org/wiki/Tracing_garbage_collection)

We need to integrate the JS objects and their garbage collection scheme with the GObjects and their reference counting scheme. That looks like this:

The associations between the Javascript objects (“JS”) and the GObjects are bidirectional. That means, the JS object owns a reference to the GObject, meaning the reference count of every GObject in this diagram is 2. The GObject also “roots” the JS object (marks it as unable to be garbage collected) because the JS object may have some state set on it (for example, by writing button._alreadyClicked = false; in JS) that should not be lost while the object is still alive.

The JS objects can also refer to each other. For example, see the rightmost arrow from the window’s JS object to the button’s JS object. The JS code that created this GUI probably contained something like win._button = button;. These references are directional, because the JS engine needs to know which objects refer to which other objects, in order to implement the garbage collector.

Speaking of the garbage collector! The JS objects, unlike the GObjects, are cleaned up by garbage collection. So as long as a JS object is not “rooted” and no other JS object refers to it, the garbage collector will clean it up. None of the JS objects in the above graph can be garbage collected, because they are all rooted by the GObjects.

Toggle references and tardy sweeps

Two objects (G and JS) keeping each other alive equals a reference cycle, you might think. That’s right; as I described it above, neither object could ever get deleted, so that’s a memory leak right there. We prevent this with a feature called toggle references: when a GObject’s reference count drops to 1 we assume that the owner of the one remaining reference is the JS object, and so the GObject drops its reference to the JS object (“toggles down“). The JS object is then eligible for garbage collection if no other JS object refers to it.

(If this doesn’t make much sense, don’t worry. Toggle references are among the most difficult to comprehend code in the GJS codebase. It took me about two years after I became the maintainer of GJS to fully understand them. I hope that writing about them will demystify them for others a bit.)

When we close the window of this GUI, here is approximately what happens. The app drops its references to the GObjects and JS objects that comprise the window. The window’s reference count drops to 1, so it toggles down, dropping one direction of the association between GObject and JS object.

Unlike the GObject-only case where everything was destroyed immediately, that’s all that can happen for now! Everything remains in place until the next garbage collection, because at the top of the object tree is the window’s JS object. It is eligible to be collected because it’s not rooted and no other JS object refers to it.

Normally the JS garbage collector can collect a whole tree of objects at once. That’s why the JS engine needs to have all the information about the directionality of the references.

However, it won’t do that for this tree. The JS garbage collector doesn’t know about the GObjects. So unfortunately, it takes several passes of the garbage collector to get everything. After one garbage collection only the window is gone, and the situation looks like this:

Now, the outermost box’s JS object has nothing referring to it, so it will be collected on the next pass of the garbage collector:

And then it takes one more pass for the last objects to be collected:

The objects were not leaked, as such, but it took four garbage collection passes to get all of them. The problem we previously had, that Georges blogged about, was that the garbage collector didn’t realize that this was happening. In normal use of a Javascript engine, there are no GObjects that behave differently, so trees of objects don’t deconstruct layer by layer like this. So, there might be hours or days in between garbage collector passes, making it seem like that memory was leaked. (And often, other trees would build up in the intervening time between passes.)

Avoiding toggle references

To mitigate the problem Georges implemented two optimizations. First, the “avoid toggle references” patch, which was actually written by Giovanni Campagna several years ago but never finished, made it so that objects don’t start out using the toggle reference system. Instead, only the JS objects hold references to the GObjects. The JS object can get garbage collected whenever nothing else refers to it, and it will drop its reference to the GObject.

A problem then occurs when that wasn’t the last reference to the GObject, i.e. it’s being kept alive by some C code somewhere, and the GObject resurfaces again in JS, for example by being returned by a C function. In this case we recreate the JS object, assuming that it will be identical to the one that was already garbage collected. The only case where that assumption doesn’t hold, is when the JS code sets some state on one of the JS objects. For example, you execute something like myButton._tag = 'foo';. If myButton gets deleted and recreated, it won’t have a _tag property. So in the case where any custom state is set on a JS object, we switch it over to the toggle reference system once again.

In theory this should help, because toggle references cause the tardy sweep problem, so if fewer objects use toggle references, there should be fewer objects collected tardily. However, this didn’t solve the problem, because especially in GNOME Shell, most JS objects have some state on them. And, sadly, it made the toggle reference code even more complicated than it already was.

The Big Hammer

The second optimization Georges implemented was the affectionately nicknamed “Big Hammer”. It checks if any GObjects toggled down during a garbage collector pass, and if so, restart the garbage collector a few seconds after. This made CPU performance worse, but would at least make sure that all unused objects were deleted from memory within a reasonable time frame (under a minute, rather than a day.)

Combined with some other memory optimizations, this made GNOME 3.30 quite a lot less memory hungry than its predecessors.

An afternoon at Mozilla

Earlier this year, I had been talking on IRC to Ted Campbell and Steve Fink on the SpiderMonkey team at Mozilla for a while, about various ins and outs of being an external (i.e. not Firefox) user of SpiderMonkey’s JS engine API. Early September I found myself in Toronto, where Ted Campbell is based out of, and I paid a visit to the Mozilla office one afternoon.

I had lunch with Ted and Kannan Vijayan of the SpiderMonkey team where we discussed the current status of external SpiderMonkey API users. Afterwards, we made the plans which eventually became this GitHub repository of examples and best practices for using the SpiderMonkey JS engine outside of Firefox. We have both documentation and code examples there, and more on the way. This is still in progress, but it should be the beginning of a good resource for embedding the JS engine, and the end of all those out-of-date pages on MDN!

I also learned some good practices that I can put to use in GJS. For example, we should avoid using JS::PersistentRooted except as a last resort, because it roots objects by putting them in a giant linked list, which is then traced during garbage collection. It’s often possible to store the objects more efficiently than that, and trace them from some other object, or the context.

Ending the tardy sweeps

In the second half of the afternoon we talked about some of the problems that I had with SpiderMonkey that were specific to GJS. Of course, the tardy sweep problem was one of them.

For advice on that, Ted introduced me to Nika Layzell, an engineer on the Gecko team. We looked at the XPCOM cycle collector and I was surprised to learn that Gecko uses a scheme similar to toggle references for some things. However, rather than GJS sticking with toggle references, she suggested a solution that had the advantage of being much simpler.

In “Avoiding toggle references” above, I mentioned that the only thing standing in the way of removing toggle references, is custom state on the JS objects. If there is custom state, the objects can’t be destroyed and recreated as needed. In Gecko, custom state properties on DOM objects are called “expandos” or “expando properties” and are troublesome in a similar way that they are in GJS’s toggle references.

Nika’s solution is to separate the JS object from the expandos, putting the expandos on a separate JS object which has a different lifetime from the JS object that represents the GObject in the JS code. We can then make the outer JS objects into JS Proxies so that when you get or set an expando property on the JS object, it delegates transparently to the expando object.

Kind of like this:

In the “before” diagram, there is a reference cycle which we have to solve with toggle references, and in the “after” diagram, there is no reference cycle, so everything can simply be taken care of by the garbage collector.

In cases where an object doesn’t have any expando properties set on it, we don’t even need to have an expando object at all. It can be created on demand, just like the JS object. It’s also important to note that the expando objects can never be accessed directly from JS code; the GObject is the sole conduit by which they can be accessed.

Recasting our GUI from the beginning of the post with a tree of GUI elements where the top-level window has an expando property pointing to the bottom-level button, and where the window was just closed, gives us this:

Most of these GObjects don’t even need to have expando objects, or JS objects!

At first glance this might seem to be garbage-collectable all at once, but we have to remember that GObjects aren’t integrated with the garbage collector, because they can’t be traced, they can only have their reference counts decremented. And the JS engine doesn’t allow you to make new garbage in the middle of a garbage collector sweep. So a naive implementation would have to collect this in two passes, leaving the window’s expando object and the button for the second pass:

This would require an extra garbage collector pass for every expando property that referred to another GObject via its JS object. Still a lot better than the previous situation, but it would be nice if we could collect the whole thing at once.

We can’t walk the whole tree of GObjects in the garbage collector’s marking phase; remember, GObject references are nondirectional, so there’s no generic way to ask a GObject which other GObjects it references. What we can do is partially integrate with the marking phase so that when a GObject has only one reference left, we make it so that the JS object traces the expando object directly, instead of the GObject rooting the expando object. Think of it as a “toggle reference lite”. This would solve the above case, but there are still some more corner cases that would require more than one garbage collection pass. I’m still thinking about how best to solve this.

What’s next

All together, this should make the horrible toggle reference code in GJS a lot simpler, and improve performance as well.

I started writing the code for this last weekend. If you would like to help, please get in touch with me. You can help by writing code, figuring out the corner cases, or testing the code by running GNOME Shell with the branch of GJS where this is being implemented. Follow along at issue #217.

Additionally, since I am in Toronto again, I’ll be visiting the Mozilla office again this week, and hopefully more good things will come out of that!

Acknowledgements

Thanks to Ted Campbell, Nika Layzell, and Kannan Vijayan of Mozilla for making me feel welcome at Mozilla Toronto, and taking some time out of their workday to talk to me; and thanks to my employer Endless for letting me take some time out of my workday to go there.

Thank you to Ted Campbell and Georges Stavracas for reading and commenting on a draft version of this post.

The diagrams in this post were made with svgbob, a nifty tool; hat tip to Federico Mena Quintero.

The Parable of the Code Review

Last week’s events, with Linus Torvalds pledging to stop behaving like an asshole, instituting a code of conduct in Linux kernel development, and all but running off to join a monastery, have made a lot of waves. The last bastion of meritocracy has fallen! Linus, the man with five middle fingers on each hand, was going to save free software from ruin by tellin’ it like it is to all those writers of bad patches. Now he has gone over to the Dark Side, etc., etc.

There is one thing that struck me when reading the arguments last week, that I never realized before (as I guess I tend to avoid reading this type of material): the folks who argue against, are convinced that the inevitable end result of respectful behaviour is a weakening of technical skill in free software. I’ve read from many sources last week the “meritocracy or bust” argument that meritocracy means three things: the acceptance of patches on no other grounds than technical excellence, the promotion of no other than technically excellent people to maintainer positions within projects, and finally the freedom to disrespect people who are not technically excellent. As I understand these people’s arguments, the meritocracy system works, so removing any of these three pillars is therefore bound to produce worse results than meritocracy. Some go so far as to say that treating people respectfully, would mean taking technically excellent maintainers and replacing them with less proficient people chosen for how nice1 they are.

I never considered the motivations that way; maybe I didn’t give much thought to why on earth someone would argue in favour of behaving like an asshole. But it reminded me of a culture shift that happened a number of years ago, and that’s what this post is about.

Back in the bad old days…

It used to be that we didn’t have any code review in the free software world.

Well, of course we have always had code review; you would post patches to something like Bugzilla or a mailing list and the maintainer would review them and commit them, ask for a revision, or reject them (or, if the maintainer was Linus Torvalds, reject them and tell you to kill yourself.)

But maintainers just wrote patches and committed them, and didn’t have to review them! They were maintainers because we trusted them absolutely to write bug-free code, right?2 Sure, it may be that maintainers committed patches with mistakes sometimes, but those could hardly have been avoided. If you made avoidable mistakes in your patches, you didn’t get to be a maintainer, or if you did somehow get to be a maintainer then you were a bad one and you would probably run your project into the ground.

Somewhere along the line we got this idea that every patch should be reviewed, even if it was written by a maintainer. The reason is not because we want to enable maintainers who make mistakes all the time! Rather, because we recognize that even the most excellent maintainers do make mistakes, it’s just part of being human. And even if your patch doesn’t have a mistake, another pair of eyes can sometimes help you take it to the next level of elegance.

Some people complained: it’s bureaucratic! it’s for Agile weenies! really excellent developers will not tolerate it and will leave! etc. Some even still believe this. But even our tools have evolved over time to expect code review — you could argue that the foundational premise of the GitHub UI is code review! — and the perspective has shifted in our community so that code review is now a best practice, and what do you know, our code has gotten better, not worse. Maintainers who can’t handle having their code reviewed by others are rare these days.

By the way, it may not seem like such a big deal now that it’s been around for a while, but code review can be really threatening if you aren’t used to it. It’s not easy to watch your work be critiqued, and it brings out a fight-or-flight response in the best of us, until it becomes part of our routine. Even Albert Einstein famously wrote scornfully to a journal editor after a reviewer had pointed out a mistake in his paper, that he had sent the manuscript for publication, not for review.

And now imagine a future where we could say…

It used to be that we treated each other like crap in the free software world.

Well, of course we didn’t always treat each other like crap; you would submit patches and sometimes they would be gratefully accepted, but other times Linus Torvalds would tell you to kill yourself.

But maintainers did it all in the name of technical excellence! They were maintainers because we trusted them absolutely to be objective, right? Sure, it may be that patches by people who didn’t fit the “programmer” stereotype were flamed more often, and it may be that people got sick of the disrespect and left free software entirely, but the maintainers were purely objectively looking at technical excellence. If you weren’t purely objective, you didn’t get to be a maintainer, or if you somehow did get to be a maintainer then you were a bad one and you would probably run your project into the ground.

Somewhere along the line we got this idea that contributors should be treated with respect and not driven away from projects, even if the maintainer didn’t agree with their patches. The reason is not because we want to force maintainers to be less objective about technical excellence! Rather, because we recognize that even the most objective maintainers do suffer from biases, it’s just part of being human. And even if someone’s patch is objectively bad, treating them nonetheless with respect can help ensure they will stick around, contribute their perspectives which may be different from yours, and rise to a maintainer’s level of competence in the future.

Some people complained: it’s dishonest! it’s for politically correct weenies! really excellent developers will not tolerate it and will leave! etc. Some even still believe this. But the perspective has shifted in our community so that respect is now a best practice, and what do you know, our code (and our communities) have gotten better, not worse. Maintainers who can’t handle treating people respectfully are rare these days.

By the way, it may not seem like such a big deal now that it’s been around for a while, but confronting and acknowledging your own biases can be really threatening if you aren’t used to it… I think by now you get the idea.

Conclusion, and a note for the choir

I generally try not to preach to the choir anymore, and leave that instead to others. So if you are in the choir, you are not the audience for this post. I’m hoping, possibly vainly, that this actually might convince someone to think differently about meritocracy, and consider this a bug report.

But here’s a small note for us in the choir: I believe we are not doing ourselves any favours by framing respectful behaviour as the opposite of meritocracy, and I think that’s part of why the pro-disrespect camp have such a strong reaction against it. I understand why the jargon developed that way: those driven away by the current, flawed, implementation of meritocracy are understandably sick of hearing about how meritocracy works so well, and the term itself has become a bit poisoned.

If anything, we are simply trying to fix a bug in meritocracy3, so that we get an environment where we really do get the code written by the most technically excellent people, including those who in the current system get driven away by abusive language and behaviour.


[1] To be clear, I strive to be both nice and technically excellent, and the number of times I’ve been forced to make a tradeoff between those two things is literally zero. But that’s really the whole point of this essay

[2] A remnant of these bad old days of absolute trust in maintainers, that still persists in GNOME to this day, is that committer privileges are for the whole GNOME project. I can literally commit anything I like, to any repository in gitlab.gnome.org/GNOME, even repositories that I have no idea what they do, or are written in a programming language that I don’t know!

[3] A point made eloquently by Matthew Garrett several years ago

JavaScript news from GNOME 3.30

JavaScript news from GNOME 3.30

Welcome back to the latest news on GJS, the Javascript engine that powers GNOME Shell, Endless OS, and many GNOME apps.

I haven’t done one of these posts for several versions now, but I think it’s a good tradition to continue. GNOME 3.30 has been released for several weeks now, and while writing this post I just released the first bugfix update, GJS 1.54.1. Here’s what’s new!

If you prefer to watch videos rather than read, see my GUADEC talk on the subject.

JavaScript upgrade!

GJS is based on SpiderMonkey, which is the name of the JavaScript engine from Mozilla Firefox. We now use the version of SpiderMonkey from Firefox 60. (The way it goes is that we upgrade whenever Firefox makes an extended support release (ESR), which happens about once a year.)

This brings a few language improvements: not as many as in 2017 when we zipped through a backlog of four ESRs in one year, but here’s a short list:

  • Asynchronous iterators (for await (... in ...))
  • Rest operator in object destructuring (var {a, b, ...cd} = ...)
  • Spread operator in object literals (obj3 = {...obj1, ...obj2})
  • Anonymous catch (catch {...} instead of catch (e) {...})
  • Promise.prototype.finally()

There are also some removals from the language, of Mozilla-specific extensions that never made it into the web standards.

  • Conditional catch (catch (e if ...))
  • For-each-in loops (for each (... in ...))
  • Legacy lambda syntax (function (x) x * x)
  • Legacy iterator protocol
  • Array and generator comprehensions ([for (x of iterable) expr(x)])

Hopefully you weren’t using any of these, because they will not even parse anymore! I wrote a tool called moz60tool that will scan your source files and hopefully flag any uses of the removed syntax. It’s also available as a shell extension by Andy Holmes.’

person using black blood pressure monitor

Time for your code to get a checkup… Photo by rawpixel.com on Pexels.com

ByteArray

A special note about ByteArray: the SpiderMonkey upgrade made it necessary to rewrite the ByteArray class, since support for intercepting property accesses in C++-native JS objects was removed, and that was what ByteArray used internally to implement expressions like bytearray[5].

The replacement API I think would have made performance worse, and ByteArray is pretty performance critical; so I took the opportunity to replace ByteArray with JavaScript’s built-in Uint8Array. (Uint8Array didn’t exist when GJS was invented.) For this, I implemented a feature in SpiderMonkey that allows you to store a GBytes inside a JavaScript ArrayBuffer object.

The result is not 100% backwards compatible. Some functions now return a Uint8Array object instead of a ByteArray and there’s not really a way around that. The two are not really unifiable; Uint8Array’s length is immutable, for one thing. If you want the old behaviour back, you can call new ByteArray.ByteArray() on the returned Uint8Array and all the rest of your code should work as before. However, the legacy ByteArray will have worse performance than the Uint8Array, so instead you should port your code.

Technical Preview: Async Operations

The subject of Avi Zajac’s summer internship was integrating Promises and async functions with GIO’s asynchronous operations. That is, instead of this,

file.load_contents_async(null, (obj, res) => {
    const [, contentsBytes, etag] = obj.load_contents_finish(res);
    print(ByteArray.toString(contentsBytes));
});

you should be able to do this:

file.load_contents_async(null)
.then((contentsBytes, etag) => print(ByteArray.toString(contentsBytes)));

or this:

const [contentsBytes, etag] = await file.load_contents_async(null);
print(ByteArray.toString(contentsBytes));

If you don’t pass in a callback to the operation, it assumes you want a Promise instead of a callback, and will return one so that you can call .then() on it, or use it in an await expression.

This feature is a technology preview in GNOME 3.30 meaning, you must opt in for each method that you want to use it with. Opt in by executing this code at the startup of your program:

Gio._promisify(classPrototype, asyncMethodName, finishMethodName);

This is made a bit extra complicated for file operations, because Gio.File is actually an interface, not a class, and because of a bug where JS methods on interface prototypes are ignored. We also provide a workaround API for this, which unfortunately only works on local (disk) files. So the call to enable the above load_contents_async() code would look like this:

Gio._promisify(Gio._LocalFilePrototype, 'load_contents_async', 'load_contents_finish');

And, of course, if you are using an older GNOME version than 3.30 but you still want to use this feature, you can just copy the Promisify code into your own program, if the license is suitable. I’ve already been writing some code for Endless Hack in this way and it is so convenient that I never want to go back.

Debugger

At long last, there is a debugger. Run it with gjs -d yourscript.js!

The debugger commands should be familiar if you’ve ever used GDB. It is a bit bare-bones right now; if you want to help improve it, I’ve opened issues #207 and #208 for some improvements that shouldn’t be too hard to do.

The debugger is based on Jorendb, a toy debugger by Jason Orendorff which is included in the SpiderMonkey source repository as an example of the Debugger API.

Performance improvements

We’ve made some good improvements in performance, which should be especially apparent in GNOME Shell. The biggest improvement is the Big Hammer patch by Georges Stavracas, which should stop your GNOME Shell session from holding on to hundreds of megabytes at a time. It’s a mitigation of the Tardy Sweep problem which is explained in detail by Georges here. Unfortunately, it makes a tradeoff of worse CPU usage in exchange for better memory usage. We are still trying to find a more permanent solution. Carlos Garnacho also made some further improvements to this patch during the 3.30 cycle.

The other prominent improvement is better memory usage for GObjects in general. A typical GNOME Shell run contains thousands or maybe ten-thousands of GObjects, so shaving even a few bytes off per object has a noticeable effect. Carlos Garnacho started some work in this direction and I continued it. In the end we went from 128 bytes per GObject to 88 bytes. In both cases there is an extra 32 byte penalty if the object has any state set on it from JavaScript code. With these changes, GNOME Shell uses several tens of megabytes less memory before you even do anything.

I have opened two issues for further investigation, #175 and #176. These are two likely avenues to reduce the memory usage even more, and it would be great if someone were interested to work on them. If they are successful, it’s likely we could get the memory usage down to 56 bytes per GObject, and eliminate the extra 32 byte penalty.

celine-nadon-694931-unsplash.jpg

Eventually we will get to that “well-oiled machine” state… Photo by Celine Nadon on Unsplash

Developer Experience

I keep insisting it’s no coincidence, that as soon as we switched to GitLab we started seeing an uptick in contributors whom we hadn’t seen before. This trend has continued: we merged patches from 22 active contributors to GJS in this cycle, up from 13 last time.

Claudio André landed many improvements to the GitLab CI. For one thing, the program is now built and tested on more platforms and using more compile options. He also spent a lot of effort ensuring that the most common failures will fail quickly, so that developers get feedback quickly.

From my side, the maintainer tasks have gotten a lot simpler with GitLab. When I review a merge request, I can leave the questions of “does it build?” and “are all the semicolons there?” to the CI, and concentrate on the more important questions of “is this a feature we want?” and “is it implemented in the best way?” The thumbs-up votey things on issues and merge requests also provide a bit of an indication of what people would most like to see worked on, although I am not really using these systematically yet.

We have some improvements soon to be deployed to DevDocs, and GJS Guide, a site explaining some of the more basic GJS concepts. Both of these were the subject of Evan Welsh’s summer internship. Evan did a lot of work in upstream DevDocs, porting it from the current unsupported CoffeeScript version to a more modern web development stack, which will hopefully be merged upstream eventually.

mountains nature arrow guide

It’s about time we had a signpost to point the way in GJS. Photo by Jens Johnsson on Pexels.com

We also have an auto formatter for C++ code, so if you contribute code, it’s easier to avoid your branches failing CI due to linter errors. You can set it up so that it will correct your code style every time you commit; there are instructions in the Hacking file. It uses Marco Barisione’s clang-format-hooks. The process isn’t infallible, though: the CI job uses cpplint and the auto formatter uses clang-format, and the two are not 100% compatible.

There are a few miscellaneous nice things that Claudio made. The test coverage report for the master branch is automatically published on every push. And if you want to try out the latest GJS interpreter in a Flatpak, you can manually trigger the “flatpak” CI job and download one.

What’s coming in 3.32

There are a number of efforts already underway in the 3.32 cycle.

ES6 modules should be able to land! This is an often requested feature and John Renner has a mostly-working implementation already. You can follow along on the merge request.

Avi Zajac is working on the full version of the async Promises feature, both the gobject-introspection and GJS parts, which will make it no longer opt-in; Promises will “just work” with all GIO-based async operations.

Also related to async and promises, Florian Müllner is working on a new API that will simplify calling DBus interfaces using some of the new ES6 features we have gained in recent releases.

I hope to land Giovanni Campagna’s old “argument cache” patch set, which looks like it will speed up calls from JS into C by quite a lot. Apparently there is a similar argument cache in PyGObject.

Finally, and this will be the subject of a separate blog post coming soon, I think we have a plausible solution to the Tardy Sweep problem! I’m really excited to write about this, as the solution is really ingenious (I can say that, because I did not think of it myself…)

Contributors

Thanks to everyone who participated to bring GJS to GNOME 3.30: Andy Holmes, Avi Zajac, Carlos Garnacho, Christopher Wheeldon, Claudio André, Cosimo Cecchi, Emmanuele Bassi, Evan Welsh, Florian Müllner, Georges Basile Stavracas Neto, James Cowgill, Jason Hicks, Karen Medina, Ole Jørgen Brønner, pixunil, Seth Woodworth, Simon McVittie, Tomasz Miąsko, and William Barath.

As well, this release incorporated some old patches that people contributed in the past, even up to 10 years ago, that were never merged because they needed some tweaks here or there. Thanks to those people for participating in the past, and I’m glad we were finally able to land your contributions: Giovanni Campagna, Jesus Bermudez Velazquez, Sam Spilsbury, and Tommi Komulainen.

GUADEC 2018 Reminiscences

This year’s GUADEC in Almería, Spain, was over two months ago, and so here is a long overdue post about it. It was so long ago that I might as well call it a reminiscence! This will be a different kind of post than the ones I’ve done in past years, as plenty of other bloggers have already posted summaries about the talks.

Board of Directors

I didn’t even get to see that many talks anyway, as this was my first GUADEC after being elected to the GNOME Foundation board of directors and I found myself doing a lot of running around to complete things. The board has to prepare for a number of meetings including the GNOME Foundation’s Annual General Meeting that’s always held at GUADEC, and so there was plenty of preparation to be done.

So, except for a few sessions, I mainly followed the “hallway track” this year.

It’s an exciting time to be on the board; it’s been in the news recently that the GNOME Foundation has received two substantial donations, and is hiring some new roles. If you want more information and background, Rosanna Yuen, director of operations, explains all about it in this GUADEC talk.

Interns

Somehow I found myself mentoring two interns this summer, Avi Zajac and Evan Welsh, and both of them were able to attend GUADEC. (I co-mentored Evan with my coworker Manuel Quiñones, who unfortunately could not be there.) I had not done a good job introducing them to each other, but they connected with each other and realized that they were both working with me! If you haven’t already, make sure to read Avi’s blog post and Evan’s blog post for their perspectives on how it went. I was glad to have both of them there and really enjoyed meeting up in person.

Both internships have finished up in the meantime. Evan’s website is viewable here, as well as some improvements to DevDocs which I hope to deploy soon. Avi’s project was released as a technical preview in GNOME 3.30 and will be covered in my next blog post.

JavaScript Talk

I gave one scheduled talk, on GJS and JavaScript, and one unscheduled talk, on Endless Code.

I will cover the material from the JavaScript talk in my next post about the new features in GJS, but for now I wanted to post the slides for everyone’s reference. The video of the talk is here.

Endless Hack Talk

I was voted into one of the conference’s open talk slots with a proposal to talk about Endless Code (since then, renamed to Endless Hack). This is a new (well, it was new at the time of the conference) project at Endless. It’s a continuation of this feature which (I didn’t work on, but) my coworkers demoed about two years ago:

The Endless Hack product generalizes this idea to the whole desktop. The idea is that you should be able to tinker with everything, and there’s a narrative that guides you along the way and teaches you programming concepts. It’s aimed at children and young teenagers. Although this product hasn’t been released yet, and although some of the source code is currently open it’s not in a finished or usable state yet, I did want to talk about it at GUADEC because I think the ability to learn by tinkering is an important part of the free software experience and a direct consequence of one of the Four Software Freedoms, and it’s something the GNOME community should be aware of.

We also made a survey asking people about their experience learning how to program, or not learning how to program, and it’s still open, because I did not do a very good job in the talk of publicizing the link! You can fill it out here.

I haven’t dared to watch the video of me talking completely unrehearsed, but you can watch it here if you want.

Unconference

I had high hopes for organizing a GJS unconference session like last year, but after a certain point I was just completely tired out. We did eventually have a GJS session that consisted of people hacking on their favourite thing. Happily, I was able to convince Georges Stavracas to fix a regression that was preventing GNOME Shell from starting. I got a chance to work with Meg Ford on testing with JavaScript, and I also got some work done on the GJS debugger, a new feature in GNOME 3.30. I will talk about all this and more in my next post!

We also used some of the unconference time for a kickoff session for the GNOME Developer Center. Bastian Ilsø is leading this initiative and has a lot of material for you to read on what’s happened in the meantime.

Acknowledgements

I’d like to thank the GNOME Foundation for making it possible for me to attend the conference and the board meetings.

Thank you to my coworker Lisette Silva for convincing me to submit the open talk and giving some last-minute feedback beforehand.