Browse Source

- fix deadlock for when the http server for dashboard fails and Stop() is called

flashmob 8 years ago
parent
commit
2ff69cf370
2 changed files with 73 additions and 21 deletions
  1. 41 20
      dashboard/dashboard.go
  2. 32 1
      dashboard/dashboard_test.go

+ 41 - 20
dashboard/dashboard.go

@@ -22,7 +22,17 @@ var (
 	stopDataListener   chan bool = make(chan bool)
 	stopDataListener   chan bool = make(chan bool)
 	stopHttp           chan bool = make(chan bool)
 	stopHttp           chan bool = make(chan bool)
 
 
-	wg sync.WaitGroup
+	wg      sync.WaitGroup
+	started sync.WaitGroup
+
+	s state
+)
+
+type state int
+
+const (
+	stateStopped state = iota
+	stateRunning
 )
 )
 
 
 var upgrader = websocket.Upgrader{
 var upgrader = websocket.Upgrader{
@@ -61,6 +71,21 @@ func Run(c *Config) {
 
 
 	rand.Seed(time.Now().UnixNano())
 	rand.Seed(time.Now().UnixNano())
 
 
+	started.Add(1)
+	defer func() {
+		s = stateStopped
+
+	}()
+
+	closer, err := ListenAndServeWithClose(c.ListenInterface, r)
+	if err != nil {
+		log.WithError(err).Error("Dashboard server failed to start")
+		started.Done()
+		return
+	}
+	log.Infof("started dashboard, listening on http [%s]", c.ListenInterface)
+	wg.Add(1)
+
 	go func() {
 	go func() {
 		wg.Add(1)
 		wg.Add(1)
 		dataListener(tickInterval)
 		dataListener(tickInterval)
@@ -72,30 +97,26 @@ func Run(c *Config) {
 		wg.Done()
 		wg.Done()
 	}()
 	}()
 
 
-	closer, err := ListenAndServeWithClose(c.ListenInterface, r)
-	if err != nil {
-		stopDataListener <- true
-		stopRankingManager <- true
-		log.WithError(err).Error("Dashboard server failed to start")
+	s = stateRunning
+	started.Done()
+
+	select {
+	case <-stopHttp:
+		closer.Close()
+		wg.Done()
 		return
 		return
 	}
 	}
-	log.Infof("started dashboard, listening on http [%s]", c.ListenInterface)
-	wg.Add(1)
-	for {
-		select {
-		case <-stopHttp:
-			closer.Close()
-			wg.Done()
-			return
-		}
-	}
 }
 }
 
 
 func Stop() {
 func Stop() {
-	stopDataListener <- true
-	stopRankingManager <- true
-	stopHttp <- true
-	wg.Wait()
+	started.Wait()
+	if s == stateRunning {
+		stopDataListener <- true
+		stopRankingManager <- true
+		stopHttp <- true
+		wg.Wait()
+	}
+
 }
 }
 
 
 // Parses options in config and applies to global variables
 // Parses options in config and applies to global variables

+ 32 - 1
dashboard/dashboard_test.go

@@ -3,13 +3,14 @@ package dashboard
 import (
 import (
 	"sync"
 	"sync"
 	"testing"
 	"testing"
+	"time"
 )
 )
 
 
 func TestRunStop(t *testing.T) {
 func TestRunStop(t *testing.T) {
 
 
 	config := &Config{
 	config := &Config{
 		Enabled:               true,
 		Enabled:               true,
-		ListenInterface:       ":8081",
+		ListenInterface:       ":8082",
 		TickInterval:          "5s",
 		TickInterval:          "5s",
 		MaxWindow:             "24h",
 		MaxWindow:             "24h",
 		RankingUpdateInterval: "6h",
 		RankingUpdateInterval: "6h",
@@ -22,6 +23,36 @@ func TestRunStop(t *testing.T) {
 		Run(config)
 		Run(config)
 		wg.Done()
 		wg.Done()
 	}()
 	}()
+	// give Run some time to start
+	time.Sleep(time.Second)
+
+	Stop()
+
+	// Wait for Run() to exit
+	wg.Wait()
+
+}
+
+// Test if starting with a bad interface address
+func TestRunStopBadAddress(t *testing.T) {
+
+	config := &Config{
+		Enabled:               true,
+		ListenInterface:       "1.1.1.1:0",
+		TickInterval:          "5s",
+		MaxWindow:             "24h",
+		RankingUpdateInterval: "6h",
+	}
+
+	var wg sync.WaitGroup
+
+	wg.Add(1)
+	go func() {
+		Run(config)
+		wg.Done()
+	}()
+
+	time.Sleep(time.Second * 2)
 
 
 	Stop()
 	Stop()