lib/model: Return index from deviceActivity.leastBusy

This way, we don't need a second loop over the Availabilities to remove
the selected item.
This commit is contained in:
greatroar 2021-11-10 19:56:29 +01:00 committed by Jakob Borg
parent b84ee4d240
commit 6a9716e8a1
3 changed files with 26 additions and 35 deletions

View File

@ -26,20 +26,19 @@ func newDeviceActivity() *deviceActivity {
} }
} }
func (m *deviceActivity) leastBusy(availability []Availability) (Availability, bool) { // Returns the index of the least busy device, or -1 if all are too busy.
func (m *deviceActivity) leastBusy(availability []Availability) int {
m.mut.Lock() m.mut.Lock()
low := 2<<30 - 1 low := 2<<30 - 1
found := false best := -1
var selected Availability for i := range availability {
for _, info := range availability { if usage := m.act[availability[i].ID]; usage < low {
if usage := m.act[info.ID]; usage < low {
low = usage low = usage
selected = info best = i
found = true
} }
} }
m.mut.Unlock() m.mut.Unlock()
return selected, found return best
} }
func (m *deviceActivity) using(availability Availability) { func (m *deviceActivity) using(availability Availability) {

View File

@ -19,42 +19,42 @@ func TestDeviceActivity(t *testing.T) {
devices := []Availability{n0, n1, n2} devices := []Availability{n0, n1, n2}
na := newDeviceActivity() na := newDeviceActivity()
if lb, ok := na.leastBusy(devices); !ok || lb != n0 { if lb := na.leastBusy(devices); lb != 0 {
t.Errorf("Least busy device should be n0 (%v) not %v", n0, lb) t.Errorf("Least busy device should be n0 (%v) not %v", n0, lb)
} }
if lb, ok := na.leastBusy(devices); !ok || lb != n0 { if lb := na.leastBusy(devices); lb != 0 {
t.Errorf("Least busy device should still be n0 (%v) not %v", n0, lb) t.Errorf("Least busy device should still be n0 (%v) not %v", n0, lb)
} }
lb, _ := na.leastBusy(devices) lb := na.leastBusy(devices)
na.using(lb) na.using(devices[lb])
if lb, ok := na.leastBusy(devices); !ok || lb != n1 { if lb := na.leastBusy(devices); lb != 1 {
t.Errorf("Least busy device should be n1 (%v) not %v", n1, lb) t.Errorf("Least busy device should be n1 (%v) not %v", n1, lb)
} }
lb, _ = na.leastBusy(devices) lb = na.leastBusy(devices)
na.using(lb) na.using(devices[lb])
if lb, ok := na.leastBusy(devices); !ok || lb != n2 { if lb := na.leastBusy(devices); lb != 2 {
t.Errorf("Least busy device should be n2 (%v) not %v", n2, lb) t.Errorf("Least busy device should be n2 (%v) not %v", n2, lb)
} }
lb, _ = na.leastBusy(devices) lb = na.leastBusy(devices)
na.using(lb) na.using(devices[lb])
if lb, ok := na.leastBusy(devices); !ok || lb != n0 { if lb := na.leastBusy(devices); lb != 0 {
t.Errorf("Least busy device should be n0 (%v) not %v", n0, lb) t.Errorf("Least busy device should be n0 (%v) not %v", n0, lb)
} }
na.done(n1) na.done(n1)
if lb, ok := na.leastBusy(devices); !ok || lb != n1 { if lb := na.leastBusy(devices); lb != 1 {
t.Errorf("Least busy device should be n1 (%v) not %v", n1, lb) t.Errorf("Least busy device should be n1 (%v) not %v", n1, lb)
} }
na.done(n2) na.done(n2)
if lb, ok := na.leastBusy(devices); !ok || lb != n1 { if lb := na.leastBusy(devices); lb != 1 {
t.Errorf("Least busy device should still be n1 (%v) not %v", n1, lb) t.Errorf("Least busy device should still be n1 (%v) not %v", n1, lb)
} }
na.done(n0) na.done(n0)
if lb, ok := na.leastBusy(devices); !ok || lb != n0 { if lb := na.leastBusy(devices); lb != 0 {
t.Errorf("Least busy device should be n0 (%v) not %v", n0, lb) t.Errorf("Least busy device should be n0 (%v) not %v", n0, lb)
} }
} }

View File

@ -1532,8 +1532,8 @@ loop:
// Select the least busy device to pull the block from. If we found no // Select the least busy device to pull the block from. If we found no
// feasible device at all, fail the block (and in the long run, the // feasible device at all, fail the block (and in the long run, the
// file). // file).
selected, found := activity.leastBusy(candidates) found := activity.leastBusy(candidates)
if !found { if found == -1 {
if lastError != nil { if lastError != nil {
state.fail(errors.Wrap(lastError, "pull")) state.fail(errors.Wrap(lastError, "pull"))
} else { } else {
@ -1542,7 +1542,9 @@ loop:
break break
} }
candidates = removeAvailability(candidates, selected) selected := candidates[found]
candidates[found] = candidates[len(candidates)-1]
candidates = candidates[:len(candidates)-1]
// Fetch the block, while marking the selected device as in use so that // Fetch the block, while marking the selected device as in use so that
// leastBusy can select another device when someone else asks. // leastBusy can select another device when someone else asks.
@ -1804,16 +1806,6 @@ func (f *sendReceiveFolder) inConflict(current, replacement protocol.Vector) boo
return false return false
} }
func removeAvailability(availabilities []Availability, availability Availability) []Availability {
for i := range availabilities {
if availabilities[i] == availability {
availabilities[i] = availabilities[len(availabilities)-1]
return availabilities[:len(availabilities)-1]
}
}
return availabilities
}
func (f *sendReceiveFolder) moveForConflict(name, lastModBy string, scanChan chan<- string) error { func (f *sendReceiveFolder) moveForConflict(name, lastModBy string, scanChan chan<- string) error {
if isConflict(name) { if isConflict(name) {
l.Infoln("Conflict for", name, "which is already a conflict copy; not copying again.") l.Infoln("Conflict for", name, "which is already a conflict copy; not copying again.")