Sfoglia il codice sorgente

Immediately recalculate summary when folder state changes syncing->idle

Jakob Borg 10 anni fa
parent
commit
e4dba99cc0
1 ha cambiato i file con 79 aggiunte e 48 eliminazioni
  1. 79 48
      cmd/syncthing/summarysvc.go

+ 79 - 48
cmd/syncthing/summarysvc.go

@@ -18,9 +18,10 @@ import (
 // The folderSummarySvc adds summary information events (FolderSummary and
 // FolderCompletion) into the event stream at certain intervals.
 type folderSummarySvc struct {
-	model *model.Model
-	srv   suture.Service
-	stop  chan struct{}
+	model     *model.Model
+	srv       suture.Service
+	stop      chan struct{}
+	immediate chan string
 
 	// For keeping track of folders to recalculate for
 	foldersMut sync.Mutex
@@ -32,6 +33,7 @@ func (c *folderSummarySvc) Serve() {
 	srv.Add(serviceFunc(c.listenForUpdates))
 	srv.Add(serviceFunc(c.calculateSummaries))
 
+	c.immediate = make(chan string)
 	c.stop = make(chan struct{})
 	c.folders = make(map[string]struct{})
 	c.srv = srv
@@ -50,7 +52,7 @@ func (c *folderSummarySvc) Stop() {
 // listenForUpdates subscribes to the event bus and makes note of folders that
 // need their data recalculated.
 func (c *folderSummarySvc) listenForUpdates() {
-	sub := events.Default.Subscribe(events.LocalIndexUpdated | events.RemoteIndexUpdated)
+	sub := events.Default.Subscribe(events.LocalIndexUpdated | events.RemoteIndexUpdated | events.StateChanged)
 	defer events.Default.Unsubscribe(sub)
 
 	for {
@@ -63,9 +65,29 @@ func (c *folderSummarySvc) listenForUpdates() {
 
 			data := ev.Data.(map[string]interface{})
 			folder := data["folder"].(string)
-			c.foldersMut.Lock()
-			c.folders[folder] = struct{}{}
-			c.foldersMut.Unlock()
+
+			if ev.Type == events.StateChanged && data["to"].(string) == "idle" && data["from"].(string) == "syncing" {
+				// The folder changed to idle from syncing. We should do an
+				// immediate refresh to update the GUI. The send to
+				// c.immediate must be nonblocking so that we can continue
+				// handling events.
+
+				select {
+				case c.immediate <- folder:
+					c.foldersMut.Lock()
+					delete(c.folders, folder)
+					c.foldersMut.Unlock()
+
+				default:
+				}
+			} else {
+				// This folder needs to be refreshed whenever we do the next
+				// refresh.
+
+				c.foldersMut.Lock()
+				c.folders[folder] = struct{}{}
+				c.foldersMut.Unlock()
+			}
 
 		case <-c.stop:
 			return
@@ -82,48 +104,9 @@ func (c *folderSummarySvc) calculateSummaries() {
 	for {
 		select {
 		case <-pump.C:
-			// We only recalculate sumamries if someone is listening to events
-			// (a request to /rest/events has been made within the last
-			// pingEventInterval).
-
-			lastEventRequestMut.Lock()
-			// XXX: Reaching out to a global var here is very ugly :( Should
-			// we make the gui stuff a proper object with methods on it that
-			// we can query about this kind of thing?
-			last := lastEventRequest
-			lastEventRequestMut.Unlock()
-
 			t0 := time.Now()
-			if time.Since(last) < pingEventInterval {
-				for _, folder := range c.foldersToHandle() {
-					// The folder summary contains how many bytes, files etc
-					// are in the folder and how in sync we are.
-					data := folderSummary(c.model, folder)
-					events.Default.Log(events.FolderSummary, map[string]interface{}{
-						"folder":  folder,
-						"summary": data,
-					})
-
-					for _, devCfg := range cfg.Folders()[folder].Devices {
-						if devCfg.DeviceID.Equals(myID) {
-							// We already know about ourselves.
-							continue
-						}
-						if !c.model.ConnectedTo(devCfg.DeviceID) {
-							// We're not interested in disconnected devices.
-							continue
-						}
-
-						// Get completion percentage of this folder for the
-						// remote device.
-						comp := c.model.Completion(devCfg.DeviceID, folder)
-						events.Default.Log(events.FolderCompletion, map[string]interface{}{
-							"folder":     folder,
-							"device":     devCfg.DeviceID.String(),
-							"completion": comp,
-						})
-					}
-				}
+			for _, folder := range c.foldersToHandle() {
+				c.sendSummary(folder)
 			}
 
 			// We don't want to spend all our time calculating summaries. Lets
@@ -132,6 +115,9 @@ func (c *folderSummarySvc) calculateSummaries() {
 			wait := 2*time.Since(t0) + pumpInterval
 			pump.Reset(wait)
 
+		case folder := <-c.immediate:
+			c.sendSummary(folder)
+
 		case <-c.stop:
 			return
 		}
@@ -141,6 +127,20 @@ func (c *folderSummarySvc) calculateSummaries() {
 // foldersToHandle returns the list of folders needing a summary update, and
 // clears the list.
 func (c *folderSummarySvc) foldersToHandle() []string {
+	// We only recalculate sumamries if someone is listening to events
+	// (a request to /rest/events has been made within the last
+	// pingEventInterval).
+
+	lastEventRequestMut.Lock()
+	// XXX: Reaching out to a global var here is very ugly :( Should
+	// we make the gui stuff a proper object with methods on it that
+	// we can query about this kind of thing?
+	last := lastEventRequest
+	lastEventRequestMut.Unlock()
+	if time.Since(last) < pingEventInterval {
+		return nil
+	}
+
 	c.foldersMut.Lock()
 	res := make([]string, 0, len(c.folders))
 	for folder := range c.folders {
@@ -151,6 +151,37 @@ func (c *folderSummarySvc) foldersToHandle() []string {
 	return res
 }
 
+// sendSummary send the summary events for a single folder
+func (c *folderSummarySvc) sendSummary(folder string) {
+	// The folder summary contains how many bytes, files etc
+	// are in the folder and how in sync we are.
+	data := folderSummary(c.model, folder)
+	events.Default.Log(events.FolderSummary, map[string]interface{}{
+		"folder":  folder,
+		"summary": data,
+	})
+
+	for _, devCfg := range cfg.Folders()[folder].Devices {
+		if devCfg.DeviceID.Equals(myID) {
+			// We already know about ourselves.
+			continue
+		}
+		if !c.model.ConnectedTo(devCfg.DeviceID) {
+			// We're not interested in disconnected devices.
+			continue
+		}
+
+		// Get completion percentage of this folder for the
+		// remote device.
+		comp := c.model.Completion(devCfg.DeviceID, folder)
+		events.Default.Log(events.FolderCompletion, map[string]interface{}{
+			"folder":     folder,
+			"device":     devCfg.DeviceID.String(),
+			"completion": comp,
+		})
+	}
+}
+
 // serviceFunc wraps a function to create a suture.Service without stop
 // functionality.
 type serviceFunc func()