Programming Servo: the bird’s-eyes view

Gregory Terzian
Programming Servo
Published in
6 min readJul 11, 2020

Having recently discovered something called “a design application”(it’s really amazing, a whole new world opens-up to you), the time seems appropriate to provide a high-level overview of Servo,“Ze system”.

There goes nothing:

Ok that’s it, hope it’s clear and just message me if you have any question. Just kidding!

So first of all, this is a massive simplification, and yet it’s already complicated as it is. Some omissions:

  1. WebGL
  2. Servo-media
  3. Webxr
  4. Devtools
  5. WebDriver
  6. Stylo
  7. WebGPU
  8. WebRender
  9. And more…

However, I do think the above gives a pretty good sense of the structure, namely that:

  1. There is a kind of “main process”
  2. There are a bunch of “other processes”, visibly a kind of “content process”, and a “Service-worker process”.
  3. The “main process” appears unique, while the other types can be more numerous(conveyed via the “there are a few others and they’re getting smaller and smaller effect”).

We can zoom-in to each one, starting with the main process.

The main process

So, what’s worth noting here?

Clearly, the “Constellation” is something pretty central to the engine.

Next, it’s also worth noting that few things from outside of this process seems to have a direct link with anything, except the “IPC Router”. And that’s correct: messages from other processes will be received on that router thread, and then, well, routed mainly to the constellation.

What’s the purpose of the “IPC Router”? Well, first of all, it allows select! on a mix of IPC and “normal” threaded channel, by re-routing the IPC messages over to a threaded channel(from Crossbeam).

Secondly, it also allows for an operation on any thread in the process, to register a kind of “cross-process callback”, via a dedicated route on the IPC Router(see for example this, or this).

What’s a compositor? That’s actually a pretty cool part of modern(only since about 2008) web engines, Just Google It. The “embedder” should speak for itself(it’s actually too complicated to cover in detail here, sadly).

The “Resource thread” is basically the place where all the I/O of the engine takes place, both network- and file-related. As you can see, it internally owns a thread-pool for just that purpose. Also note that this one can receive message from other processes directly, bypassing the IPC router thread, so yeah, that’s possible too.

Also, note that each “oval” in the diagram, is basically a thread, usually one running an event-loop.

Finally, the “constellation” is quite simply the central piece of state of the entire engine, a.k.a the “User Agent”. So for example, when the HTML Living Standard reads that “A user agent holds a browsing context group set”, in Servo it means “the constellation holds a…”.

The constellation is very useful for any workflow involving several components, when they might not have a direct link to each other, and/or when you need some state to be stored in some sort of “third-party”.

Also, in case it wasn’t clear already , an “arrow” in the diagram represents a channel. If the arrow is somewhat thicker and it crosses a process boundary, you’re talking IPC, otherwise it’s a threaded channel.

The content process

The “content-process” is really the “meat” of the engine, the place where an actual “web page” will end-up running. Why is it isolated in a separate process? Partly for increased robustness(a page can crash without taking down the entire engine with it), increased security(sandboxing and so on), and finally: Spectre(although that one seems to have dropped off the charts almost as fast as the eponymous movie, but a sequel is never too far off).

Again, we see the IPC router thread, and yes, that’s the same concept as in the “main process”.

The way to think of a “content-process”, is basically like an open “tab” in your browser(although it can actually contain several tabs, see certain cases of using window.open ).

The “script-thread”, quite visually, takes a central place within one such process. You can think of it as the “main-thread” of that process. It will “run” one of several “web-pages”, if they belong to the same “browsing context group” and their origin is “similar enough”. Think of a web-page, containing a bunch of same-origin iframe, for example. Formally, it is an implementation of a window-event-loop.

Such a “web-page or iframe” in Servo is known as a “pipeline”. For each pipeline, the content-process also spawns a “layout-thread”, and all those “layout-threads” share a thread-pool for certain operations that can potentially be parallelized(or at least it used to, it’s changing and I’m not following those changes closely).

I show a “pipeline” as a kind of block, because those aren’t actually threads, rather they are some state owned by the same script-thread. Since they are kind of key to what the content-process does, I chose to highlight that piece of state independently from the thread owning it.

Does Servo need one “layout-thread” per pipeline? It’s a good question. I think there was an initial idea that layout could, partially at least, run “in-parallel” to a script-thread, however in the end I think the script-thread actually always blocks on layout completing. That’s separate from the opportunity to parallelize some parts of the layout algorithm, using the thread-pool. So perhaps those threads could be consolidated into the script-thread, while retaining the thread-pool to parallelize some of their operations.

As can also be seen, a script-thread can further spawn one or several workers, which can all spawns workers as well, forming a kind of graph of “dedicated workers”.

The Service-worker process

This one is still very much a work in progress, and it’s worth highlighting it in the architecture since it’s also a component “running Javascript”, however it’s not in the same process as the “script-thread”, unlike dedicated workers. For a further explanation, see this.

Again, it’s a separate process, and it’s “central piece” is the “Service worker manager”, a thread somewhat similar to a “script-thread”, except that it manages “service workers”, as opposed to “web-pages or iframes”.

Actually, as can be seen, one thread is spawned per worker(although they probably could all run on a single thread, it’s still a work-in-progress).

The “manager” is the only thing with a direct link to other processes, mainly the constellation. There is no “IPC Router” yet, although there probably should be one. The thing is that those “workers” are not quite running just yet, and once they do they will almost certainly use the “IPC Router”.

The basic idea is that, similar to a “content-process”, the “service worker process” is used to group together “service workers” that can run in the same process, based on their origin.

Why not just run them inside a “content-process”? That’s exactly the kind of stuff explained over there.

That’s it.

So here we are. Perhaps we can take another look at the thing as a whole, armed with our new-found knowledge?

And yes, the “smaller” ones that look like the processes we looked at, are meant to convey a sense of “there can be more than one”, but the arrows have been omitted from those to keep a semblance of readability.

So, now you are totally ready to jump-in and start fixing stuff.

--

--

Gregory Terzian
Programming Servo

I write in .js, .py, .rs, .tla, and English. Always for people to read