mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-06-02 20:18:20 -04:00
cartridge will reload when HUP signal is received
This commit is contained in:
parent
bd170bd0b3
commit
3340e5cc8d
|
@ -391,15 +391,33 @@ func NewDebugger(opts CommandLineOptions, create CreateUserInterface) (*Debugger
|
|||
// fast and the channel queue should be pretty lengthy to prevent dropped
|
||||
// events (see PushFunction()).
|
||||
dbg.events = &terminal.ReadEvents{
|
||||
UserInput: make(chan userinput.Event, 10),
|
||||
UserInputHandler: dbg.userInputHandler,
|
||||
IntEvents: make(chan os.Signal, 1),
|
||||
PushedFunctions: make(chan func(), 4096),
|
||||
PushedFunctionsImmediate: make(chan func(), 4096),
|
||||
UserInput: make(chan userinput.Event, 10),
|
||||
UserInputHandler: dbg.userInputHandler,
|
||||
Signal: make(chan os.Signal, 1),
|
||||
SignalHandler: func(sig os.Signal) error {
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
return terminal.UserReload
|
||||
case syscall.SIGINT:
|
||||
return terminal.UserInterrupt
|
||||
case syscall.SIGQUIT:
|
||||
return terminal.UserQuit
|
||||
case syscall.SIGKILL:
|
||||
// we're unlikely to receive the kill signal, it normally being
|
||||
// intercepted by the terminal, but in case we do we treat it
|
||||
// like the QUIT signal
|
||||
return terminal.UserQuit
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
},
|
||||
PushedFunction: make(chan func(), 4096),
|
||||
PushedFunctionImmediate: make(chan func(), 4096),
|
||||
}
|
||||
|
||||
// connect Interrupt signal to dbg.events.intChan
|
||||
signal.Notify(dbg.events.IntEvents, os.Interrupt, syscall.SIGHUP)
|
||||
// connect signals to dbg.events.Signal channel. we include the Kill signal
|
||||
// but the chances are it'll never be seen
|
||||
signal.Notify(dbg.events.Signal, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT)
|
||||
|
||||
// allocate memory for user input
|
||||
dbg.input = make([]byte, 255)
|
||||
|
@ -715,9 +733,10 @@ func (dbg *Debugger) StartInDebugMode(filename string) error {
|
|||
}
|
||||
|
||||
defer dbg.end()
|
||||
|
||||
err = dbg.run()
|
||||
if err != nil {
|
||||
if errors.Is(err, terminal.UserQuit) {
|
||||
if errors.Is(err, terminal.UserSignal) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("debugger: %w", err)
|
||||
|
@ -842,7 +861,7 @@ func (dbg *Debugger) StartInPlayMode(filename string) error {
|
|||
|
||||
err = dbg.run()
|
||||
if err != nil {
|
||||
if errors.Is(err, terminal.UserQuit) {
|
||||
if errors.Is(err, terminal.UserSignal) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("debugger: %w", err)
|
||||
|
@ -876,8 +895,8 @@ func (dbg *Debugger) CartYield(yield coprocessor.CoProcYieldType) coprocessor.Yi
|
|||
return coprocessor.YieldHookContinue
|
||||
}
|
||||
|
||||
// if emulation is in itialisation state then we cause coprocessor execution
|
||||
// to end unless it's a memory or access erorr
|
||||
// if emulation is in the intialisation state then we cause coprocessor
|
||||
// execution to end unless it's a memory or access erorr
|
||||
//
|
||||
// this is an area that's likely to change. it's of particular interest to
|
||||
// ACE and ELF ROMs in which the coprocessor is run very early in order to
|
||||
|
@ -921,12 +940,15 @@ func (dbg *Debugger) run() error {
|
|||
err := dbg.playLoop()
|
||||
if err != nil {
|
||||
// if we ever encounter a cartridge ejected error in playmode
|
||||
// then simply open up the ROM selector
|
||||
// then simply open up the ROM selector and continue with the
|
||||
// running loop
|
||||
if errors.Is(err, cartridge.Ejected) {
|
||||
err = dbg.forceROMSelector()
|
||||
if err != nil {
|
||||
return fmt.Errorf("debugger: %w", err)
|
||||
}
|
||||
} else if errors.Is(err, terminal.UserReload) {
|
||||
dbg.reloadCartridge()
|
||||
} else {
|
||||
return fmt.Errorf("debugger: %w", err)
|
||||
}
|
||||
|
@ -946,7 +968,13 @@ func (dbg *Debugger) run() error {
|
|||
|
||||
err := dbg.inputLoop(dbg.term, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("debugger: %w", err)
|
||||
// there is no special handling for the cartridge ejected error,
|
||||
// unlike in play mode
|
||||
if errors.Is(err, terminal.UserReload) {
|
||||
dbg.reloadCartridge()
|
||||
} else {
|
||||
return fmt.Errorf("debugger: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -1445,13 +1473,8 @@ func (dbg *Debugger) reloadCartridge() error {
|
|||
}
|
||||
|
||||
// ReloadCartridge inserts the current cartridge and states the emulation over.
|
||||
//
|
||||
// It should not be run directly from the emulation/debugger goroutine, use
|
||||
// reloadCartridge() for that
|
||||
func (dbg *Debugger) ReloadCartridge() {
|
||||
dbg.PushFunctionImmediate(func() {
|
||||
dbg.unwindLoop(dbg.reloadCartridge)
|
||||
})
|
||||
dbg.events.Signal <- syscall.SIGHUP
|
||||
}
|
||||
|
||||
func (dbg *Debugger) insertCartridge(filename string) error {
|
||||
|
|
|
@ -15,12 +15,6 @@
|
|||
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/debugger/terminal"
|
||||
)
|
||||
|
||||
// readEventsHandler is called by inputLoop to make sure the program is
|
||||
// handling pushed events and/or user input.
|
||||
//
|
||||
|
@ -32,13 +26,8 @@ import (
|
|||
func (dbg *Debugger) readEventsHandler() error {
|
||||
for {
|
||||
select {
|
||||
case sig := <-dbg.events.IntEvents:
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
dbg.reloadCartridge()
|
||||
default:
|
||||
return terminal.UserInterrupt
|
||||
}
|
||||
case sig := <-dbg.events.Signal:
|
||||
return dbg.events.SignalHandler(sig)
|
||||
|
||||
case ev := <-dbg.events.UserInput:
|
||||
err := dbg.events.UserInputHandler(ev)
|
||||
|
@ -46,10 +35,10 @@ func (dbg *Debugger) readEventsHandler() error {
|
|||
return err
|
||||
}
|
||||
|
||||
case ev := <-dbg.events.PushedFunctions:
|
||||
case ev := <-dbg.events.PushedFunction:
|
||||
ev()
|
||||
|
||||
case ev := <-dbg.events.PushedFunctionsImmediate:
|
||||
case ev := <-dbg.events.PushedFunctionImmediate:
|
||||
ev()
|
||||
return nil
|
||||
|
||||
|
|
|
@ -190,11 +190,10 @@ func (dbg *Debugger) inputLoop(inputter terminal.Input, nonInstructionQuantum bo
|
|||
if err != nil {
|
||||
if errors.Is(err, terminal.UserInterrupt) {
|
||||
dbg.handleInterrupt(inputter)
|
||||
} else if errors.Is(err, terminal.UserSignal) {
|
||||
return err
|
||||
} else {
|
||||
// don't print UserQuit error to terminal
|
||||
if !errors.Is(err, terminal.UserQuit) {
|
||||
dbg.printLine(terminal.StyleError, "%s", err)
|
||||
}
|
||||
dbg.printLine(terminal.StyleError, "%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,9 +565,8 @@ func (dbg *Debugger) termRead(inputter terminal.Input) error {
|
|||
// user interrupts are used to quit or halt an operation
|
||||
dbg.handleInterrupt(inputter)
|
||||
|
||||
} else if errors.Is(err, terminal.UserAbort) || errors.Is(err, io.EOF) {
|
||||
// like user interrupts, abort are used to quit the application but
|
||||
// more forcibly
|
||||
} else if errors.Is(err, io.EOF) {
|
||||
// an EOF error causes the emulation to exit immediately
|
||||
dbg.running = false
|
||||
dbg.continueEmulation = false
|
||||
return nil
|
||||
|
@ -581,8 +579,8 @@ func (dbg *Debugger) termRead(inputter terminal.Input) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// interrupt errors that are sent back to the debugger need some special care
|
||||
// depending on the current state and what sort of terminal is being used.
|
||||
// interrupt signals need some special care depending on the current state and
|
||||
// what sort of terminal is being used.
|
||||
func (dbg *Debugger) handleInterrupt(inputter terminal.Input) {
|
||||
// end script scribe (if one is running)
|
||||
err := dbg.scriptScribe.EndSession()
|
||||
|
|
|
@ -34,25 +34,23 @@ func (dbg *Debugger) forceROMSelector() error {
|
|||
}
|
||||
|
||||
func (dbg *Debugger) playLoop() error {
|
||||
if dbg.forcedROMselection != nil {
|
||||
done := false
|
||||
for !done {
|
||||
select {
|
||||
case <-dbg.events.IntEvents:
|
||||
return terminal.UserInterrupt
|
||||
case ev := <-dbg.events.PushedFunctions:
|
||||
ev()
|
||||
case ev := <-dbg.events.PushedFunctionsImmediate:
|
||||
ev()
|
||||
return nil
|
||||
case ev := <-dbg.events.UserInput:
|
||||
if _, ok := ev.(userinput.EventQuit); ok {
|
||||
return terminal.UserQuit
|
||||
}
|
||||
case <-dbg.forcedROMselection:
|
||||
dbg.forcedROMselection = nil
|
||||
done = true
|
||||
// if forcedROMSelection is active (is not nil) then the event loop is
|
||||
// simplified for the duration (until it is set to nil)
|
||||
for dbg.forcedROMselection != nil {
|
||||
select {
|
||||
case sig := <-dbg.events.Signal:
|
||||
return dbg.events.SignalHandler(sig)
|
||||
case ev := <-dbg.events.PushedFunction:
|
||||
ev()
|
||||
case ev := <-dbg.events.PushedFunctionImmediate:
|
||||
ev()
|
||||
return nil
|
||||
case ev := <-dbg.events.UserInput:
|
||||
if _, ok := ev.(userinput.EventQuit); ok {
|
||||
return terminal.UserQuit
|
||||
}
|
||||
case <-dbg.forcedROMselection:
|
||||
dbg.forcedROMselection = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import (
|
|||
// inserted into the emulation loop correctly.
|
||||
func (dbg *Debugger) PushFunction(f func()) {
|
||||
select {
|
||||
case dbg.events.PushedFunctions <- f:
|
||||
case dbg.events.PushedFunction <- f:
|
||||
default:
|
||||
logger.Log("debugger", "dropped raw event push")
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ func (dbg *Debugger) PushFunction(f func()) {
|
|||
// return to the input loop for immediate action.
|
||||
func (dbg *Debugger) PushFunctionImmediate(f func()) {
|
||||
select {
|
||||
case dbg.events.PushedFunctionsImmediate <- f:
|
||||
case dbg.events.PushedFunctionImmediate <- f:
|
||||
default:
|
||||
logger.Log("debugger", "dropped raw event push (to return channel)")
|
||||
}
|
||||
|
|
|
@ -99,8 +99,8 @@ func (ct *ColorTerminal) TermRead(input []byte, prompt terminal.Prompt, events *
|
|||
|
||||
// wait for an event and respond
|
||||
select {
|
||||
case <-events.IntEvents:
|
||||
return 0, terminal.UserInterrupt
|
||||
case sig := <-events.Signal:
|
||||
return 0, events.SignalHandler(sig)
|
||||
|
||||
case ev := <-events.UserInput:
|
||||
ct.EasyTerm.TermPrint(ansi.CursorStore)
|
||||
|
@ -110,10 +110,10 @@ func (ct *ColorTerminal) TermRead(input []byte, prompt terminal.Prompt, events *
|
|||
return inputLen + 1, err
|
||||
}
|
||||
|
||||
case ev := <-events.PushedFunctions:
|
||||
case ev := <-events.PushedFunction:
|
||||
ev()
|
||||
|
||||
case ev := <-events.PushedFunctionsImmediate:
|
||||
case ev := <-events.PushedFunctionImmediate:
|
||||
ev()
|
||||
return 0, nil
|
||||
|
||||
|
|
|
@ -95,8 +95,8 @@ func (pt PlainTerminal) TermRead(input []byte, prompt terminal.Prompt, events *t
|
|||
// other events do not need to be checked - they will be serviced by the
|
||||
// debugger inputer loop elsewhere
|
||||
select {
|
||||
case <-events.IntEvents:
|
||||
return 0, terminal.UserInterrupt
|
||||
case sig := <-events.Signal:
|
||||
return 0, events.SignalHandler(sig)
|
||||
default:
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ package terminal
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/userinput"
|
||||
|
@ -53,30 +54,31 @@ type Input interface {
|
|||
}
|
||||
|
||||
// sentinal errors controlling program exit
|
||||
var UserInterrupt = errors.New("user interrupt")
|
||||
var UserAbort = errors.New("user abort")
|
||||
|
||||
// UserQuit indicates an intentional quit and should probably be caught and silenced
|
||||
var UserQuit = errors.New("user quit")
|
||||
var (
|
||||
UserSignal = errors.New("user signal")
|
||||
UserQuit = fmt.Errorf("%w: quit", UserSignal)
|
||||
UserInterrupt = fmt.Errorf("%w: interrupt", UserSignal)
|
||||
UserReload = fmt.Errorf("%w: reload", UserSignal)
|
||||
)
|
||||
|
||||
// ReadEvents *must* be monitored during a TermRead().
|
||||
type ReadEvents struct {
|
||||
// user input events. these are the inputs into the emulation (ie.
|
||||
// joystick, paddle, etc.)
|
||||
// user input events. these are the inputs into the emulation
|
||||
// (ie. joystick, paddle, etc.)
|
||||
UserInput chan userinput.Event
|
||||
UserInputHandler func(userinput.Event) error
|
||||
|
||||
// interrupt signals from the operating system
|
||||
IntEvents chan os.Signal
|
||||
// signals from the operating system
|
||||
Signal chan os.Signal
|
||||
SignalHandler func(os.Signal) error
|
||||
|
||||
// PushedFunctions allows functions to be pushed into the debugger goroutine
|
||||
//
|
||||
// errors are not returned by PushedFunctions so errors should be logged
|
||||
PushedFunctions chan func()
|
||||
// PushedFunction allows functions to be pushed into the debugger goroutine.
|
||||
// errors are not returned by PushedFunction so errors should be logged
|
||||
PushedFunction chan func()
|
||||
|
||||
// PushedFunctionsImmediate is the same as PushedFunctions but handlers
|
||||
// PushedFunctionImmediate is the same as PushedFunctions but handlers
|
||||
// must return control to the inputloop after the function has run
|
||||
PushedFunctionsImmediate chan func()
|
||||
PushedFunctionImmediate chan func()
|
||||
}
|
||||
|
||||
// Output defines the operations required by an interface that allows output.
|
||||
|
|
|
@ -37,14 +37,14 @@ func (dbg *Debugger) userInputHandler_catchUpLoop() {
|
|||
}
|
||||
|
||||
func (dbg *Debugger) userInputHandler(ev userinput.Event) error {
|
||||
// quite event
|
||||
// quit event
|
||||
switch ev.(type) {
|
||||
case userinput.EventQuit:
|
||||
dbg.running = false
|
||||
return terminal.UserQuit
|
||||
}
|
||||
|
||||
// mode specific special input (not passed to the VCS as controller input)
|
||||
// special handling of some user input (not passed to the VCS as controller input)
|
||||
switch dbg.Mode() {
|
||||
case govern.ModePlay:
|
||||
switch ev := ev.(type) {
|
||||
|
|
|
@ -121,13 +121,13 @@ func (trm *term) TermRead(buffer []byte, prompt terminal.Prompt, events *termina
|
|||
copy(buffer, inp+"\n")
|
||||
return len(inp) + 1, nil
|
||||
|
||||
case <-events.IntEvents:
|
||||
return 0, terminal.UserInterrupt
|
||||
case sig := <-events.Signal:
|
||||
return 0, events.SignalHandler(sig)
|
||||
|
||||
case ev := <-events.PushedFunctions:
|
||||
case ev := <-events.PushedFunction:
|
||||
ev()
|
||||
|
||||
case ev := <-events.PushedFunctionsImmediate:
|
||||
case ev := <-events.PushedFunctionImmediate:
|
||||
ev()
|
||||
return 0, nil
|
||||
|
||||
|
|
Loading…
Reference in a new issue