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 { } else {
RESTART <- true RESTART <- true
} }
// Safety Sleep (Ensure shutdown tasks get done)
time.Sleep(time.Second)
} }
app.loadConfig() app.loadConfig()
// Reinitialize password validator on config change, as opposed to every applicable request like in python. // 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 { } else {
RESTART <- true RESTART <- true
} }
// Safety Sleep (Ensure shutdown tasks get done)
time.Sleep(time.Second)
return nil return nil
} }

View File

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

84
main.go
View File

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

View File

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

View File

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

View File

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