internal/thread: remove Stop and use context.Context instead

This commit is contained in:
Hajime Hoshi 2022-12-28 17:03:49 +09:00
parent 01eb1a0bca
commit c638727759
3 changed files with 38 additions and 28 deletions

View File

@ -14,18 +14,20 @@
package thread
import (
"context"
)
// Thread defines threading behavior in Ebitengine.
type Thread interface {
Call(func())
Loop()
Stop()
Loop(context.Context) error
}
// OSThread represents an OS thread.
type OSThread struct {
funcs chan func()
done chan struct{}
terminate chan struct{}
}
// NewOSThread creates a new thread.
@ -33,7 +35,6 @@ func NewOSThread() *OSThread {
return &OSThread{
funcs: make(chan func()),
done: make(chan struct{}),
terminate: make(chan struct{}),
}
}
@ -42,7 +43,7 @@ func NewOSThread() *OSThread {
// It is assumed that an OS thread is fixed by runtime.LockOSThread when Loop is called.
//
// Loop must be called on the thread.
func (t *OSThread) Loop() {
func (t *OSThread) Loop(ctx context.Context) error {
for {
select {
case fn := <-t.funcs:
@ -53,17 +54,12 @@ func (t *OSThread) Loop() {
fn()
}()
case <-t.terminate:
return
case <-ctx.Done():
return ctx.Err()
}
}
}
// Stop stops the thread loop.
func (t *OSThread) Stop() {
t.terminate <- struct{}{}
}
// Call calls f on the thread.
//
// Do not call this from the same thread. This would block forever.
@ -83,10 +79,9 @@ func NewNoopThread() *NoopThread {
}
// Loop does nothing
func (t *NoopThread) Loop() {}
func (t *NoopThread) Loop(ctx context.Context) error {
return nil
}
// Call executes the func immediately
func (t *NoopThread) Call(f func()) { f() }
// Stop does nothing
func (t *NoopThread) Stop() {}

View File

@ -17,6 +17,9 @@
package ui
import (
stdcontext "context"
"errors"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
"github.com/hajimehoshi/ebiten/v2/internal/thread"
)
@ -36,20 +39,25 @@ func (u *userInterfaceImpl) Run(game Game, options *RunOptions) error {
return err
}
ctx, cancel := stdcontext.WithCancel(stdcontext.Background())
defer cancel()
ch := make(chan error, 1)
go func() {
defer close(ch)
if err := u.loopGame(); err != nil {
ch <- err
cancel()
return
}
}()
defer u.mainThread.Stop()
u.mainThread.Loop()
err := u.mainThread.Loop(ctx)
if errors.Is(err, stdcontext.Canceled) {
return <-ch
}
return err
}
// runOnAnotherThreadFromMainThread is called from the main thread, and calls f on a new goroutine (thread).
@ -68,9 +76,11 @@ func (u *userInterfaceImpl) runOnAnotherThreadFromMainThread(f func()) {
u.mainThread = thread.NewOSThread()
graphicscommand.SetRenderingThread(u.mainThread)
ctx, cancel := stdcontext.WithCancel(stdcontext.Background())
defer cancel()
go func() {
defer u.mainThread.Stop()
defer cancel()
f()
}()
u.mainThread.Loop()
_ = u.mainThread.Loop(ctx)
}

View File

@ -17,6 +17,7 @@
package ui
import (
stdcontext "context"
"fmt"
"runtime/debug"
"sync"
@ -80,12 +81,16 @@ func (u *userInterfaceImpl) Update() error {
return err
}
ctx, cancel := stdcontext.WithCancel(stdcontext.Background())
defer cancel()
renderCh <- struct{}{}
go func() {
<-renderEndCh
u.t.Stop()
cancel()
}()
u.t.Loop()
_ = u.t.Loop(ctx)
return nil
}