0

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:

  • Arch linux, 3.14.4-1-ARCH.x86_64
  • GLFW 3.0.4-1
  • GLEW 1.10.0-2
  • nVidia GeForce GTX 570M
  • nVidia driver 337.12-1
  • go1.2 linux/amd64

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

Mediocre Gopher
  • 2,274
  • 1
  • 22
  • 39
  • Your title is incomprehensible, the first sentence makes sense but it is all down-hill from there. – Andon M. Coleman May 26 '14 at 05:35
  • 6
    I don't know if it is the problem you are experiencing, but some parts of opengl require that they be run by the same system thread. It could be different runtime functions are causing it to be called from different system threads. If this is the case, you need http://golang.org/pkg/runtime/#LockOSThread – Stephen Weinberg May 26 '14 at 05:39
  • also, why are you using tick if you want to wait once and not periodically? time.After seems like the right tool anyway. – Not_a_Golfer May 26 '14 at 08:59
  • @StephenWeinberg: OpenGL multithreading is a source of lot of confusion. Okay here's the gist of it: All OpenGL functions are perfectly fine to be called from any thread. However a given OpenGL context can be active in only one thread at a time, but it's perfectly possible to move a OpenGL context to another thread. Calling OpenGL functions without a context active in the thread does nothing, even for extension functions. This is how things are specified and crashes caused by calls to OpenGL functions context active or not are a bug in the implementation, not the program. – datenwolf May 26 '14 at 09:09
  • @StephenWeinberg: Calls to uninitialized OpenGL extension function pointers of course result in a crash, but if the extension function pointers are validly initialized then calling them must not cause a crash. – datenwolf May 26 '14 at 09:10
  • @Not_a_Golfer In my actual code I am looping using the Ticker, this is just a very simplified test case – Mediocre Gopher May 26 '14 at 13:29
  • I agree with @StephenWeinberg, weird OpenGL crashes in Go are usually the result of the runtime running the code on another thread without telling you. **ALWAYS** use `runtime.LockOSThread` in your rendering goroutine. – Linear May 26 '14 at 15:01
  • @datenwolf, I won't pretend to know what is actually going on. However, I have heard it claimed numerous times that opengl has thread local storage and whatnot that causes problems with the Go runtime. https://groups.google.com/forum/#!topic/golang-nuts/5Pvv1Gr1eoo is one such example. – Stephen Weinberg May 26 '14 at 18:19
  • @StephenWeinberg: Thanks for pointing me to the discussion. However there's going around a lot of half-knowledge there. I can only restate that OpenGL by itself is specified to be *thread safe* and all OpenGL calls are reentrant. It is true, that OpenGL implementations may have to do some funky stuff with thread local storage and if the Go runtime does something funky with TLS as well, that may clash hilariously. Also OP uses GLEW and GLEW itself also does funky things with OpenGL symbols and TLS, so that may be the cause as well. – datenwolf May 26 '14 at 21:06
  • @StephenWeinberg: It's an interesting phenomenon that should not happen. I was planning to do something nice with Go for a while now. I think this is the time to look into the subject matter. – datenwolf May 26 '14 at 21:07
  • @StephenWeinberg LockOSThread did indeed fix both the test case I posted and my actual code, so thanks! If you want to post it as an actual answer I'll mark it as the solution. – Mediocre Gopher May 27 '14 at 13:54
  • Not totally related, but there are pretty [good libraries](https://github.com/go-gl) for GLFW/OpenGL in go, the CGO work has already been done. – toasted_flakes May 28 '14 at 09:57

1 Answers1

2

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

Stephen Weinberg
  • 51,320
  • 14
  • 134
  • 113