Broken! Do not execute!

This commit is contained in:
Simon Martens
2024-11-11 14:03:57 +01:00
parent 28ea04f50d
commit 8be5c6a4d7
3 changed files with 152 additions and 50 deletions

52
helpers/evmux.go Normal file
View File

@@ -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)
}

View File

@@ -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.")

View File

@@ -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)
}