banthar/Go-OpenGL — Issue #52: Mixing GL code with goroutines causes crashes in GL libs.
Author: scpayson
Created: 2012-01-21 04:09:33+00:00
State: open

## The Problem

My program has a parser which makes use of goroutines. No GL calls are made from any goroutine other than the main/original goroutine. As soon as I start using goroutines, my GL calls start segfaulting deep in the OpenGL drivers (based on disassembly, I know that a lookup table contains NULL where it should contain a function pointer). The same program without the use of goroutines works perfectly. Here is an overview of the facts:

1. My goroutines never touch GL code or cgo of any kind (other than burried in the standard libs)
2. The code works flawlessly if it runs without goroutines used anywhere in the program
3. As soon as a goroutine other than the main goroutine makes a GL call (I did this in a test) or a system call, all GL functions are instantly broken
- Any glcall results in a segfault
- The segfault occurs at some sort of table lookup deep in the graphics vendor's GL implementation
- The results are the same on Intel, NVidia, and ATI chips (I have at least one of each in the lab I'm working in)
4. Debugging and disassembly reveal that the arguments passed to the GL calls that cause the crash are 100% perfect. The problem is unrelated to GLEW, I make it to the actual GL function.
5. The same problem occurs if I call C.gl*() directly, it is not specific to Go-OpenGL

## A Solution

The whole problem goes away if I

1. Call runtime.LockOSThread() in the same goroutine that I call gl.Init() from.
2. Never make GL calls from any other thread

I discovered this solution based on a lot of tracing and guessing, I don't know if I'm really fixing the problem or just covering it up. If anyone knows what is actually happening it might help us find a good universal solution.

## A Guess At The Cause

I don't know very much about the subject, but it seems like perhaps the OpenGL implementation is using Thread Local Storage. Then maybe the following would happen:

- Program starts on Goroutine 0 on OS thread 0
- Program launches goroutine 1
- Goroutine 1 gets scheduled in on OS thread 0
- Goroutine 1 makes a system call, Go scheduler locks it to OS thread 0.
- Go scheduler spawns OS thread 1 so that goroutine 0 can keep working
- GL call happens from goroutine 0 on OS thread 1, where thread local storage is no longer properly initialized
- SIGSEGV

Even if it isn't the thread local storage thing, I am relatively sure it has something to do with GL calls being made from a thread other than that where OpenGL was initialized.

*As a disclaimer, I don't fully understand all of the topics I've discussed here, at least not enough to preach about them. What I know is based on a few days of debugging. I read through all sorts of the go runtime's source code and the generated cgo code, and I disassembled and walked through a lot of library code. If anyone has more detailed knowledge of all of this stuff, I would be very grateful to hear it.*