LWN.net Logo

The Cairo operating system

The Cairo operating system

Posted Jul 4, 2006 20:57 UTC (Tue) by evgeny (guest, #774)
In reply to: The Cairo operating system by tetromino
Parent article: Cairo release 1.2.0 now available

> * libXrender (you alredy use that, otherwise your fonts will look ugly)

Nope. I was going to use Cairo to build nice-looking charts for my mamba-nyamba CGI script. No X stuff on the web server. Shall I continue?

PS. I'm not the poster you replied to, but very much share his attitude. It's a shame to not use dlopen() and friends today.


(Log in to post comments)

The Cairo operating system

Posted Jul 5, 2006 9:44 UTC (Wed) by nix (subscriber, #2304) [Link]

dlopen()ed shared libraries
- can't be prelinked
- don't benefit from ld.so.cache
- have to be explicitly located by the application
- don't necessarily have consistent error messages if they can't be found
(e.g. if mesa can't find the DRI libraries because you need to hack a
#define deep in the source to get it to look in the right place with
modular X, there's no message; it falls back to unusuably slow software
rendering...)
- have... interesting effects if you dlopen() a library that is already
named in a DT_NEEDED
and much else.

They're useful, but they're definitely not a panacea, and in general I prefer DT_NEEDED for stuff that isn't strictly optional or totally plugin (by 'totally plugin' I mean something like KParts, where even the name of the library to load is unknown until runtime).

The Cairo operating system

Posted Jul 5, 2006 10:42 UTC (Wed) by evgeny (guest, #774) [Link]

> - can't be prelinked

Well, yes, this is the purpose.

> - don't benefit from ld.so.cache
> - have to be explicitly located by the application

Why? From the dlopen man page, dlopen("libm.so", RTLD_LAZY); works fine without specifying explicit path.

But actually I didn't mean a mere replacement of the compile time linking with dlopen(), but rather a plugin approach - which is usually (but not always) done using dlopen() on a plugin which in turn is linked to all necessary for this specific plugin .so libs.

The Cairo operating system

Posted Jul 5, 2006 13:51 UTC (Wed) by farnz (guest, #17727) [Link]

> - can't be prelinked Well, yes, this is the purpose.
Why exactly do you want to prevent prelink(8) from working? All it does is move the cost of doing relocations to the time at which prelink is run, not runtime. By using dlopen(3), you prevent prelink from determining the relocations itself, and force the dynamic linker to recalculate the relocation values every time the library is used. For a long running process, this is no big deal (it's a once per process run cost). For desktop processes, where the user is sitting waiting for the application, any delay in startup is noticeable.

The Cairo operating system

Posted Jul 5, 2006 14:13 UTC (Wed) by evgeny (guest, #774) [Link]

> Why exactly do you want to prevent prelink(8) from working?

I want the cairo library (and hence my app) to not be prelinked against anything which is strictly necessary for the 2D rendering core itself.

> By using dlopen(3), you prevent prelink from determining the relocations itself, and force the dynamic linker to recalculate the relocation values every time the library is used.

The time needed for the dynamic linker to recalculate the relocation of a plugin (typically containing only a couple of functions) is negligible comparing to the rest of initialization of any GUI app. The rest (actual libraries doing the hard work) can be prelink'ed to the plugin object.

The Cairo operating system

Posted Jul 5, 2006 14:16 UTC (Wed) by evgeny (guest, #774) [Link]

Oops. s/which is strictly/which is NOT strictly/

The Cairo operating system

Posted Jul 5, 2006 14:43 UTC (Wed) by farnz (guest, #17727) [Link]

Your application should only be prelinked against the backends if they're installed, as your application should only be linked against the 2D rendering engine (cairo), and not against the backends. The 2D rendering engine is linked against the installed backends; when prelink does the work needed to guarantee that the runtime linker does not need to do any relocations, it'll pick up the backends.

Why does it bother you that I've done the caculations to determine how to do the relocations in advance? If you never use the DirectFB or X11 backends, that data is simply ignored, as the backend is never called, so the relocation never takes place, and the backend library is never loaded into memory.

If you're complaining because your choice of distribution doesn't provide you with some mechanism to only have the backends you need installed, bitch at your distribution, not the authors, who provide you with compile-time switches to disable the backends you don't want.

For some classes of applications, notably C++ applications, the relocation time is significant. Plus, you're ignoring the effects of hot caches; when an executable is fully prelinked, all the code pages, including the GOT and PLT, are never dirtied, so they are shared with all other executables using that library; when you've not got much RAM, not having to keep a private copy of these pages helps. This is especially true on a desktop, where the chances are high that another application is using the library, and thus has pulled the pages you'll need into memory already.

In short, you seem to be advocating that cairo drops a perfectly reasonable compile-time method for controlling which backends are installed, and goes to a runtime mechanism with some disadvantages for desktop applications, just because your distro doesn't provide a decent selection of packages without the backends you don't need. I'd like you to justify that position, or correct my misunderstanding of what you're after.

The Cairo operating system

Posted Jul 5, 2006 15:28 UTC (Wed) by evgeny (guest, #774) [Link]

> If you're complaining because your choice of distribution doesn't provide you with some mechanism to only have the backends you need installed, bitch at your distribution, not the authors, who provide you with compile-time switches to disable the backends you don't want.

I'm complaining NOT as an end-user, but as a developer. My application does NOT need X/FB/...; it's a server-level utility. As such, when I consider the use of Cairo for 2D-rendering, I ask myself: what's the chance that potential users of my app will have Cairo installed on their servers? Right now, the answer is "hardly if at all", since for most mainstream binary distros, Cairo will require X/gtk/what not, probably entire Gnome libs, so who in a sane mind would install all this cruft on e.g. a web server?! Thus, the stone-age inflexibility of Cairo precludes me from using it.

> For some classes of applications, notably C++ applications, the relocation time is significant.

Cairo is not C++, and so isn't my app.

> you seem to be advocating that cairo drops a perfectly reasonable compile-time method for controlling which backends are installed,

Huh? You call it a perfectly reasonable method? Then why don't you advocate for returning to pre-1.0 linux version when drivers were built monolithically in the kernel? or just try persuading Firefox or Gimp users and developers that compile-time selection of extensions/plugins is very reasonable, if not perfect? I bet you'd be laughed at.

> and goes to a runtime mechanism with some disadvantages for desktop applications

Do you have hard figures proving this claim? I'd expect we're talking about some milliseconds or even less, that's all.

The Cairo operating system

Posted Jul 5, 2006 16:03 UTC (Wed) by farnz (guest, #17727) [Link]

So, by the same token, I assume you avoid all libraries, in case a distribution is fool enough to build versions that depend on libraries your users won't install? If Cairo provides enough benefits to you, write to it; at the moment, you're saying that you might want to use Cairo in a situation the Cairo developers aren't designing for, but are scared to because if distributions don't provide a server-suited Cairo build, and if Cairo turns out to be the best way for you to do something, you don't think anyone (Cairo developers or distribution builders) will flex to accomodate you.

You seem to advocate changing everything at runtime or startup time; why can't I choose a different VM at startup time? Why can't Firefox drop the XUL handler, if I only need a HTML renderer without a UI? Why can't GIMP run without GTK+, if I'm only using it from the command line? If I'm not planning to use text, why does GIMP pull in FreeType and Pango?

My point is that the mechanism works for the currently intended uses of Cairo, and provides a small, but measurable benefit for them in terms of memory usage as well as time in their choice of application. It also makes their code considerably simpler to write in the first place (and yes, I've written code in both styles - dlopen is slightly harder to get right, and harder to debug if you get it wrong). You are asking them to drop that gain, in order to get into a class of applications that they're not targetting.

If using Cairo makes your application simpler, use it. If the result is that Cairo needs better backend selection (so that your users don't have to install any X11 libraries), that will happen. In the meantime, you're asking them to drop a small gain from doing things the way they've done them, for a potential future benefit.

The Cairo operating system

Posted Jul 5, 2006 16:58 UTC (Wed) by jimmybgood (guest, #26142) [Link]

> You seem to advocate changing everything at runtime or startup time; why can't I choose a different VM at startup time? Why can't Firefox drop the XUL handler, if I only need a HTML renderer without a UI? Why can't GIMP run without GTK+, if I'm only using it from the command line? If I'm not planning to use text, why does GIMP pull in FreeType and Pango?

Those possibilities are very appealing. It probably would sacrifice some performance for people who have powerful computers and want all the features anyway. But it would make it much easier for less-advantaged people the world around to install reduced feature set software on legacy computers.

Cairo is not like a new RSS aggregator that we can just delete from our computer, if we feel uncomortable with it. Gtk2 is central to the desktop. I think it's reasonable to ask the developers to consider the needs of all users, not just the power users and to anticipate the potential misuses and abuses of package managers and application writers.

On the other hand, it's still possible to have a desktop without the bloated, insecure monstrosities that QT and now GTK2 have become.

I see several comments that say to the effect, X and OpenGl do something so it's OK that Cairo does it. Let me repeat, it's easy to keep OpenGL on a tight leash and turn it off completely. As for X, it's a very old application that was written in a different era. If experienced developers were to rewrite it from scratch, if they wanted to they could make it much more controllable, stable and secure.

The Cairo operating system

Posted Jul 5, 2006 20:26 UTC (Wed) by evgeny (guest, #774) [Link]

> So, by the same token, I assume you avoid all libraries, in case a distribution is fool enough to build versions that depend on libraries your users won't install?

I avoid all libraries that invariably chain with themselves absolutely different (and optional to their core functionality) API layers.

> You seem to advocate changing everything at runtime or startup time;

I advocate for maximal flexibility (as far as it hasn't noticeable negative side effects - and in this case, it doesn't).

> why can't I choose a different VM at startup time? Why can't Firefox drop the XUL handler, if I only need a HTML renderer without a UI? Why can't GIMP run without GTK+, if I'm only using it from the command line? If I'm not planning to use text, why does GIMP pull in FreeType and Pango?

1. Not sure what you meant (i.e., in which context) by VM, but the rest would be really great. Why don't you think so?
2. In fact, in the above, "would be" should be replaced by "is". I suggest you do e.g. "ldd /usr/lib/libgimp.so" and verify that the base Gimp functionality is available in a clean (no gtk/X/...) way. This is a sign of a well-thought design.

> My point is that the mechanism works for the currently intended uses of Cairo,

No, it doesn't. The fact is you _can_ build Cairo without the X stuff and other cholesterol additions, so such a use (alongside with others) _was_ intended. Yet, when considering Cairo as a mainstream 2D rendering API (which is obviously what Cairo devs think of it as), it becomes clear that with the current monolithic design this will never be realized except in DIY distros like Gentoo.

> and provides a small, but measurable benefit for them in terms of memory usage as well as time in their choice of application.

Who cares about extra half a millisecond or a few bytes?! And are you serious the choice (i.e., monolithic vs. plugin design) was ever discussed and decided upon based on the performance considerations?

> It also makes their code considerably simpler to write in the first place

OK, now this boils down to the truth. That's the whole reason - it simply has NOT been designed/thought of/coded YET. Adding another mumbo-jumbo backend is cooler than doing the backend separation in a proper way, that's all.

Now, please notice that I'm NOT criticizing the priority list of the Cairo devs. Most of us (myself including) is coding FOSS stuff for fun. Let mumbo-jumbo be first. But please, don't call a bad design by any other name. Perhaps this will help releasing Cairo-2.0 with all the backends as optional plugins.

The Cairo operating system

Posted Jul 5, 2006 21:44 UTC (Wed) by farnz (guest, #17727) [Link]

I'm still not clear what you're after; Cairo has the needed separation between backends and core library internally. They just haven't spent the hours needed to write dlopen() boilerplate code, and their own runtime linking of backends derived from this, especially since the backend interface isn't stabilised, and thus this boilerplate code would need plenty of changes.

You're still arguing that because your distribution of choice combines a lack of flexibility in providing alternative versions of a package with maintainers unwilling to do the extra (manual) work involved in providing those alternatives, all library developers should do the extra (manual) work in working round this deficiency.

Plus, as you've said yourself, who cares about a few bytes? If you never use anything other than the SVG backend for Cairo, the other backends and their associated libraries won't be used in memory at all, and disk is even cheaper than RAM (which you're happy to burn). Indeed, if the dynamic linker is bright enough (and I've never checked this), you could delete the X11 and OpenGL backends, and not even notice that you're missing compile-time dependencies.

If it's security that worries you, I'd just like to point out that you're advocating that the Cairo developers stop making use of the dynamic linker's symbol resolution, and write their own version; there's no guarantee that this code won't be buggy. In addition, bugs in a manually coded subset of a dynamic linker are likely to lead to arbitrary code execution, not to output faults.

Here's a constructive suggestion for you; if the dynamic linker isn't bright enough to cope with missing libraries (despite the fact that they're not used), why don't you motivate someone to fix this? The result should be that your server code (which presumably never touches the DirectFB backend) can link to a version of Cairo that was compiled with all the backends, and never notice that someone didn't install the DirectFB backend. Then, your distribution of choice can compile Cairo with everything turned on, but ship it in a split set of packages with different backends.

The Cairo operating system

Posted Jul 5, 2006 22:35 UTC (Wed) by evgeny (guest, #774) [Link]

> I'm still not clear what you're after; Cairo has the needed separation between backends and core library internally. They just haven't spent the hours needed to write dlopen() boilerplate code

Exactly, that's my point.

> especially since the backend interface isn't stabilised, and thus this boilerplate code would need plenty of changes.

I don't believe it. If the internal API changes, they still need doing practically the same amount of work, be it a direct linking or glued by dlopen. As far as the core and the backends are maintained by the same team, at least - which is the case now.

> You're still arguing that because your distribution of choice combines a lack of flexibility

NO! why don't you read carefully what I've said earlier? _My_ distribution of choice (Gentoo) is flexible enough to accomodate my needs, not to mention I'm pretty much content with compiling from the source tarballs. But this doesn't matter. What I care about are _users_ of my software, which is NOT specific to Gentoo, so if a sysadmin doing e.g. "apt-get install myapp" on a server will see about entire Gnome lib pack selected (due to the deps) plus the kitchen sink, he will (and rightfully so) say "No". That's what I'm afraid of. I don't agree to cut the number of potential users of my app tenfold because of the dumb Cairo architecture.

> Plus, as you've said yourself, who cares about a few bytes?

Now you're comparing apples to the oranges...

> Here's a constructive suggestion for you; if the dynamic linker isn't bright enough to cope with missing libraries (despite the fact that they're not used), why don't you motivate someone to fix this?

Huh? May I have a contr-suggestion for you: write about this idea to LKML or the glibc maillist, or better both. And don't forget letting me know - I want to enjoy the show ;-)

The Cairo operating system

Posted Jul 7, 2006 0:26 UTC (Fri) by nix (subscriber, #2304) [Link]

It's not half a millisecond. It's hundreds of Kb per running instance and 10--20 seconds of extra startup time for applications with large numbers of symbols.

Prelinking *matters*.

(FWIW, Jakub Jelinek and Ulrich Drepper recently designed a new ELF symbol hash table format that should reduce the relocation time of non-prelinked apps by 40% or so: but that's a glibc-2.5 thing and requires apps to be relinked to benefit anyway. Plus it can't reduce the memory hit.)

The Cairo operating system

Posted Jul 7, 2006 10:40 UTC (Fri) by evgeny (guest, #774) [Link]

Oh come on, why on Earth are you continuing to talk about some thought-out-of-the-thin-air situations? Let's focus on Cairo - I'm not suggesting to convert a C++ bloatware to a thousand of plugins! Each Cairo backend (total number < 10) should have a single dlopen'd init entry! Add another one per plugin for maximal flexibility (i.e., the versioning check), but that's all.

The Cairo operating system

Posted Jul 8, 2006 22:54 UTC (Sat) by nix (subscriber, #2304) [Link]

Cairo is *in use* by large C++ applications.

I repeat: prelinking matters.

The Cairo operating system

Posted Jul 9, 2006 17:33 UTC (Sun) by evgeny (guest, #774) [Link]

I repeat: one or two dlopen's per backend in Cairo will be unnoticeable in your favorite C++ bloatware app.

The Cairo operating system

Posted Jul 6, 2006 20:27 UTC (Thu) by oak (guest, #2786) [Link]

You forgot one additional problem with dlopen() which is debugging
things for libraries which have been dlclose()d.

Trying to determine with Valgrind in a large application doing lots
of dlopen()s and dlclose()s from which dlopened library a memory
leak is coming is "interesting". Only way to debug in that situation
is to change the code not to do dlclose() which then can have some
other problems when the libraries are dlopen()d another time...

And yes, the performance and memory usage difference between
prelinked libraries and dlopen()ed libraries is very noticable
if you have a lot of them and especially if they use C++.
For large C++ program/library not being able to do prelinking
can easily waste megabytes of memory per process (if there are
tens of thousands of symbols that need resolving).

The Cairo operating system

Posted Jul 7, 2006 0:30 UTC (Fri) by nix (subscriber, #2304) [Link]

dlclose() also interacts... interestingly with VM randomization.

I found a bug in Subversion (with --enable-dso) a while back which reduces to dlopen(), store a pointer to a function in the shared object in a data structure, dlclose(); later dlopen() again, call through the function pointer, *boom*; there's no guarantee that the dlopen() is at the same address this time, and with VM randomization it's pretty much guaranteed to be different.

Oops.

I think this would likely also kill C++ RTTI through repeatedly-dlopen()ed shared libraries, since that relies on pointer comparisons. The lesson in all this is to avoid dlclose().

The Cairo operating system

Posted Jul 7, 2006 8:28 UTC (Fri) by xoddam (subscriber, #2322) [Link]

> The lesson in all this is to avoid dlclose().

... er ... or at least to invalidate any dlsym()s you have cached when
you dlclose(), as any *sane* developer would, and look them up afresh
if/when you re-dlopen() the library.

The Cairo operating system

Posted Jul 7, 2006 10:15 UTC (Fri) by nix (subscriber, #2304) [Link]

Well, yes, but it can be hard to keep track of such things if you're keeping pointers to some entities which are dlsym()ed from different places and others which are not, in the same data structure.

The Cairo operating system

Posted Jul 7, 2006 10:46 UTC (Fri) by evgeny (guest, #774) [Link]

Why the hell do you need to unregister a backend (except for atexit time)? I know of no plugin-enabled app (Gimp/Firefox/...) that allow for run-time plugin unloading. Even in the kernel support for rmmod is a non-default option.

The Cairo operating system

Posted Jul 7, 2006 19:38 UTC (Fri) by oak (guest, #2786) [Link]

If you need to change the backend. For example when you migrate your
application window from local display (with OpenGL support) to a remote
display. Gtk has already some support for display migration.

For something like in-process panel applets dlclose() & dlopen()
make sense. User might very well want to change the panel applets
at run-time...

The Cairo operating system

Posted Jul 7, 2006 20:01 UTC (Fri) by evgeny (guest, #774) [Link]

Fine, but this still doesn't explain why you insist on closing the first _backend_? What you describe is switching from an _instance_ of a device to an instance of another device. Say, the X11 device, once registered, could allow you to have several Drawables (Pixmap/Window) in the same app - e.g., one for the main window, another one for File/Open... preview, etc, which doesn't mean the X11 device plugin should be registered/unregistered each time. Just as the dynamic loader does symbol resolving only once, similarly, all available/enabled backend plugins need to be initiated only once.

The Cairo operating system

Posted Jul 7, 2006 20:24 UTC (Fri) by oak (guest, #2786) [Link]

> Fine, but this still doesn't explain why you insist on closing
> the first _backend_?

I did not say any such thing. :-)
I was merely commenting on the pros&cons of dlopen() and dlclose().
Whether some piece of code actually uses them, is up to the code...


> Say, the X11 device, once registered, could allow you to have several
> Drawables (Pixmap/Window) in the same app - e.g., one for the main
> window, another one for File/Open...

Note that those are different windows. (X) display migration means
moving all of applications (open) windows from one (e.g. local) X server
to another X server. For example you take a PDA with you to a meeting
and you want to show something you've scribbled with it briefly on the
projector before continuing using the same program. Or you have in your
desktop machine multiple graphics cards each running a separate X server
and you want to migrate some of your apps from one X server to another
*without closing* the apps.

Copyright © 2012, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds