This is a guest post by Alan Griffiths, Software Developer at Canonical. If you would like to contribute a guest post, please contact firstname.lastname@example.org
I’m Alan Griffiths and I’m a software developer. Being a software developer means I deal with a lot of problems that are rarely appreciated by non-developers. This is a story about dealing successfully with one of these problems.
Software developers often talk about “technical debt”. This phrase comes from a metaphor that tries to explain the issue without being “too technical”. I think the term was first used by Ward Cunningham, but I could be wrong.
The metaphor describes the effect of doing things in a way that meets the immediate goals but introduces future costs. For example, using fixed English text in an application can get it working for a demo or even an initial release, but if it needs to work in other languages there will be a lot of changes needed. Not just to text, but to assumptions about layout.
The problem with this metaphor is that other people (in the business the developers work for) are used to working with bank loans and other forms of acceptable debt and don’t realise the true cost of “technical debt” – it is more “payday loan” than “mortgage”.
As a result many software projects struggle with technical debt that adds a significant, hidden cost to the business. I hope a few of them will be inspired by this success to find a solution of their own.
What is Mir?
The project I’m working on is Mir. And it had a form of technical debt.
Mir is a set of libraries providing the facilities for the participants in display management: the servers that manage organising windows on one or more screens and the clients (or applications) that provide the content of the windows.
On the server side Mir has been in use by the Unity8 window manager on phones and tablets and as a preview on the Ubuntu 16.10 desktop.
There are multiple downstream projects using the client side of Mir: there’s Mir support available in GTK, Qt and SDL2. In addition there’s an X11 server that runs on Mir: Xmir, this allows X based applications to run on Mir servers.
Under the hood there are facilities to load plugin modules for different graphics platforms, input platforms and render platforms. At the time of writing the supported platforms are the Mesa and Android graphics stacks. This enables running the server either directly on these drivers or as a client of either Mir itself or of X11.
While having Unity8 use Mir on real devices that ship to real customers has been a big benefit, the close association of the two projects has had some downsides. (Just like the “only English” application mentioned above.)
To explain the problem I need to introduce the terms API and ABI. The API (Application Programming Interface) is used by other programmers to write code that works with Mir and the ABI (Application Binary Interface) is how the code works with Mir when it runs. Changes to the API and ABI can break programs that use Mir. They are considered stable if they only change in ways that allow the existing programs to work.
Because the client side of Mir has multiple projects using it, the importance of a stable API and ABI has been both appreciated and acted upon.
On the server side things haven’t been so disciplined. The API has evolved gradually and the ABI has broken on almost every release.
The slow evolution of the server API has been managed by maintaining a “compatibility branch” of Unity8 and releasing those changes to Unity8 at the same time as every Mir release. In practice, it isn’t just Unity8 that we need to keep in step, there are additional projects (unity-system-compositor and QtMir) involved in this dance, which makes it even more involved and expensive.
Maintaining and releasing these compatibility branches significantly increases the effort involved in releasing Mir. Changes need to be made and tested across a family of projects belonging to separate teams. Because the pain involved increased gradually this didn’t get the attention I felt it deserved. It is actually ridiculous to take several weeks and man-days of effort to release software!
The unstable server API and ABI has another, indirect cost: it makes it impractical for anyone outside of the few Canonical teams we work with to write and maintain a Mir server. Unless someone updates and releases such a server with every Mir release it would be broken by each release.
This problem seemed unlikely to change in the normal course of events. The Mir server API was not designed with ABI stability in mind and the development focus was on delivering more features and not reducing the cost of this technical debt. While some efforts have been made by the team which have reduced the API churn they didn’t affect the underlying issue.
Elevating a system from chaos to order takes energy and conscious effort. And energy was constantly being drained by managing the release process.
Repaying The Debt: MirAL
Canonical has a policy of allowing staff to work on projects they choose for half a day each week (subject to some reasonable conditions). And, having checked with management, I elected to work on providing an alternative, stable API and ABI for writing window managers.
I began by setting up a separate “Mir Abstraction Layer” project (MirAL) and populating it with a copy of the code from the Mir example servers. This immediately identified a series of bugs in the Mir packaging that had gone undetected. Nothing hard to fix, but obstacles to using Mir: headers referenced but not installed, overlooked dependencies on other projects, incorrect pkg-conf files, and so forth. I filed the Mir bugs and fixed them in my “day job”.
I then started separating out the generic window management logic from the specifics in the examples and building an API between them. Thus emerged the three principle interfaces of this library: a “window management policy”, a “basic window manager” into which a policy slotted and “window management tools” which provides functionality.
This meant that the basic window management functionality, like the placement of menus, could be shared between different approaches to window management:
- a “normal” desktop;
- a “tiling” version; and,
- a “kiosk” to support embedded uses
Having got these in place, along with some other meaningful concepts, I started rework to protect against the types of ABI breakage that were all too common with the existing server APIs. There are a lot of things that can break ABIs and APIs. Public data structures and virtual function tables in particular are fragile with respect to changes. One technique I used a lot was the “Cheshire Cat” idiom to avoid these.
I’d got to the point where I’d proved to myself that this approach could work when a new priority arrived in my “day job”. This was to provide window management support for Unity8 on the desktop. Until this point, Unity8 had only had to deal with the “one screen, one active application, one window” context of the phone and then an extension of this to support “sidestage” on the tablet form. And in Mir much of the window management support was example and test code.
What was needed was a place to consolidate the existing window management support and iterate quickly towards a more integrated approach. To support additional projects beyond Unity8 (both from within Canonical or from outside) this also needed to be a place where other shells and desktop environments can leverage this functionality.
It sounded a lot like what I’d started with MirAL. As “the business” could more readily appreciate the value of this functionality than the cost of the technical debt. So MirAL switched from being my hobby to being my “day job” and gained Unity8, a real world shell, as a prospective downstream.
A colleague (Gerry Boland) who had done much of the work integrating Unity8 with Mir and I started working to make it possible to update the QtMir project (which does the integration) to use the new API and exploit the window management support it provides.
We copied the code from the QtMir project into the MirAL source tree and started work on joining things together. This proved very helpful in refining the concepts in the MirAL API, identifying gaps in the functionality it provides and soon gave us confidence that this was a workable approach.
Once the effectiveness of this approach had been established, work was started on joining things up all the way into Unity8 and integrating it with the more sophisticated animations and transitions it implements.
It took a few months to get MirAL added to the archive and to integrate all the changes back into the original QtMir. Recent versions of Unity8 have used this work to get window management working for the desktop.
MirAL for Testing Client Applications
Another piece of work happening around the same time was the effort to get third party applications to work correctly with Mir shells.
The window management work I was doing in MirAL and especially the sample “miral-shell” has become a testing ground for how window management features used by applications interact with the Mir support.
The MirAL shell examples can be installed and run as follows:
$ sudo apt install miral-examples
To aid with testing MirAL comes with a handy script (miral-app) to set up a “miral-shell” running in a desktop window. There’s also a “miral-desktop” script that runs miral-shell as a full desktop.
Debugging Window Management
While working to track down problems in the interaction between toolkits and MirAL’s window management I found the time to introduce an immensely helpful logging facility that logs all calls into the window management policy and all the calls made to the window management tools. This has been essential in discovering why problems we see are happening. It has helped diagnose bugs in MirAL, the window management policies and the toolkit backends. This can be used with any server based on MirAL by adding “‑‑window‑management‑trace” to the command-line:
$ miral-app --window-management-trace
Applications Using Toolkits With Mir Backends
Most applications are not written directly against X-Windows but use “toolkits” that provide higher level concepts and these have requirements on window management (like putting tooltips in the right place).
Two toolkits of immediate significance are GTK+ (on which gnome applications are built) and Qt (which is widely used in Canonical) both of which already have optional Mir “backends”. However, the amount of testing these received had been limited whilst work was focussed on the phone.
These are now being tested with both Unity8 and miral-shell. They can be started directly from a terminal in a “miral-app” session, or from outside using “miral-run ”.
Not all applications use toolkits that support Mir directly and supporting X11 applications by running the Xmir an X-server based on Mir is a bit fiddly. To facilitate testing this approach with MirAL there’s another script “miral-xrun” that finds a free port, starts an X server, runs the application and then closes the X server when the application exits.
The State Of MirAL Now
It is now a year since I started MirAL as an elective project on Launchpad [https://launchpad.net/miral] and it is shipping in Ubuntu Zesty [17.04]! (For earlier releases of Ubuntu see “Note on getting MirAL” below.)
MirAL is also available for Raspberry Pi, DragonBoard and other devices running Ubuntu Core as a snap. The “Mir Kiosk” mentioned here is the miral-kiosk example program: [https://developer.ubuntu.com/en/snappy/guides/mir-snaps/].
For a developer using MirAL it is very easy to create Mir based window managers. The API needed by developers is installed like this:
$ sudo apt install libmiral-dev
As an experiment, a colleague created this (rather silly) shell in under an hour: [https://github.com/BrandonSchaefer/bad-shell].
Mir based window managers do not need to be reworked and rebuilt when Mir (or MirAL) is released. That is the result of MirAL providing a stable ABI.
At the time of writing not all of the Unity8 dependencies on the Mir “server” APIs have yet been replaced by MirAL.The reason is that in addition to the window management functionality that MirAL supports, Unity8 customizes compositing the displays.
This means that we still need to update and release Unity8 when we release Mir. While the size of the changes and the consequent testing has been reduced we are still “paying interest” on the “technical debt” and I still intend to fix that.
Work is continuing to improve the window management capabilities (we recently introduced support for “workspaces”) and to offer the Mir compositing facilities used by QtMir that are not currently supported. These too will be presented in a form suitable for consumption by other projects.
For the latest information visit my “Canonical Voices” blog [http://voices.canonical.com/alan.griffiths/category/miral/]
Note on getting MirAL
Yakkety [16.10] has only version 0.2 of MirAL (the current release is 1.3) and Xenial [16.04LTS] doesn’t have it at all. (If you’re using the Xenial “stable phone overlay” ppa then that has the latest MirAL release, but unless you already know about and use that ppa this isn’t an advisable way to try MirAL.)
If you’re not yet on Zesty or want the latest version of MirAL it can be built from source on Xenial, Yakkety and Zesty by “checking out” the source:
$ bzr branch lp:miral
This creates a “miral” directory with more instructions in ‘miral/getting_and_using_miral.md’.