[GLX/XLIB] Event loop without busy waiting?

Started by
3 comments, last by Katie 13 years, 9 months ago
Hi there,

I think the topic already says it. I am currently confronted with a program whose rendering process basically is A) Process pending messages, B) render stuff. Unfortunately, the whole thing hogs 100% CPU resources even when rendering nothing. Being unfamiliar with the Xlib/GLX environment, my guess is that this is a busy-waiting thing.

The event handler in the program is non-blocking (events are only extracted when they are queued. The documentation says that extracting an event from an empty event-queue will block, much like socket reading, etc.). I know how to overcome this problem in a Windows setting, but I have no idea how to do it in Linux. Found nothing on that problem and all the source-code that I've seen does the same thing.

Anyone got experience with this particular problem?

- Michael.
Advertisement
OK, first things first.

Your X11 connection is a socket, and hence can be checked using epoll(). Epoll conveniently takes timeouts. Hence, you can do a loop which says;

put the X11 socket into an epoll set.while(1){   get the time.   draw a frame.   while(1)   {     work out (1/framerate)-elapsed time.     if (<0) break;     epoll the set.     if (epoll result > 0)     {        process X11 events until none left.     }   }}


If you have network connections, you can hook them into the epoll cycle as well.

Processing events will be along in a minute when I can find a suitable lump of code...
So, to put the X11 display handling into a non-blocking mode you need to not call XNextEvent.

That returns you events off the queue if there are any or it tries to read the socket. Usually, for X11 apps, this is perfectly OK.

We want to get events if there are any, or come back straight off if not.

There is a missing function in the X11 API. There ought to be an XCheckEvent (get events or return false) analogous to XNextEvent (get events or block) and XPeekEvent (copy events or return false) but it doesn't exist.

So the trick is to use one of the XCheckSOMETHING style calls.

If you only have 1 window to worry about, XCheckWindowEvent will pull events for the given window of a given type, or return false for "none available".

If you have several windows or you don't care about types, you're probably better off going for XCheckIfEvent, which allows you to pass in an arbitrary decision function.

Obviously, if that just returns "TRUE" then you'll get the first event or an immediate return for an empty queue.


The one BIG caution I'll raise here is that these functions may still take a small amount of time to return. Why? Because they all flush the pending X11 queue. This is important because it may include flipping the window and so on, and may involve a frame sync. This is one reason why it's useful to put it after the rendering.





Hello,

first of all, let me thank you for your answer. I think I need to clarify the situation, so I'll include some source code this time:

XEvent event;// [...]while (true) {  // Process pending events:  while (XPending(display) > 0) {    // Fetch next event:    XNextEvent(display, &event);    // Process the event:    switch (event.type) {      // I'll save you the event handling, since it is only      // delegation into other functions of the program, like      // key mapping, shutting down, etc. However, no other      // calls to the Xlib are made.    }  }    // Draw:  drawScene(); // <- I modified this so it just clears the screen and               //    swaps the buffers. Not much involved here.  }


So, the message loop is non-blocking in this implementation (verified that by inserting a 'cout << ...'). However, the application (although doing virtually nothing) hogs the whole CPU time on the core that it runs on.

As I said in the OP, I am not familiar with this whole thing under Linux, but I think the missing piece is some instruction that tells the system 'okay, now you can do what you want and then return to this thread please'. So, I don't really care about the event right now. It is more important to get the program not to hog all the CPU time, since this is a little bit annoying on single-cored CPUs when you want to use other programs in parallel :)

- Michael.
Ahhh, right, yeah. Use epoll().

Your loop says; process all pending events. Draw stuff, flip buffers. Go round again.

What you're after is a loop which says; process events. Snooze for a while, draw, flip and go round again.

The "snooze for a while" is where you use epoll().

Stick the X11 connection's file descriptor into an epoll() set. You then call epoll on that set with a timeout equal to 1/framerate - rendering_time

It'll return whether either there are X11 events to do or it's slept through the timeout.

While in the epoll, the process doesn't run. The CPU sets an alarm to wake it up and also asks the comms infrastructure to wake it up early if there's events to handle and then stops it running.

If the epoll comes back with work on the socket, you do an event loop like you have, then go back to epolling for the remaining time until you're next due a frame.

This topic is closed to new replies.

Advertisement