9

I've already killed a week trying to solve a mysterious problem in a project of mine and I am out of ideas.

I wrote a Go package intended to play sounds which wraps OpenAL... pretty basic stuff. I got it working on my Xubuntu 14.04 (32-bit), so I booted into Windows (7, also 32-bit) in order to port it... and that's where the problems started.

Whenever I tried to use my audio package, the program crashed with c0000005. I tried to run it through gdb and was surprised to find out that it worked without a problem, it even played my test sound.

Time passed and not knowing what to do, I downloaded the OpenAL Soft source code and started adding printfs - and discovered the exact line it crashed on:

http://repo.or.cz/w/openal-soft.git/blob/HEAD:/Alc/backends/dsound.c#l361

For those lazy to click the link (or if the link stopped working), it's a call to DirectSoundCreate. Running through the debugger again, I saw my prints right before and after the call, and 4 new threads being created between them.

These are the relevant things in the Go file:

package audio

/*
#cgo CFLAGS: -I"../libraries/include"

#cgo windows,386 LDFLAGS: ../libraries/lib/windows/x86/OpenAL32.dll
#cgo windows,amd64 LDFLAGS: ../libraries/lib/windows/x64/OpenAL32.dll
#cgo linux LDFLAGS: -lopenal

#include "audio.h"
*/
import "C"
import (
    "errors"
)

var context *C.ALCcontext

func Init() error {
    context = C.initAudio() // it crashes on this line
    if context == nil {
        return errors.New("could not initialize audio")
    }

    SetActiveListener(NewListener())

    return nil
}

And here's the C file actually making the OpenAL calls:

#include "audio.h"

#include <string.h>
#include <stdio.h>

ALCcontext* initAudio() {
    ALCdevice* device = alcOpenDevice(NULL); // crashes here
    if (device == NULL) {
        return NULL;
    }

    ALCcontext* context = alcCreateContext(device, NULL);
    if (!alcMakeContextCurrent(context)) {
        return NULL;
    }

    return context;
}

Last but not least, if I do the exact same thing in pure C (literally just adding main into the file I posted and calling initAudio), it works.

My question is obvious: What the #@!$ is going on?

edit:

Some other things which might be worth mentioning:

I created a new project completely separate from the previous one, in which I have the two files I posted earlier (with different paths for the linker in the Go file, obviously) and the main Go file which does only one thing: call audio.Init. Regardless of which dll I try (I tried the "official" one from http://openal.org, the precompiled version of OpenAL Soft and two versions I compiled myself, one with MinGW and one with Visual Studio), it behaves the same. I also tried to call runtime.LockOSThread at the start of audio.Init, but it didn't help.

Also, I sent the compiled program to my two friends, one running Windows 8 and one with Windows 7 (both 64-bit). The one with Windows 8 said it worked, the one with 7 said it crashed. However, if we tried to compile it from source on the latter machine, with the 64-bit toolchain, it worked.

edit #2:

I just tried to compile OpenAL Soft from source again - first with Visual Studio and then with MinGW - and then link my independent project I mentioned earlier with the library files (OpenAL32.lib from VS and libOpenAL32.dll.a from MinGW) instead of the DLL directly (since that is... the more correct way I guess?)

The result with VS:

It failed during linking, with the following message:

# command-line-arguments
C:\Users\Milan\AppData\Local\Temp\go-build078751523/audiot/audio.a(_all.o): malf
ormed pe file: unexpected flags 0xe0500020 for PE section .text
audiot/audio._Cfunc_initAudio: undefined: _cgo_e0a3be4f138e_Cfunc_initAudio

The result with MinGW:

It succeeded to compile and run, and while it didn't prevent the crash, at least it printed something out:

fatal error: unexpected signal during runtime execution
[signal 0xc0000005 code=0x1 addr=0x420f85 pc=0x42874c]

runtime stack:
invalid spdelta 96655 -1
runtime: unexpected return pc for _cgo_ec587e40eeca_Cfunc_initAudio called from
0x42874800
runtime.throw(0x455dc0)
        c:/go/src/pkg/runtime/panic.c:520 +0x71
runtime.sigpanic()
        c:/go/src/pkg/runtime/os_windows.c:352 +0x46
invalid spdelta 96655 -1
runtime: unexpected return pc for _cgo_ec587e40eeca_Cfunc_initAudio called from
0x42874800
_cgo_ec587e40eeca_Cfunc_initAudio()
        ?:0 +0xc

goroutine 16 [syscall]:
runtime.cgocall(0x428740, 0x533f64)
        c:/go/src/pkg/runtime/cgocall.c:143 +0xed fp=0x533f58 sp=0x533f2c
audiot/audio._Cfunc_initAudio(0x42e340)
        audiot/audio/_obj/_cgo_defun.c:53 +0x37 fp=0x533f64 sp=0x533f58
audiot/audio.Init(0x0, 0x0)
        C:/Users/Milan/Desktop/Dropbox/Projekty/Go/src/audiot/audio/at.go:23 +0x
3c fp=0x533f90 sp=0x533f64
main.main()
        c:/Users/Milan/Desktop/Dropbox/Projekty/Go/src/audiot/main.go:10 +0x29 f
p=0x533f9c sp=0x533f90
runtime.main()
        c:/go/src/pkg/runtime/proc.c:247 +0x11e fp=0x533fd0 sp=0x533f9c
runtime.goexit()
        c:/go/src/pkg/runtime/proc.c:1445 fp=0x533fd4 sp=0x533fd0
created by _rt0_go
        c:/go/src/pkg/runtime/asm_386.s:101 +0x102

goroutine 17 [syscall]:
runtime.goexit()
        c:/go/src/pkg/runtime/proc.c:1445
exit status 2

I also thought it can't hurt to post the specifics of my toolchain:

$ gcc --version
gcc.exe (GCC) 4.8.1
Copyright (C) 2013 Free Software Foundation, Inc.

$ go version
go version go1.3 windows/386

And let's not forget Visual Studio 2013 Express

weegee
  • 3,256
  • 2
  • 18
  • 32
MilanV
  • 129
  • 1
  • 8
  • 1
    does equivalent C only code work well? Does memtest reveal faulty RAM (http://www.portalforums.net/viewtopic.php?f=62&t=7625) (ok that one's a stretch). Do you need to call coinitialize in each thread? are you running it with more than one thread a possibility? Maybe...go is garbage collecting something? latest OpenAL32.dll? [possibly related: https://java.net/jira/browse/JOAL-4] – rogerdpack Aug 15 '14 at 18:23
  • 4
    Did you try locking the OS thread to the go routine with `runtime.LockOSThread()`? – nemo Aug 15 '14 at 21:02
  • @rogerdpack Yes, C only code works well. Memtest says my RAM is OK. Sorry, I'm not really sure what you mean by coinitializing, could you elaborate? I don't think there's anything Go might be garbage collecting, I store the ALCcontext in a global variable and besides, the code crashes while still on the C side. – MilanV Aug 16 '14 at 11:38
  • @nemo Yes, I did, it didn't help. Anyway, I've edited the original post with some more info, see if it sheds some light. – MilanV Aug 16 '14 at 11:40
  • wonder if it is running into some type of "go doesn't give you enough stack" issue... – rogerdpack Aug 16 '14 at 14:39
  • Wait... "Howewer, if we tried to compile it from source on the latter machine, with the 64-bit toolchain, it worked." Are you trying to link 32-bit dependencies with 64-bit Go code? That tends to be a problem. – Linear Aug 16 '14 at 23:49
  • @Jsor I have both 32-bit and 64-bit versions of the dependencies stored in separate folders in the project and am using Go's conditional compilation to link with the correct ones (just like it is done in the first snippet I posted). It might be worth checking all of them once again though, just to be sure. As far as OpenAL is concerned, we used the Windows DLLs available here http://kcat.strangesoft.net/openal.html and used the correct one for each machine. It that was the problem though, how is it possible that the independent project I created doesn't work either? P.S. I updated the OP again – MilanV Aug 17 '14 at 11:53
  • possibly call CoInitialize again, after calling LockOSThread, to make sure it's taking? – rogerdpack Aug 18 '14 at 19:54
  • @rogerdpack Do you mean CoInitialize from DirectSound? If so, I should probably clarify something - I use OpenAL, I only mentioned DirectSound because it happens to be what OpenAL uses as a backend and what I found out to cause the crash. Writing DirectSound directly would defeat the whole purpose of choosing a cross-platform library. – MilanV Aug 18 '14 at 20:59
  • what if you run it in a goroutine? Also maybe ask on the golang-nuts ML if it sounds familiar to anyone. – rogerdpack Aug 20 '14 at 16:27

2 Answers2

3

I just learnt Go 1.3.1 came out, so I tried updating... and the problem disappeared. (argh!)

I guess it was a compiler bug then. Anyway, thanks to everyone who tried to help, I really appreciate it.

MilanV
  • 129
  • 1
  • 8
2

It might be related to Thread Switching:

The solution could be to use LockOSThread

http://golang.org/pkg/runtime/#LockOSThread

You can read more about it here: https://code.google.com/p/go-wiki/wiki/LockOSThread

Also your Init function has capital I if you are trying to initialize the library it has to be lower case.

fabrizioM
  • 46,639
  • 15
  • 102
  • 119
  • Thanks for your answer, but I'm afraid that - as I stated before - I did in fact try LockOSThread, without much success. I guess you're right about init, though. I originally meant to let the user call Init himself if he needed to, although the usage of init to initialize the package automatically is more idiomatic, I guess. – MilanV Aug 18 '14 at 20:53