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

ebiten: running a pulled iterator can crash the game #3042

Open
11 tasks
hajimehoshi opened this issue Jul 15, 2024 · 4 comments
Open
11 tasks

ebiten: running a pulled iterator can crash the game #3042

hajimehoshi opened this issue Jul 15, 2024 · 4 comments
Labels
Milestone

Comments

@hajimehoshi
Copy link
Owner

Ebitengine Version

52820e2

Operating System

  • Windows
  • macOS
  • Linux
  • FreeBSD
  • OpenBSD
  • Android
  • iOS
  • Nintendo Switch
  • PlayStation 5
  • Xbox
  • Web Browsers

Go Version (go version)

go version go1.23rc1 darwin/arm64

What steps will reproduce the problem?

Run this program with Go 1.23 (rc1):

package main

import (
	"iter"

	"github.com/hajimehoshi/ebiten/v2"
)

type Game struct {
	next func() (int, bool)
}

func (g *Game) Update() error {
	g.next()
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return outsideWidth, outsideHeight
}

func main() {
	seq := func(yield func(int) bool) {
		for {
			if !yield(0) {
				break
			}
		}
	}
	next, _ := iter.Pull(seq)
	g := &Game{
		next: next,
	}
	if err := ebiten.RunGame(g); err != nil {
		panic(err)
	}
}

What is the expected result?

No crash

What happens instead?

Crash:

coro: got thread 0x14000100008, want 0x100cce260
coro: got lock internal 0, want 0
coro: got lock external 0, want 1
fatal error: coro: OS thread locking must match locking at coroutine creation

runtime stack:
runtime.throw({0x100a106ee?, 0x14000103dc0?})
        /Users/hajimehoshi/sdk/go1.23rc1/src/runtime/panic.go:1067 +0x38 fp=0x17167ae80 sp=0x17167ae50 pc=0x100812b38
runtime.coroswitch_m(0x14000103dc0?)
        /Users/hajimehoshi/sdk/go1.23rc1/src/runtime/coro.go:125 +0x46c fp=0x17167af00 sp=0x17167ae80 pc=0x1007ae0ec
runtime.mcall()
        /Users/hajimehoshi/sdk/go1.23rc1/src/runtime/asm_arm64.s:193 +0x54 fp=0x17167af10 sp=0x17167af00 pc=0x100818204
...

Anything else you feel useful to add?

Reported by @eihigh

If needed, let's backport the fix to 2.7

@hajimehoshi hajimehoshi added this to the v2.8.0 milestone Jul 15, 2024
@hajimehoshi
Copy link
Owner Author

The problem is that Ebitengine calls LockOSThread in an internal init function, so the goroutine for the main function already locks an OS thread.

Actually, this works without crashing:

package main

import (
	"iter"

	"github.com/hajimehoshi/ebiten/v2"
)

type Game struct {
	next func() (int, bool)
}

func (g *Game) Update() error {
	g.next()
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return outsideWidth, outsideHeight
}

func main() {
	var next func() (int, bool)
	ch := make(chan struct{})
	go func() {
		seq := func(yield func(int) bool) {
			for {
				if !yield(0) {
					break
				}
			}
		}
		next, _ = iter.Pull(seq)
		close(ch)
	}()
	<-ch
	g := &Game{
		next: next,
	}
	if err := ebiten.RunGame(g); err != nil {
		panic(err)
	}
}

@hajimehoshi
Copy link
Owner Author

Related: golang/go#67694

We might not need LockOSThread for main. See golang/go#64777 (comment)

@hajimehoshi
Copy link
Owner Author

Sorry, 3d385ef doesn't fix the issue.

hajimehoshi added a commit that referenced this issue Jul 15, 2024
@hajimehoshi
Copy link
Owner Author

hajimehoshi commented Jul 15, 2024

I think this is not a fixable issue unless mainthread package is introduced... (golang/go#64777). Especially, some APIs for macOS must be called from the main thread, and there is no way to do it without LockOSThread in an init function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant