Categories
Mozilla

XPCOMUtils API Addition

We’ve recently added two new API’s on XPCOMUtils that make a common coding pattern simpler to do. It is quite common to have “smart getters” to store a reference to a service that you use in JavaScript. Normally, folks write code that looks something like this:

this.__defineGetter__("_ioService", function() {
  delete this._ioService;
  return this._ioService = Components.classes["@mozilla.org/network/io-service;1"].
                           getService(Components.interfaces.nsIIOService);
});


That gets long and verbose if you have any more than a few of these getters. The new approach looks like this:

XPCOMUtils.defineLazyServiceGetter(this, "_ioService",
                                   "@mozilla.org/network/io-service;1",
                                   "nsIIOService");


The net result is a lot less boilerplate code, which, in my opinion, is a lot nicer to read. I’ve already converted the toolkit code in the Places module over, and the resulting code is much nicer.

Sometimes, however, you want to have a getter for something that is not a service. That’s where the second API comes in. You can pass in a lambda function that will be called when the value is needed for the first time. From then on, the value is cached. Some old code may look something like this:

this.__defineGetter__("_db", function() {
  delete this._db;
  return this._db = Components.classes["@mozilla.org/browser/nav-history-service;1"].
                    getService(Components.interfaces.nsPIPlacesDatabase).
                    DBConnection;
});


With the new API, that code turns into this:

XPCOMUtils.defineLazyGetter(this, "_db", function() {
  return Components.classes["@mozilla.org/browser/nav-history-service;1"].
         getService(Components.interfaces.nsPIPlacesDatabase).
         DBConnection;
});


Again, much nicer looking! If you are interested, the implementation of these methods can be found here.

Categories
Mozilla

New API Added on NetUtil

Did you know about the handy NetUtil object available when you import NetUtil.jsm? I bet you didn’t since it’s fairly new! There’s no MDC page yet, but the API is getting even better. Bug 508902 added a new method on NetUtil to create nsIURI objects. NetUtil will cache the IO Service so the calling site doesn’t have to. As more code starts to use this, fewer objects will have to cache the IO Service themselves.

The API mirrors nsIOService‘s newURI method with one minor improvement; the last two arguments are optional. Most callers in JavaScript tend to pass null for those last two arguments anyway, but when using NetUtil.newURI, they just have to pass the spec which should be easier.

New JavaScript code that needs to create nsIURI objects should start using this method.

Categories
Mozilla

Swing and a Miss

When we landed the asynchronous location bar, some people started to see substantially slower results. This was alarming since it was supposed to end up speeding or staying the same for everyone. After some investigation, we realized that the AutoComplete code was doing something very dumb with asynchronous searches. The problem was that the code would not actually handle the user pressing the enter key until the next set of results came in. Not only did this result in slower processing of the user’s selection, it also meant that weird race conditions would come up such as up opening a new tab, pasting a url in, press enter, and then switch tabs resulting in the reloading of the page that was just switched to. In other words, epic fail all around.

Luckily, the fix was trivial. Now we handle the enter keypress immediately when the user hits it, and not later. Problem solved :)

Categories
Mozilla

Session Restore Now Writes to Disk Off of the Main Thread

Last week I landed bug 485976 which moves the writing and subsequent fsync (or flush on windows) call to a background thread. This should benefit all of our users, especially those with slower hard drives. Paul O’Shannessey has filed another bug that will reduce the amount of disk activity substantially more that will benefit our users even more.

Background

Session restore writes out to disk very frequently – every ten seconds, in fact. This behavior is controllable by the preference browser.sessionstore.interval for those who want to reduce that, but then you run the risk of not having all your data saved if you crash. We really don’t want to reduce that time for our users.

The amount of data that is written out to disk by session restore scales linearly with the number of tabs and windows you have open. The more you have, the more data has to be written out to disk, and the longer it is going to take.

As we learned in the past with Places, writing to disk and calling fsync can be painfully slow. In session restore code, we are doing this very often and on the main thread. Clearly, this is a bad thing.

Process and Solution

This section is a bit technical, so feel free to skip it. The short answer is “do not block the main thread while writing and flushing data to the hard drive.”

We wanted to address this problem as much as we could for Firefox 3.6. In order to actually reduce the number of writes and fsync calls, we would have to heavily modify how session restore manages and writes its data. That is a big change that we were not comfortable doing this late in the 3.6 cycle. On top of that, we do not really have the manpower to do that change since the people who know that code well are working on other performance improvements for this release. The simple solution for now then is to move our write and fsync calls off of the main thread.

Luckily, Boris Zbarsky had recently written a new API for JS consumers to asynchronously copy an input stream to an output stream. This API would work great for session restore! We had to fix one minor issue with the underlying code not properly handling nsISafeOutputStreams (which make sure we fsync properly), but once that was done, the fix was incredibly simple.

Categories
Mozilla

Asynchronous Location Bar has Landed

About two weeks ago the asynchronous location bar work landed in mozilla-central without much issue. It’s also in the Firefox 3.6 alpha we just recently released. This has the potential to impact all of our users, but those on slower hard drives will notice this the most. Your location bar searches may not complete any faster than before, but they certainly won’t be hanging your browser and locking up the UI.

Background

We’ve been getting reports for some time about the location bar hanging the application for some users when they are typing in it. This wasn’t a problem that was reproducible on every machine, and even on machines that saw it, it wasn’t always 100% reproducible. Clearly, this behavior is not desirable, so we set out to fix it.

I had a theory to the cause almost a year ago and filed a bug that I was hoping we could work on and fix for Firefox 3.5. We knew that reading data off a disk can be slow (and certainly would complete in a non-deterministic amount of time). Since SQLite uses blocking read calls (no more code can execute until the data is read from disk), this could certainly be the cause of the slowdown our users were seeing. Some simple profiling showed that this was largely the cause of the hanging. Work began on the project, but it was clear that enough issues were cropping up that we were not going to be able to safely take this change for Firefox 3.5, and resources were diverted elsewhere.

Process and Solution

This section is a bit technical, so feel free to skip it. The short answer is “do not block the main thread while reading from the hard drive.”

In order to not block the main thread while reading from disk we either need to make SQLite use non-blocking read system calls, or call into SQLite off of the main thread. Changing the SQLite code isn’t something we want to do, so that solution was out of the question. Luckily, we had solved a similar problem with writes and fsyncs earlier in the Firefox 3.5 development with the asynchronous Storage API.

The first implementation that we tried essentially did the same thing that the old code did. We would execute a query, but this time asynchronously, and then process the results and see if they match. There were two issues with this approach, however. The first issue was that we were filtering every history and bookmark entry on the main thread for a given search. That could be a lot of work we end up doing, and with the additional overhead of moving data across threads, the common case would see no win. The second issue was that once we selected a result in the location bar, and a search was not yet complete, there would be a hang as the main thread processed a bunch of events that Storage had posted to it containing results.

At this point, we realized we needed to do the filtering on a thread other than the main thread. After some thought, we was figured that the easiest way to do that would be to use a SQL function that we define in the WHERE clause of our autocomplete queries. This way, all the filtering is done on a background thread, and the code that runs on the main thread only deals with results we will actually use. This solution exposed some things in the Storage backend like lock contention and a few other subtle issues, but nothing major came up.

For more details on how the location bar search results are generated, see my explanation here.

If you weren’t having a problem before, chances are you won’t notice any difference at all.