Programming Servo: A generic “worker event-loop”

Gregory Terzian
Sep 2, 2018 · 4 min read

Let’s take a look at how in Rust you can have an algorithm generic over T, where T is further bound by a trait, which itself is generic over one of several parameters, as used in the Servo codebase.

A little introduction to workers and their event-loops in the web

The HTML living standard tells us the following with regards to the event-loops of an user agent:

To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section. There are two kinds of event loops: those for browsing contexts, and those for workers.


A bit more on the worker event-loops:

Each WorkerGlobalScope object has a distinct event loop, separate from those used by units of related similar-origin browsing contexts. This event loop has no associated browsing context, and its task queues only have events, callbacks, and networking activity as tasks. These event loops are created by the run a worker algorithm.

Furthermore, there are several kinds of workers:

There are two kinds of workers; dedicated workers, and shared workers.

And the W3c also defines a “service worker.”

Lastly, the living standard also writes that “The global scope is the “inside” of a worker”.

Workers in Servo

As a result of the above, Servo ended-up with several worker “global scopes”, each with their own “run the event-loop” algorithm. While working on a related issue, I realized that I was often making the same changes in both of those worker event-loops, which was error prone. So I set to out to turn that code into one “generic worker event-loop”.

The ‘run_event_loop’ of the ServiceWorkerGlobalScope at
The ‘run_event_loop’ of the DedicatedWorkerGlobalScope at

This proved harder then I though, because what I needed was a run_worker_event_loop<T> sort of algorithm, where T was a type of “Worker Globalscope” and a replacement for &self. But since &self was previously used to access some capabilities of the “Worker Globalscope”, I would need T to implement a trait giving run_worker_event_loop access to these capabilities of T .

Yet those capabilities themselves required to be generic because each event-loop consisted of handling different messages on different channels, and then handling those messages as different type of event-loop specific events.

Furthermore, those run_event_loop differed in some ways, for example because the “dedicated worker global scope” takes this worker argument, and handles it as part of each iteration of the event-loop, whereas the “service worker global scope” lacks that concept.

The different stuff inside each type of “worker globalscope”

At first, I tried to have some sort of “double generic” setup looking like run_worker_event_loop<T<TimerMsg, WorkerMsg, Event>> , where I wanted to express something like run_worker_event_loop is generic over T, which itself is generic over <TimerMsg, WorkerMsg, Event> , and the Rust compiler didn’t seem to appreciate that approach a lot.

In the end, the solution came in the form of a run_worker_event_loop<T, TimerMsg, WorkerMsg, Event>, where TimerMsg, WorkerMsg, Eventare arguments passed on to the underlying trait bound on T: WorkerEventLoopMethods<TimerMsg = TimerMsg, WorkerMsg = WorkerMsg, Event = Event>. Let’s take a look into what that exactly means…

A trait

So our “worker global scope” T, will have to implement WorkerEventLoopMethods .

The trait itself is generic with regards to three “associated typesTimerMsg, WorkerMsg, Event .

This gives the generic event-loop a few ‘hooks’ to access the specific capabilities of each “worker global scope”, which are needed for the parts of the event-loop that are specific to the “worker global scope” at hand.

Those ‘hooks’ themselves require a few generic parameters, because each “worker global scope” receives different types of messages, and expects different types of events resulting from those.

Here is what an implementation looks like:

As you can see, all that the implementation does is “make available” that which is specific to the DedicatedWorkerGlobalScope , it doesn’t contain any event-loop logic, just the stuff that the generic event-loop will need to specific it’s behavior for a given “worker global scope”.

A generic worker event-loop

At the end of the day, we get a generic event-loop logic, using WorkerEventLoopMethods for the parts that are worker-specific.

Through our use of associated items on the trait, we’re able to express the fact that T is a struct implementing a generic trait, in a way that the compiler appreciates…

Programming Servo

Adventures in contributions to Servo, the parallel browser…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store