Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help: program exits abnormally with exit status 0xc0000005 #94

Closed
anhoder opened this issue May 8, 2024 · 2 comments · Fixed by #95
Closed

Help: program exits abnormally with exit status 0xc0000005 #94

anhoder opened this issue May 8, 2024 · 2 comments · Fixed by #95

Comments

@anhoder
Copy link

anhoder commented May 8, 2024

After registering an event callback, the program may exit unexpectedly when the event is triggered, and left only exit status 0xc0000005

image

This is a demo that can be reproduced, it is a simple program based on bubbletea(sorry, I can't reproduce on other simpler demos).

package main

import (
	"fmt"
	"log"
	"path/filepath"
	"runtime"
	"time"
	"unsafe"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/go-ole/go-ole"
	"github.com/saltosystems/winrt-go"
	"github.com/saltosystems/winrt-go/windows/foundation"
	"github.com/saltosystems/winrt-go/windows/media/core"
	"github.com/saltosystems/winrt-go/windows/media/playback"
)

func main() {
	p := tea.NewProgram(newModel(), tea.WithAltScreen())
	if _, err := p.Run(); err != nil {
		log.Fatal(err)
	}
}

type model struct {
	player *playback.MediaPlayer
	t      time.Duration
}

func newModel() *model {
	_ = ole.RoInitialize(1)

	// NOTE: init player
	p, _ := playback.NewMediaPlayer()
	playbackSession, _ := p.GetPlaybackSession()
	_ = p.SetAudioCategory(playback.MediaPlayerAudioCategoryMedia)

	// NOTE: register callback
	eventReceivedGuid := winrt.ParameterizedInstanceGUID(
		foundation.GUIDTypedEventHandler,
		playback.SignatureMediaPlaybackSession,
		"cinterface(IInspectable)",
	)
	h := foundation.NewTypedEventHandler(ole.NewGUID(eventReceivedGuid), func(_ *foundation.TypedEventHandler, sender, _ unsafe.Pointer) {})
	_, _ = playbackSession.AddPlaybackStateChanged(h)

	return &model{player: p}
}

func (m model) Init() tea.Cmd {
	return tick()
}

func (m model) Update(message tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := message.(type) {
	case tea.KeyMsg:
		key := msg.String()
		switch key {
		case "q", "esc", "ctrl+c":
			return m, tea.Quit
		case " ":
			// NOTE: run gc
			runtime.GC()

			// NOTE: start play, will trigger callback event
			m.play()
			return m, tick()
		}

	case tickMsg:
		m.t += time.Millisecond * 100
		return m, tick()
	}

	return m, nil
}

func (m model) View() string {
	return fmt.Sprintf("\n\n     Hello world! %s", m.t)
}

func (m model) play() {
	_, path, _, _ := runtime.Caller(0)
	uri, _ := foundation.UriCreateUri("file:///" + filepath.Dir(path) + "/a.mp3")
	s, _ := core.MediaSourceCreateFromUri(uri)
	_ = m.player.SetSource((*playback.IMediaPlaybackSource)(unsafe.Pointer(s)))
	_ = m.player.Play()
}

type tickMsg time.Time

func tick() tea.Cmd {
	return tea.Tick(time.Millisecond*100, func(t time.Time) tea.Msg {
		return tickMsg(t)
	})
}

demo.zip

After the program is running, click the space key several times, and it may exit abnormally

I guess it's a memory issue caused by go gc

When I disabled the gc with debug.SetGCPercent(-1), it worked fine

It's been bothering me for a few days, have you encountered this problem? 😢

jagobagascon added a commit that referenced this issue May 9, 2024
The VTable is a field all WinRT types have and it is used to store
pointers to all the class functions. This field is usually created by
WinRT, but for delegates we need to provide our own VTable instance.

We were previously using a Go struct pointer as our VTable, but Go
structs can be randomly moved or GCed by the Go runtime, and since we
are pasing the VTable to WinRT (out of the Go runtime), we were getting
random invalid memory errors.

This was addressed in a previous PR (#64) but somehow we missed this.

fixes #94
@jagobagascon
Copy link
Contributor

Hi! Thanks for the code sample, I can effectively reproduce the issue now.

The error occurs when we pass a Go pointer to WinRT, and the pointer is moved or deleted by the GC. The go runtime usually throws an error if it finds out. But for some reason it is not detecting it.

After lot's of testing I found the culprit:

inst.RawVTable = (*interface{})(unsafe.Pointer(&TypedEventHandlerVtbl{

We fixed a simmilar issue in the past (#64), and I can't believe how I missed such an obvious mistake 😅.

After fixing that the issue does no longer happen to me. Could you test the branch I pushed to #95 and see if it's fixed now?

jagobagascon added a commit that referenced this issue May 9, 2024
The VTable is a field all WinRT types have and it is used to store
pointers to all the class functions. This field is usually created by
WinRT, but for delegates we need to provide our own VTable instance.

We were previously using a Go struct pointer as our VTable, but Go
structs can be randomly moved or GCed by the Go runtime, and since we
are pasing the VTable to WinRT (out of the Go runtime), we were getting
random invalid memory errors.

This was addressed in a previous PR (#64) but somehow we missed this.

fixes #94
jagobagascon added a commit that referenced this issue May 9, 2024
The VTable is a field all WinRT types have and it is used to store
pointers to all the class functions. This field is usually created by
WinRT, but for delegates we need to provide our own VTable instance.

We were previously using a Go struct pointer as our VTable, but Go
structs can be randomly moved or GCed by the Go runtime, and since we
are pasing the VTable to WinRT (out of the Go runtime), we were getting
random invalid memory errors.

This was addressed in a previous PR (#64) but somehow we missed this.

fixes #94
@anhoder
Copy link
Author

anhoder commented May 9, 2024

Nice! Thanks for your work!

I tried again with the new branch and it worked great.
Thanks again for the fix.

@anhoder anhoder closed this as completed May 9, 2024
jagobagascon added a commit to jagobagascon/bluetooth that referenced this issue May 10, 2024
This version fixes an error that leaked Go pointers to the WinRT runtime
causing random access violation errors (0xc0000005) whenver these
pointers where freed or moved by Go's GC. For more info checkout
saltosystems/winrt-go#94.

Diff:
saltosystems/winrt-go@45c2d7a...4f7860a
deadprogram pushed a commit to tinygo-org/bluetooth that referenced this issue May 10, 2024
This version fixes an error that leaked Go pointers to the WinRT runtime
causing random access violation errors (0xc0000005) whenver these
pointers where freed or moved by Go's GC. For more info checkout
saltosystems/winrt-go#94.

Diff:
saltosystems/winrt-go@45c2d7a...4f7860a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants