From 8be5c6a4d711de82ef832089e04ca2e8180ff097 Mon Sep 17 00:00:00 2001 From: Simon Martens Date: Mon, 11 Nov 2024 14:03:57 +0100 Subject: [PATCH] Broken! Do not execute! --- helpers/evmux.go | 52 ++++++++++++++++++++ kgpz_web.go | 30 +++++++++--- server/server.go | 120 ++++++++++++++++++++++++++++++----------------- 3 files changed, 152 insertions(+), 50 deletions(-) create mode 100644 helpers/evmux.go diff --git a/helpers/evmux.go b/helpers/evmux.go new file mode 100644 index 0000000..6d4778f --- /dev/null +++ b/helpers/evmux.go @@ -0,0 +1,52 @@ +package helpers + +import "sync" + +type EventMux[T any] struct { + // INFO: This is a simple event multiplexer that allows to subscribe to events and to publish them. + mu sync.Mutex + subscribers []chan T +} + +func NewEventMux[T any]() *EventMux[T] { + return &EventMux[T]{ + subscribers: make([]chan T, 0), + } +} + +func (e *EventMux[T]) Subscribe(size uint) chan T { + e.mu.Lock() + defer e.mu.Unlock() + subscriber := make(chan T, size) + e.subscribers = append(e.subscribers, subscriber) + return subscriber +} + +func (e *EventMux[T]) Unsubscribe(subscriber chan T) { + e.mu.Lock() + defer e.mu.Unlock() + for i, s := range e.subscribers { + if s == subscriber { + close(s) + e.subscribers = append(e.subscribers[:i], e.subscribers[i+1:]...) + return + } + } +} + +func (e *EventMux[T]) Publish(event T) { + e.mu.Lock() + defer e.mu.Unlock() + for _, subscriber := range e.subscribers { + subscriber <- event + } +} + +func (e *EventMux[T]) Close() { + e.mu.Lock() + defer e.mu.Unlock() + for _, subscriber := range e.subscribers { + close(subscriber) + } + e.subscribers = make([]chan T, 0) +} diff --git a/kgpz_web.go b/kgpz_web.go index 602a8ed..2a94712 100644 --- a/kgpz_web.go +++ b/kgpz_web.go @@ -42,8 +42,11 @@ func Bootstrap(k *app.KGPZ) { } func Start(k *app.KGPZ, s *server.Server) { + s.Start() + sigs := make(chan os.Signal, 1) done := make(chan bool, 1) + serversignals := s.Events.Subscribe(1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) @@ -52,17 +55,30 @@ func Start(k *app.KGPZ, s *server.Server) { fmt.Println("Received signal. Cleaning up.") // INFO: here we add cleanup functions s.Kill() + s.BreakUntil(serversignals, server.Killed) + fmt.Println("Server killed.") k.Shutdown() done <- true }() - // INFO: if the server fails to start this exits the program - // We handle graceful server recovery in the server itself. - go func() { - s.Start() - <-s.Killed - fmt.Println("Server exited. Cleaning up.") - }() + // Interactive listening for input + if k.IsDebug() { + go func() { + for { + var input string + fmt.Scanln(&input) + if input == "r" { + fmt.Println("Restarting server.") + s.Restart() + } else if input == "p" { + fmt.Println("Pulling repo.") + k.Pull() + } else if input == "q" { + done <- true + } + } + }() + } <-done fmt.Println("Cleanup finished. Exiting.") diff --git a/server/server.go b/server/server.go index 568b581..1950189 100644 --- a/server/server.go +++ b/server/server.go @@ -6,6 +6,21 @@ import ( "net/http" "githib.com/Theodor-Springmann-Stiftung/kgpz_web/app" + "githib.com/Theodor-Springmann-Stiftung/kgpz_web/helpers" +) + +type ServerState int + +const ( + Created ServerState = iota + Running + Restarting + ShuttingDown + ShutDown + ShuttedDown + Kill + Killing + Killed ) // INFO: Server is a meta-package that handles the current router, which it starts in a goroutine. @@ -16,9 +31,7 @@ import ( // - we reload all clients // - if data validity catastrophically fails, we restart the router to map error pages. type Server struct { - running chan bool - alive chan bool - Killed chan bool + Events helpers.EventMux[ServerState] } func Start(k *app.KGPZ) *Server { @@ -26,81 +39,102 @@ func Start(k *app.KGPZ) *Server { } func (s *Server) Start() { - srv := &http.Server{Addr: ":8080"} - s.running = make(chan bool, 1) - s.alive = make(chan bool, 1) - s.Killed = s.killHandler(srv, s.alive) - shuttingdown := s.shutdownHandler(srv, s.running) - shutdown := s.runnerHandler(srv, shuttingdown) - s.restartHandler(shutdown) + srv := &http.Server{Addr: ":8081"} + s.killHandler(srv) + s.shutdownHandler(srv) + s.runnerHandler(srv) + s.restartHandler() } func (s *Server) Restart() { - s.running <- false + s.Events.Publish(ShutDown) } func (s *Server) Kill() { - s.alive <- false + s.Events.Publish(Kill) } -func (s *Server) killHandler(srv *http.Server, alive chan bool) chan bool { - kill := make(chan bool, 1) - +func (s *Server) restartHandler() { + shutdown := s.Events.Subscribe(1) go func() { - <-alive - - if err := srv.Shutdown(nil); err != nil { - fmt.Println("Error shutting down server") - } - - kill <- true - }() - - return kill -} - -func (s *Server) restartHandler(shutdown chan bool) { - go func() { - <-shutdown + s.BreakUntil(shutdown, ShuttedDown) + s.Events.Publish(Restarting) s.Start() }() } -func (s *Server) runnerHandler(srv *http.Server, shuttingdown chan bool) chan bool { - shutdown := make(chan bool, 1) +func (s *Server) runnerHandler(srv *http.Server) { + shutttingdown := s.Events.Subscribe(1) go func() { + s.Events.Publish(Running) // EXAMPLE: - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "hello world\n") }) + srv.Handler = mux + if err := srv.ListenAndServe(); err != http.ErrServerClosed { fmt.Println("Error starting server") + return } - <-shuttingdown - shutdown <- true + loop: + for { + msg := <-shutttingdown + if msg == ShuttingDown { + s.Events.Publish(ShuttedDown) + break loop + } + if msg == Killing { + s.Events.Publish(Killed) + break loop + } + } }() - return shutdown } -func (s *Server) shutdownHandler(srv *http.Server, running chan bool) chan bool { - shuttingdown := make(chan bool, 1) +func (s *Server) shutdownHandler(srv *http.Server) { + shutdown := s.Events.Subscribe(1) go func() { - <-running - + s.BreakUntil(shutdown, ShutDown) + fmt.Println("Shutting down server") if err := srv.Shutdown(nil); err != nil { fmt.Println("Error shutting down server") } - shuttingdown <- true + s.Events.Publish(ShuttingDown) }() - return shuttingdown } -func (s *Server) Shutdown() { +func (s *Server) killHandler(srv *http.Server) { + + kill := s.Events.Subscribe(1) + go func() { + s.BreakUntil(kill, Kill) + fmt.Println("Killing server") + if err := srv.Shutdown(nil); err != nil { + fmt.Println("Error shutting down server") + } + + s.Events.Publish(Killing) + }() + +} + +func (s *Server) BreakUntil(c chan ServerState, state ServerState) { +loop: + for { + msg := <-c + if msg == state { + break loop + } + } + + s.Events.Unsubscribe(c) }