Call to glClientWaitSync blocks forever

Greetings,

I’m trying to synchronize buffer uploads of a glBufferData call using fences. However, it occasionally happens that the program stops inside the glClientWaitSync function and remains there forever. This is the callstack:

1 sched_yield syscall-template.S 84 0x7ffff3fcca37
2 ?? 0x7fffd5259ea4
3 ?? 0x7fffd4efa083
4 ?? 0x7fffd4e6dea2
return ( ::glClientWaitSync ( m_BufferUploadSync, 0, 1 ) == GL_ALREADY_SIGNALED ); // this is to check if the buffer has already uploaded and is probed once per frame.

This i how the fence is initialized before:

glBufferData(…)
m_BufferUploadSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);

It usually happens when I upload/render lots of stuff. Is this normal?

Regards

Did you ever flush the fence? I know you’re not doing it in your glClientWaitSync call, but you may have explicitly flushed somewhere else.

Also, if all you want to do is check to see if it completed, use a timeout of 0, not 1.

Try adding a glFlush() after your glFenceSync(). Either immediately afterwards (on the same thread of course), or somewhere after that before you expect the glClientWaitSync() to see the completion.

Because GL calls are queued on a command queue, basically you can end up with a situation where your glFenceSync call was queued but not yet processed by the driver/GPU. Since no other GL commands are getting pushed onto this queue, your FenceSync is “stuck” in the queue.

The glFlush() says to the driver “get to work on all those commands I’ve queued, but don’t block this CPU thread waiting on it all to finish.”

Ordinarily you don’t have to do any manual flushing (when doing double buffered rendering single-thread via a single GL context) because the *SwapBuffers does the only explicit glFlush required – at the end. With multithread, you have to start thinking about such things.

For more details, see:

[ul]
[li]glFlush or glFinish with multithreading (Piers Daniell) [/li][/ul]

Do I understand it correctly that a status pull of a sync object can block the whole thread just because the command queue is full?

So basically every time you risk a death lock if you not at last have one flush before or at the status pull?

Do I understand it correctly that a status pull of a sync object can block the whole thread just because the command queue is full?

Yes. This is why the extension, the OpenGL specification, and the Wiki article on sync objects all explicitly warn you about this. This is also why glClientWaitSync has the GL_SYNC_FLUSH_COMMANDS_BIT flag to begin with.

The ARB really isn’t trying to hide something from you here. It’s simply a consequence of the way GPUs have to interact with the CPU.

If the GPU queue is full when you add a sync, then that sync (and any other commands) have to sit somewhere and be added to the GPU’s queue when there’s room for it. But since that is a CPU operation, only a CPU process can push the data to the GPU, something has to tell the CPU to actually do it. After all, the driver cannot just hijack the CPU when the GPU gets some queue space.

glClientWaitSync stops the CPU thread from doing things until the GPU has processed something. But since the GPU processing that thing can’t happen until the CPU adds more stuff to the GPU queue… oops.

The only way to remove this from the API would be to make glFenceSync implicitly perform a flush. That’s like using a sledgehammer to kill a fly; sure, it may work, but the wall the fly is on isn’t likely to survive.

A flush needs to happen. Since this is a performance-critical thing, OpenGL requires that you take care of deciding when that will be.

So basically every time you risk a death lock if you not at last have one flush before or at the status pull?

Or you could just use the GL_SYNC_FLUSH_COMMANDS_BIT. Which again is why it’s there.

There is no risk if you use use the API correctly.

Note that this isn’t sufficient if you’re using multiple contexts and the glClientWaitSync() is issued for a different context to the glFenceSync(). In that case, you need glFlush() (or something else which is guaranteed to flush the command queue) on the context where the glFenceSync() was issued.