1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-12-22 00:50:12 +00:00

fix bugs with restarts/interrupts

The password reset daemon wasn't being closed on restarts, so an extra
pwr would be sent w/ every restart. Restarts & Interrupts (Ctrl-C)
rarely worked, as there were multiple listeners to the "RESTART"
channel, and I didn't know the message was consumed by whoever got it
first, meaning if the main thread didn't get it first, the app wouldn't
quit. Listeners are now registered, and the restart message is
re-broadcasted until everyone's got it.

Fixes #264
This commit is contained in:
Harvey Tindall 2023-06-11 19:48:03 +01:00
parent f88f71d933
commit ad40d7d8a9
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
6 changed files with 60 additions and 42 deletions

4
api.go
View File

@ -359,6 +359,8 @@ func (app *appContext) ModifyConfig(gc *gin.Context) {
} else {
RESTART <- true
}
// Safety Sleep (Ensure shutdown tasks get done)
time.Sleep(time.Second)
}
app.loadConfig()
// Reinitialize password validator on config change, as opposed to every applicable request like in python.
@ -527,5 +529,7 @@ func (app *appContext) Restart() error {
} else {
RESTART <- true
}
// Safety Sleep (Ensure shutdown tasks get done)
time.Sleep(time.Second)
return nil
}

View File

@ -120,7 +120,7 @@ func (d *DiscordDaemon) run() {
defer d.deregisterCommands()
defer d.bot.Close()
d.registerCommands()
go d.registerCommands()
<-d.ShutdownChannel
d.ShutdownChannel <- "Down"

84
main.go
View File

@ -17,6 +17,7 @@ import (
"path/filepath"
"runtime"
"strings"
"syscall"
"time"
"github.com/fatih/color"
@ -43,12 +44,14 @@ var (
SWAGGER *bool
QUIT = false
RUNNING = false
warning = color.New(color.FgYellow).SprintfFunc()
info = color.New(color.FgMagenta).SprintfFunc()
hiwhite = color.New(color.FgHiWhite).SprintfFunc()
white = color.New(color.FgWhite).SprintfFunc()
version string
commit string
// Used to know how many times to re-broadcast restart signal.
RESTARTLISTENERCOUNT = 0
warning = color.New(color.FgYellow).SprintfFunc()
info = color.New(color.FgMagenta).SprintfFunc()
hiwhite = color.New(color.FgHiWhite).SprintfFunc()
white = color.New(color.FgWhite).SprintfFunc()
version string
commit string
)
var temp = func() string {
@ -102,7 +105,6 @@ type appContext struct {
host string
port int
version string
quit chan os.Signal
URLBase string
updater *Updater
newUpdate bool // Whether whatever's in update is new.
@ -152,6 +154,7 @@ func test(app *appContext) {
}
func start(asDaemon, firstCall bool) {
RESTARTLISTENERCOUNT = 0
RUNNING = true
defer func() { RUNNING = false }()
@ -250,7 +253,7 @@ func start(asDaemon, firstCall bool) {
app.err.Fatalf("Couldn't establish socket connection at %s\n", SOCK)
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
os.Remove(SOCK)
@ -578,40 +581,38 @@ func start(asDaemon, firstCall bool) {
} else {
app.info.Printf("Loaded @ %s", address)
}
app.quit = make(chan os.Signal)
signal.Notify(app.quit, os.Interrupt)
go func() {
for range app.quit {
app.shutdown()
}
}()
for range RESTART {
println("got it too!")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
if err := SRV.Shutdown(ctx); err != nil {
app.err.Fatalf("Server shutdown error: %s", err)
}
app.info.Println("Server shut down.")
return
waitForRestart()
app.info.Printf("Restart/Quit signal received, give me a second!")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
if err := SRV.Shutdown(ctx); err != nil {
app.err.Fatalf("Server shutdown error: %s", err)
}
app.info.Println("Server shut down.")
return
}
func shutdown() {
QUIT = true
RESTART <- true
// Safety Sleep (Ensure shutdown tasks get done)
time.Sleep(time.Second)
}
func (app *appContext) shutdown() {
app.info.Println("Shutting down...")
QUIT = true
RESTART <- true
for {
if RUNNING {
continue
}
cntx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
if err := SRV.Shutdown(cntx); err != nil {
app.err.Fatalf("Server shutdown error: %s", err)
}
shutdown()
}
os.Exit(1)
// Receives a restart signal and re-broadcasts it for other components.
func waitForRestart() {
RESTARTLISTENERCOUNT++
<-RESTART
RESTARTLISTENERCOUNT--
if RESTARTLISTENERCOUNT > 0 {
RESTART <- true
}
}
@ -686,6 +687,15 @@ func main() {
TEST = true
}
loadFilesystems()
quit := make(chan os.Signal, 0)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
// defer close(quit)
go func() {
<-quit
shutdown()
}()
if flagPassed("start") {
args := []string{}
for i, f := range os.Args {
@ -760,7 +770,7 @@ You can then run:
start(false, true)
for {
if QUIT {
continue
break
}
printVersion()
start(false, false)

View File

@ -46,7 +46,7 @@ func (app *appContext) StartPWR() {
app.err.Printf("Failed to start password reset daemon: %s", err)
}
<-RESTART
waitForRestart()
}
// PasswordReset represents a passwordreset-xyz.json file generated by Jellyfin.

View File

@ -1,4 +1,4 @@
// +build !windows
//go:build !windows
package main
@ -11,9 +11,10 @@ import (
func (app *appContext) HardRestart() error {
defer func() {
quit := make(chan os.Signal, 0)
if r := recover(); r != nil {
signal.Notify(app.quit, os.Interrupt)
<-app.quit
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
<-quit
}
}()
args := os.Args

View File

@ -8,6 +8,7 @@ import (
"os"
"os/signal"
"syscall"
"time"
"github.com/getlantern/systray"
)
@ -26,6 +27,8 @@ func onExit() {
if RUNNING {
QUIT = true
RESTART <- true
// Safety Sleep (Ensure shutdown tasks get done)
time.Sleep(time.Second)
}
os.Remove(SOCK)
}