func UninstallSignalHandler(handler SignalHandler) {
	signalHandlers_mutex.Lock()
	delete(signalHandlers, handler)
	signalHandlers_mutex.Unlock()
}
Uninstalls the specified handler.
Trying to uninstall an non-existent handler is effectively a NOOP.
package spectrum
import (
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"time"
)
type Application struct {
	exitApp       chan byte  
	HasTerminated chan byte  
	eventLoops []*EventLoop
	terminationInProgress bool
	terminated            bool
	mutex sync.Mutex
	messageOutput MessageOutput
	Verbose         bool
	VerboseShutdown bool
	CreationTime time.Time  
}
func NewApplication() *Application {
	app := &Application{
		exitApp:       make(chan byte),
		HasTerminated: make(chan byte),
		eventLoops:    make([]*EventLoop, 0, 8),
		CreationTime:  time.Now(),
		messageOutput: &stdoutMessageOutput{},
	}
	go appGoroutine(app)
	return app
}
func appGoroutine(app *Application) {
	<-app.exitApp
	var startTime time.Time
	if app.Verbose {
		startTime = time.Now()
	}
	for {
		app.mutex.Lock()
		eventLoops := app.eventLoops
		app.eventLoops = make([]*EventLoop, 0, 8)
		app.mutex.Unlock()
		for _, e := range eventLoops {
			if app.VerboseShutdown {
				app.PrintfMsg("sending pause message to %s", e.Name)
			}
			e.Pause <- 0
			<-e.Pause
			if app.VerboseShutdown {
				app.PrintfMsg("%s is now paused", e.Name)
			}
		}
		for _, e := range eventLoops {
			if app.VerboseShutdown {
				app.PrintfMsg("sending terminate message to %s", e.Name)
			}
			e.Terminate <- 0
			<-e.Terminate
			if app.VerboseShutdown {
				app.PrintfMsg("%s has terminated", e.Name)
			}
		}
		app.mutex.Lock()
		if len(app.eventLoops) == 0 {
			app.mutex.Unlock()
			break
		} else {
			app.mutex.Unlock()
		}
	}
	if app.Verbose {
		endTime := time.Now()
		app.PrintfMsg("application shutdown completed after %s", endTime.Sub(startTime))
		app.PrintfMsg("application has terminated")
	}
	close(app.HasTerminated)
	app.mutex.Lock()
	app.terminationInProgress = false
	app.terminated = true
	app.mutex.Unlock()
}
func (app *Application) addEventLoop(e *EventLoop) bool {
	app.mutex.Lock()
	if app.terminated {
		app.mutex.Unlock()
		return false
	}
	app.eventLoops = append(app.eventLoops, e)
	app.mutex.Unlock()
	return true
}
func (app *Application) RequestExit() {
	app.mutex.Lock()
	{
		if app.terminationInProgress || app.terminated {
			app.mutex.Unlock()
			return
		}
		app.terminationInProgress = true
	}
	app.mutex.Unlock()
	close(app.exitApp)
}
func (app *Application) TerminationInProgress() bool {
	app.mutex.Lock()
	a := app.terminationInProgress
	app.mutex.Unlock()
	return a
}
func (app *Application) Terminated() bool {
	app.mutex.Lock()
	a := app.terminated
	app.mutex.Unlock()
	return a
}
func (app *Application) GetMessageOutput() MessageOutput {
	return app.messageOutput
}
func (app *Application) SetMessageOutput(out MessageOutput) MessageOutput {
	var prev MessageOutput
	app.mutex.Lock()
	{
		prev = app.messageOutput
		app.messageOutput = out
	}
	app.mutex.Unlock()
	return prev
}
func (app *Application) PrintfMsg(format string, a ...interface{}) {
	app.mutex.Lock()
	out := app.messageOutput
	app.mutex.Unlock()
	out.PrintfMsg(format, a...)
}
type EventLoop struct {
	app       *Application
	app_mutex sync.RWMutex
	Name string
	Pause chan byte
	Terminate chan byte
}
func (app *Application) NewEventLoop() *EventLoop {
	pc, _, _, _ := runtime.Caller(1)
	name := runtime.FuncForPC(pc).Name()
	e := &EventLoop{app: app, Name: name, Pause: make(chan byte), Terminate: make(chan byte)}
	ok := app.addEventLoop(e)
	if !ok {
		go func() {
			if app.VerboseShutdown {
				app.PrintfMsg("application has terminated before %s was created", e.Name)
				app.PrintfMsg("sending pause message to %s", e.Name)
			}
			e.Pause <- 0
			<-e.Pause
			if app.VerboseShutdown {
				app.PrintfMsg("%s is now paused", e.Name)
			}
			if app.VerboseShutdown {
				app.PrintfMsg("sending terminate message to %s", e.Name)
			}
			e.Terminate <- 0
			<-e.Terminate
			if app.VerboseShutdown {
				app.PrintfMsg("%s has terminated", e.Name)
			}
		}()
	}
	return e
}
func (e *EventLoop) App() *Application {
	e.app_mutex.RLock()
	app := e.app
	e.app_mutex.RUnlock()
	return app
}
func (e *EventLoop) Delete() <-chan byte {
	doneCh := make(chan byte)
	e.app_mutex.RLock()
	app := e.app
	e.app_mutex.RUnlock()
	app.mutex.Lock()
	{
		if app.terminationInProgress {
			app.mutex.Unlock()
			go func() { doneCh <- 0 }()
			return doneCh
		}
		found := false
		{
			num_eventLoops := len(app.eventLoops)
			for i := 0; i < num_eventLoops; {
				if app.eventLoops[i] == e {
					app.eventLoops[i] = app.eventLoops[num_eventLoops-1]
					app.eventLoops = app.eventLoops[0 : num_eventLoops-1]
					found = true
					break
				} else {
					i++
				}
			}
		}
		if !found {
			panic("no such event-loop")
		}
	}
	app.mutex.Unlock()
	go func() {
		e.Pause <- 0
		<-e.Pause
		e.Terminate <- 0
		<-e.Terminate
		e.app_mutex.Lock()
		e.app = nil
		e.app_mutex.Unlock()
		doneCh <- 0
	}()
	return doneCh
}
type MessageOutput interface {
	PrintfMsg(format string, a ...interface{})
}
type stdoutMessageOutput struct {
	mutex sync.Mutex
}
func (out *stdoutMessageOutput) PrintfMsg(format string, a ...interface{}) {
	out.mutex.Lock()
	{
		fmt.Printf(format, a...)
		appendNewLine := false
		if (len(format) == 0) || (format[len(format)-1] != '\n') {
			appendNewLine = true
		}
		if appendNewLine {
			fmt.Println()
		}
	}
	out.mutex.Unlock()
}
func Drain(ticker *time.Ticker) {
loop:
	for {
		select {
		case <-ticker.C:
		default:
			break loop
		}
	}
}
type SignalHandler interface {
	HandleSignal(signal os.Signal)
}
var signalHandlers = make(map[SignalHandler]bool)
var signalHandlers_mutex sync.Mutex
func InstallSignalHandler(handler SignalHandler) {
	signalHandlers_mutex.Lock()
	signalHandlers[handler] = true
	signalHandlers_mutex.Unlock()
}