I have the following two files:
bridge.go:
package cube
// #cgo LDFLAGS: -lGL -lGLEW -lglfw
// #include <GLFW/glfw3.h>
// int init(GLFWwindow**);
// void render(GLFWwindow*);
import "C"
import (
"fmt"
"time"
)
func Init() {
var window *_Ctype_GLFWwindow
windowWat := (*[0]byte)(window)
fmt.Printf("Calling init\n")
if C.init(&windowWat) != 1 {
return
}
window = (*_Ctype_GLFWwindow)(windowWat)
//t := time.Tick(time.Second) // Doesn't work
t := time.After(time.Second) // Works
<-t
fmt.Println("Rendering")
C.render((*[0]byte)(window))
select {}
}
cube.c:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#define INIT_WINDOW_W (800)
#define INIT_WINDOW_H (800)
void render(GLFWwindow* window) {
glClearColor(135.0f / 255.0f, 206.0f / 255.0f, 250.f / 254.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glfwSwapBuffers(window);
}
static void glfw_error(int errno, const char* description) {
fprintf(stderr, "GLFW [%d] %s\n", errno, description);
exit(1);
}
int init(GLFWwindow** window) {
GLenum glewErr;
glfwSetErrorCallback(glfw_error);
if (!glfwInit()) {
return 0;
}
*window = glfwCreateWindow(
INIT_WINDOW_H,
INIT_WINDOW_W,
"wat",
NULL,
NULL
);
if (*window == NULL) {
glfwTerminate();
return 0;
}
glfwMakeContextCurrent(*window);
glewErr = glewInit();
if (glewErr != GLEW_OK) {
fprintf(stderr, "glewInit failed: %s\n", glewGetErrorString(glewErr));
return 0;
}
if (!GL_VERSION_2_0) {
fprintf(stderr, "Don't have OpenGL >= 2.0\n");
return 0;
}
return 1;
}
As well as a main.go file which calls cube.Init()
and nothing else.
My issue lies in the lines just before calling C.render
. If I do a time.After
, it works fine and displays a blue window as it's supposed to. If I do a time.Tick
, it will rarely also display that blue window, but either display a black window or segfault. This test case is significantly boiled down from my actual code.
I have a feeling that go's scheduler is messing things up somehow, but I can't think how (or how to test/fix that). I'm curious if anyone has any ideas what's causing this, or can think of any ways to investigate further.
Other possibly significant information:
EDIT:
Here's the segfault message:
SIGSEGV: segmentation violation
PC=0x7fda7d6a2e29
signal arrived during cgo execution
runtime.cgocall(0x401260, 0x7fda7d6f0e58)
/opt/go/src/pkg/runtime/cgocall.c:149 +0x11b fp=0x7fda7d6f0e40
game/cube._Cfunc_render(0x24e13b0)
game/cube/_obj/_cgo_defun.c:62 +0x31 fp=0x7fda7d6f0e58
game/cube.Init()
/tmp/wat/cube/bridge.go:28 +0x156 fp=0x7fda7d6f0ee0
main.main()
/tmp/wat/main.go:10 +0xac fp=0x7fda7d6f0f48
runtime.main()
/opt/go/src/pkg/runtime/proc.c:220 +0x11f fp=0x7fda7d6f0fa0
runtime.goexit()
/opt/go/src/pkg/runtime/proc.c:1394 fp=0x7fda7d6f0fa8
goroutine 3 [syscall]:
runtime.goexit()
/opt/go/src/pkg/runtime/proc.c:1394
rax 0x0
rbx 0x24e13b0
rcx 0x7fda7d6f0e58
rdx 0x7fda7d6f0df0
rdi 0x24e13b0
rsi 0xc210001900
rbp 0xc21002a000
rsp 0x7fda76506dc8
r8 0xc210001120
r9 0x7fda7d6f0df0
r10 0x7fda76506ba0
r11 0x7fda7d6a2e20
r12 0x0
r13 0x7fda76507700
r14 0x0
r15 0x7fda76d07c80
rip 0x7fda7d6a2e29
rflags 0x10202
cs 0x33
fs 0x0
gs 0x0
exit status 2
And it appears to segfault at the call to glClearColor
Turning my comment into an answer:
For some reason OpenGL often requires everything run in the same OS thread. time.Tick and time.After call different runtime functions and one may be causing different opengl calls to run in separate threads. By default, Go makes does not guarantee a goroutine runs on a specific thread.
To fix this, you need to use runtime.LockOSThread. This will ensure that goroutine and only that goroutine ever run on the current thread.
You can read more on the topic here: https://groups.google.com/forum/#!topic/golang-nuts/5Pvv1Gr1eoo