CGO and goroutines causing issues with OpenGL

583 views
Skip to first unread message

Samuel Payson

unread,
Jan 24, 2012, 12:59:37 PM1/24/12
to golang-nuts
I'm not sure if this is the correct list to be posting this on, so
please let me know if you think it would be more useful somewhere
else.

I recently created an issue on github for the Go-OpenGL port, which
can be found here: https://github.com/banthar/Go-OpenGL/issues/52.

In short, the problem is this:

If I mix goroutines with cgo calls to OpenGL (though I never call
OpenGL from these goroutines), I get segfaults deep inside of the
OpenGL implementation. Further examination with gdb shows that the
parameters are being passed to the OpenGL function's C-side entry
point exactly as I expect them to, but I get SIGSEGV anyway. Removing
goroutines from my program, despite them never touching OpenGL, fixes
the problem and the OpenGL code runs perfectly. I have also discovered
that calling runtime.LockOSThread() in the same goroutine that makes
all of the OpenGL (and GLEW, etc...) calls fixes the problem
completely, and goroutines can once again run free.

The link above contains more information about what I found and how I
found it, as well as some explanation of the thinking that lead me to
try runtime.LockOSThread() in the first place.

I am posting it here because the bug was not directly related to Go-
OpenGL (it happens if I call OpenGL directly from cgo), and seems more
to be a peculiarity of cgo interacting with go's scheduler. The fix I
posted causes the problem to go away, but I never found a satisfying
explanation for why the problem was occurring and hence I cannot be
sure that I've truly fixed it, rather than hiding it. I am hoping that
someone here might be able to produce such an explanation, or perhaps
suggest some other possibility for what went wrong.

John Asmuth

unread,
Jan 24, 2012, 6:20:35 PM1/24/12
to golan...@googlegroups.com
This is a known issue with OpenGL. OpenGL requires that all calls made come from a single thread because it uses some sort of thread-local information that it stores on its own.

The solution you found, using runtime.LockOSThread() is the correct way to use OpenGL from Go.

Michael Jones

unread,
Jan 24, 2012, 8:12:52 PM1/24/12
to golan...@googlegroups.com
OpenGL, at the lowest driver level, interacts with the OS and hardware devices without (or with less) copying between user space and privileged spaces. It does this in essentially all cases by promising the OS that all such transfers will happen from the same thread. The result is much greater data rates but the price is honoring that commitment. The OS is in a good position to detect violations so there is no getting away with loose behavior. 

A graphics-rich program can have as many threads as it wants. They can act without synchronization if only one holds an open OpenGL context structure. It can also do graphics from multiple threads if the competing threads use a mutex-like system to close the context in thread A before thread B tries to proceed. The third, and rare as can be, approach is to have independent threads write the the same context in parallel but in a safe way with the approval of the OS and device driver. I was part something like that once at SGI, but I doubt it's possible in a typical heterogenous computer/OS/driver/hardware environment.


On Tue, Jan 24, 2012 at 3:20 PM, John Asmuth <jas...@gmail.com> wrote:
This is a known issue with OpenGL. OpenGL requires that all calls made come from a single thread because it uses some sort of thread-local information that it stores on its own.

The solution you found, using runtime.LockOSThread() is the correct way to use OpenGL from Go.



--
Michael T. Jones | Chief Technology Advocate  | m...@google.com |  +1 650-335-5765

Michael Jones

unread,
Jan 24, 2012, 5:54:54 PM1/24/12
to Samuel Payson, golang-nuts

minux

unread,
Jun 14, 2012, 11:44:42 AM6/14/12
to bar...@gmail.com, golan...@googlegroups.com

On Thu, Jun 14, 2012 at 7:50 PM, <bar...@gmail.com> wrote:
The same problem exists with CUDA. CUDA requires a CUDA context which is associated with an (OS) thread. So runtime.LockOSThread() has to be called before cuda initialization. However, if you use many goroutines, you risk that they all be locked to the same OS thread, killing true multi-threading and impacting performance.
i think runtime.LockOSThread will only lock one goroutine to a os thread at any time.
so locking all goroutines to the same os thread won't happen.

André Moraes

unread,
Jun 17, 2012, 6:40:04 PM6/17/12
to Samuel Payson, golang-nuts
I think that with the explanatiosn from John/Michael/barnex you got
that the problem is: OpenGL doesn't like being called by different
threads.

The go-scheduler runs N goroutines into M threads, so withou
runtime.LockOSThread you can't be sure that the OpenGL CGO call will
be executed by the same thread.

The solution:

create a channel
start one goroutine to talk to OpenGL and use the previous channel
call runtime.LockOSThread Thread
in every loop of the main OpenGL routine, you can either check the
channel for information or just redraw the same data.

That way you can have multi-thread in you program and the OpenGL
context will only be used in the same thread, removing the SIGSEGV
error.


--
André Moraes
http://amoraes.info
Reply all
Reply to author
Forward
0 new messages