[Xcb] Xgob

Peter Harris pharris at opentext.com
Fri Jan 28 14:44:30 PST 2011

I've been toying[3] with a port of libxcb[4] to Go[1], and I thought it
was time to write down some notes before I forget everything.

First, I'm serious when I say "toy". I didn't even implement auth
support, so it's useless for real work. That said, it wouldn't take much
to get it into shape.

Second, the Go language itself is still in flux. I wouldn't be surprised
if my toy doesn't even compile six months from now.

With that out of the way, some notes:

The feature I like most about this implementation is that there is
always exactly one thread[2] that can read from the server. That thread
decodes Errors, Events, and Replies, forwarding them to the appropriate
channel. This keeps the read half clean, with no worries about mutexes
or conds or who has to wake up whom.

The use of language "chan"s means that we don't have to actually write
(wait|poll)_for_(reply|event|error), and users of the library can freely
select{} on (reply|event|error) in combination with anything else. The
reply chans are buffered, so discard_reply is a simple matter of
forgetting the chan and letting the garbage collector sweep it up.

Making every single request checked and letting the user uncheck the
cookie is a tempting simplification of the API, but in practise it fails
the "noop" test by at least an order of magnitude. Even forcing the GC
off, there's too much allocation going on.

Asking the user to pass in a channel (or null for the default error
channel) for non-reply requests seems to work well. If the user does
pass in a channel, xgob sends a null to the channel when it can prove
that an error cannot arrive for that request. This way, the user can do
a blocking read for errors if the user wishes. I'm not fully convinced
that sending the null is worth the overhead, but the idea seems reasonable.

I hand-assemble words. I chose MSBFirst because I like stressing the
swap functions in my LSBFirst server. Someone who cares deeply about
performance could use the "unsafe" library to directly map structs onto
buffers. I didn't, because I like safety and I have lots of CPU compared
to network anyway.

xgob/util/atom.go is roughly what I want to do with xcb/util/atom.c.
Except every time I think about it, I remember that C doesn't have
map[7] and decide it would take too much effort.

More importantly, neither XCB nor Xgob have a "shutdown" hook[9]. This
leads to either a guaranteed leak, or one more function every user has
to remember to call (even if the user didn't know that one of their
libraries was using it). I mostly couldn't decide if I wanted Xgob's to
take a closure or an interface before I ran out of vacation time.

Porting xlsclients from Xlib to xgob took less effort[6] than porting it
from Xlib to xcb, despite also changing the language. The result is
shorter, easier to understand, and has similar network performance[10]
characteristics compared to the XCB version. It also puts back the
"clients are not allowed to stomp on the root ... try anyway" check for
properties on the root window, since it was trivial to restore without
incurring an extra network round trip.

CPU performance is worse than the XCB version. Turning off the garbage
collector gains some of that back. I hear rumour that the Go folks are
planning a faster garbage collector in the future. User time is
drastically worse than the Xlib version, but User+Sys time is similar
(because Xlib is always sending one tiny request at a time).

Comments welcome.

[1] http://golang.org/
[2] Goroutine, actually, but it's the same thing in the end. As of
today, 6g defaults to green threads and gccgo always uses pthreads.
[3] http://cgit.freedesktop.org/~peterh/xgob/
[4] Just libxcb; no xml parser/generator[5]. I hand-wrote a couple of
functions in the style I expect a parser/generator could generate for
testing purposes.
[5] No GenerateID yet either. Unlike xauth (which was consciously left
out for time reasons), I just forgot to add it.
[6] With the exception of print_quoted_word. I spent far too much time
thinking about different ways to implement this function. In hindsight,
I should have just done a literal translation in the first place.
[7] Or hash_map, or hash, or table, or dict, or Hashtable, or whatever
your favourite language calls it. C doesn't have one[8].
[8] Actually, C has lots, but none of them are in the language or the
standard library. I didn't want to depend on glib, for example. I also
looked at the atom cache in Xlib, but then I had to scrub my eyeballs.
[9] See
http://lists.freedesktop.org/archives/xcb/2009-October/005182.html and
[10] The Xgob implementation of XmuClientWindow is not aware of other
instances running at the same time, so it has to call Flush. This may
result in a few more TCP packet overheads, but the total number of round
trips should be the same as the Xcb version.

Peter Harris
               Open Text Connectivity Solutions Group
Peter Harris                    http://connectivity.opentext.com/
Research and Development        Phone: +1 905 762 6001
pharris at opentext.com            Toll Free: 1 877 359 4866

More information about the Xcb mailing list