Xfce

Subdomains
 

C, Type Systems, GLib, and API Design

  • October 15, 2026
  • Brian Tarricone | Xfce

I was working on xfce4-session, and encountered a weird segfault that only happened in one particular case: while I was testing some new code in xfwl4 that restarts xfce4-session if it crashes.

I tested the feature by manually killing xfce4-session, but then it would actually crash once it restarted (and then get restarted, and crash again, and get restarted, and…). The crash was confusing. In the function that handles when an app wants to register over D-Bus, I’d added code to iterate over the elements of a GQueue of running session-managed applications to ensure that a new app wasn’t trying to register with the same ID that a previous app had used. I was seeing garbage inside the client instances, and the first list item’s next pointer was 0x1, which… well, not great.

On top of that, I couldn’t reproduce the crash with any diagnostic tooling: when running under valgrind, gcc’s address sanitizer, or even gdb (with a watchpoint enabled), there would be no crash. Even if I started xfce4-session manually, running it in a while true bash loop, so it would continually restart on exit, I couldn’t get it to crash. So it was timing-dependent, great.

I spent a ton of time digging through the core file with gdb, a ton of time just staring at the code, and then tossed it at GLM 5.3 Flash to see if it could make sense of it. The LLM added more logging and I dutifully helped it gather more data. After an hour or so of this, it seemed to have mostly given up.

I was tired, and it was already well past a reasonable hour to be awake (I’d been out with friends earlier, and was working on some code as a way of winding down for the night), so I went to bed, hoping the answer would come to me after sleeping, as often is the case for me. But by the time I decided to work on it again the next day, I still had no answers.

So I told the LLM to summarize what we’d learned in a markdown file, and then handed that to a new session with the non-Flash version of GLM 5.3, assuming it would be more capable. And so it was. It churned on the problem for about five minutes, and told me I was using the wrong API to fetch the list head pointer to start the iteration. (And, amusingly, bluntly affirmed that whoever was working on it before and wrote the handoff document was completely wrong about what the problem might be.)

A GQueue is a GList under the hood (the queue structure is just a list head pointer, tail pointer, and length). It has some functions for interacting with it and fetching the head or tail. I was using g_queue_peek_head(), which I thought would return the GList pointer, but returns the data pointer of whatever is stored in the queue head’s list struct. What I really wanted was g_queue_peek_head_link().

🤦🏻‍♂ 🤦🏻‍♂ 🤦🏻‍♂

I blame:

Myself, of course. I wrote the code, and I have an auto-completion plugin set up in vim, and probably even noticed the other completions after typing g_list_peek_head, but ignored them and/or thought they weren’t relevant to what I was doing, even one with “link” in the name.

But I also blame:

C’s weak type system, and type coercion rules. The return value of g_queue_peek_head() is gpointer (aka void *), and C lets you assign void pointers to any other pointer type, without a cast or warning. I was assigning it to a GList *, and the compiler was fine with that.

C’s lack of generics1. g_queue_peek_head() returns void * because it doesn’t (and can’t) know or track what type you’ve actually put into it. If it could, I would have gotten an error when trying to assign a XfsmClient * to a GList *.

GLib’s inconsistent API. I reached for g_queue_peek_head() because I am very familiar with GList, and am not super familiar with GQueue. GList has:

  • g_list_nth(), which returns the GList * node at the nth position, and
  • g_list_nth_data(), which returns the data in the list at the nth position.

So I assumed that GQueue would have g_queue_peek_head() (it does), which would return the GList pointer (it doesn’t) and q_queue_peek_head_data() (it doesn’t), which would return the data pointer. But no, instead it’s g_queue_peek_head_link() and g_queue_peek_head().2

The first two are among the many reasons why after 25+ years, I don’t enjoy working with C code anymore, and prefer something like Rust. It has a strong type system and generics, and type confusion is much harder, to the point of being essentially impossible in code like this.

Anyhow, speaking of unreasonable hours to be awake, I should go and… not be awake.

  1. Yes, I know about _Generic. It’s very limited, often awkward to use, and not really relevant here. You can’t build a truly generic data type out of it, because you need to write out all the “generic” types you plan to use with it ahead of time, when declaring it. That’s impossible for a library to do; of course it has no idea what random users of the library will use it for. ↩

  2. GQueue also has direct analogues to the GList functions I mentioned, g_queue_peek_nth() and g_queue_peek_nth_link(). That’s consistent with the other GQueue functions, but not with GList. I actually do think GQueue’s names are better! But I think consistency is more important. ↩