From 774596e8fdf519b42d389d347e8299c6f186c4a9 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 27 Jan 2023 21:39:37 +0900 Subject: [PATCH] windows: bug fix: crash when suspending/resuming when there are no devices Updates hajimehoshi/ebiten#2318 --- api_winmm_windows.go | 36 ------------------ driver_winmm_windows.go | 83 +++++++++-------------------------------- 2 files changed, 18 insertions(+), 101 deletions(-) diff --git a/api_winmm_windows.go b/api_winmm_windows.go index b3f263b..821299c 100644 --- a/api_winmm_windows.go +++ b/api_winmm_windows.go @@ -29,10 +29,7 @@ var ( var ( procWaveOutOpen = winmm.NewProc("waveOutOpen") procWaveOutClose = winmm.NewProc("waveOutClose") - procWaveOutPause = winmm.NewProc("waveOutPause") procWaveOutPrepareHeader = winmm.NewProc("waveOutPrepareHeader") - procWaveOutReset = winmm.NewProc("waveOutReset") - procWaveOutRestart = winmm.NewProc("waveOutRestart") procWaveOutUnprepareHeader = winmm.NewProc("waveOutUnprepareHeader") procWaveOutWrite = winmm.NewProc("waveOutWrite") ) @@ -140,17 +137,6 @@ func waveOutClose(hwo uintptr) error { return nil } -func waveOutPause(hwo uintptr) error { - r, _, e := procWaveOutPause.Call(hwo) - if _MMRESULT(r) != _MMSYSERR_NOERROR { - if e != nil && e != windows.ERROR_SUCCESS { - return fmt.Errorf("oto: waveOutPause failed: %w", e) - } - return fmt.Errorf("oto: waveOutPause failed: %w", _MMRESULT(r)) - } - return nil -} - func waveOutPrepareHeader(hwo uintptr, pwh *_WAVEHDR) error { r, _, e := procWaveOutPrepareHeader.Call(hwo, uintptr(unsafe.Pointer(pwh)), unsafe.Sizeof(_WAVEHDR{})) runtime.KeepAlive(pwh) @@ -163,28 +149,6 @@ func waveOutPrepareHeader(hwo uintptr, pwh *_WAVEHDR) error { return nil } -func waveOutReset(hwo uintptr) error { - r, _, e := procWaveOutReset.Call(hwo) - if _MMRESULT(r) != _MMSYSERR_NOERROR { - if e != nil && e != windows.ERROR_SUCCESS { - return fmt.Errorf("oto: waveOutReset failed: %w", e) - } - return fmt.Errorf("oto: waveOutReset failed: %w", _MMRESULT(r)) - } - return nil -} - -func waveOutRestart(hwo uintptr) error { - r, _, e := procWaveOutRestart.Call(hwo) - if _MMRESULT(r) != _MMSYSERR_NOERROR { - if e != nil && e != windows.ERROR_SUCCESS { - return fmt.Errorf("oto: waveOutRestart failed: %w", e) - } - return fmt.Errorf("oto: waveOutRestart failed: %w", _MMRESULT(r)) - } - return nil -} - func waveOutUnprepareHeader(hwo uintptr, pwh *_WAVEHDR) error { r, _, e := procWaveOutUnprepareHeader.Call(hwo, uintptr(unsafe.Pointer(pwh)), unsafe.Sizeof(_WAVEHDR{})) runtime.KeepAlive(pwh) diff --git a/driver_winmm_windows.go b/driver_winmm_windows.go index 6495afb..336449c 100644 --- a/driver_winmm_windows.go +++ b/driver_winmm_windows.go @@ -83,6 +83,9 @@ type winmmContext struct { loopEndCh chan error cond *sync.Cond + + suspended bool + suspendedCond *sync.Cond } var theWinMMContext *winmmContext @@ -94,6 +97,7 @@ func newWinMMContext(sampleRate, channelCount int, mux *mux.Mux, bufferSizeInByt bufferSizeInBytes: bufferSizeInBytes, mux: mux, cond: sync.NewCond(&sync.Mutex{}), + suspendedCond: sync.NewCond(&sync.Mutex{}), } theWinMMContext = c @@ -151,54 +155,20 @@ func (c *winmmContext) start() error { } func (c *winmmContext) Suspend() error { - c.cond.L.Lock() - defer c.cond.L.Unlock() + c.suspendedCond.L.Lock() + c.suspended = true + c.suspendedCond.L.Unlock() + c.suspendedCond.Signal() - if err := c.err.Load(); err != nil { - return err.(error) - } - - if err := waveOutPause(c.waveOut); err != nil { - return err - } return nil } func (c *winmmContext) Resume() (ferr error) { - var restart bool - defer func() { - if ferr != nil { - return - } - if !restart { - return - } - if err := c.stopLoopIfNeeded(); err != nil { - ferr = err - return - } - if err := c.start(); err != nil { - ferr = err - return - } - }() - - c.cond.L.Lock() - defer c.cond.L.Unlock() - - if err := c.err.Load(); err != nil { - return err.(error) - } - - // TODO: Ensure at least one header is queued? + c.suspendedCond.L.Lock() + c.suspended = false + c.suspendedCond.L.Unlock() + c.suspendedCond.Signal() - if err := waveOutRestart(c.waveOut); err != nil { - if errors.Is(err, _MMSYSERR_NODRIVER) { - restart = true - return nil - } - return err - } return nil } @@ -239,29 +209,6 @@ func (c *winmmContext) waitUntilHeaderAvailable() bool { return c.err.Load() == nil && c.loopEndCh == nil } -func (c *winmmContext) stopLoopIfNeeded() error { - ch := c.stopLoop() - if ch == nil { - return nil - } - return <-ch -} - -func (c *winmmContext) stopLoop() chan error { - c.cond.L.Lock() - defer c.cond.L.Unlock() - - // If the loop is already stopping, do nothing. - if c.loopEndCh != nil { - return nil - } - - ch := make(chan error) - c.loopEndCh = ch - c.cond.Signal() - return ch -} - func (c *winmmContext) loop() { defer func() { if err := c.closeLoop(); err != nil { @@ -269,6 +216,12 @@ func (c *winmmContext) loop() { } }() for { + c.suspendedCond.L.Lock() + for c.suspended { + c.suspendedCond.Wait() + } + c.suspendedCond.L.Unlock() + if !c.waitUntilHeaderAvailable() { return }