fade out when stale
[arvados.git] / services / boot / task.go
index 1cc71a722ee08ca76a7ccdc39f607b60504175d4..75c49bd4a8956090c208abfa0ada8559cde8e1a4 100644 (file)
@@ -1,8 +1,10 @@
 package main
 
 import (
+       "context"
        "log"
        "sync"
+       "time"
 )
 
 type taskState string
@@ -19,33 +21,35 @@ type version int64
 
 type taskStateMap struct {
        s       map[task]taskState
-       lock    sync.Mutex
+       cond    *sync.Cond
        version version
 }
 
 var TaskState = taskStateMap{
-       s: make(map[task]taskState),
+       s:    make(map[task]taskState),
+       cond: sync.NewCond(&sync.Mutex{}),
 }
 
 func (m *taskStateMap) Set(t task, s taskState) {
-       m.lock.Lock()
-       defer m.lock.Unlock()
+       m.cond.L.Lock()
+       defer m.cond.L.Unlock()
        if old, ok := m.s[t]; ok && old == s {
                return
        }
        m.s[t] = s
        m.version++
+       m.cond.Broadcast()
 }
 
 func (m *taskStateMap) Version() version {
-       m.lock.Lock()
-       defer m.lock.Unlock()
+       m.cond.L.Lock()
+       defer m.cond.L.Unlock()
        return m.version
 }
 
 func (m *taskStateMap) Get(t task) taskState {
-       m.lock.Lock()
-       defer m.lock.Unlock()
+       m.cond.L.Lock()
+       defer m.cond.L.Unlock()
        if s, ok := m.s[t]; ok {
                return s
        } else {
@@ -60,6 +64,28 @@ type repEnt struct {
        Children    []repEnt
 }
 
+func (m *taskStateMap) Wait(v version, t time.Duration, ctx context.Context) bool {
+       ready := make(chan struct{})
+       var done bool
+       go func() {
+               m.cond.L.Lock()
+               defer m.cond.L.Unlock()
+               for v == m.version && !done {
+                       m.cond.Wait()
+               }
+               close(ready)
+       }()
+       select {
+       case <-ready:
+               return true
+       case <-ctx.Done():
+       case <-time.After(t):
+       }
+       done = true
+       m.cond.Broadcast()
+       return false
+}
+
 func report(tasks []task) ([]repEnt, version) {
        v := TaskState.Version()
        if len(tasks) == 0 {
@@ -78,9 +104,14 @@ func report(tasks []task) ([]repEnt, version) {
        return rep, v
 }
 
-func runTasks(tasks []task) {
+func runTasks(cfg *Config, tasks []task) {
        for _, t := range tasks {
-               TaskState.Set(t, StateChecking)
+               t.Init(cfg)
+       }
+       for _, t := range tasks {
+               if TaskState.Get(t) == taskState("") {
+                       TaskState.Set(t, StateChecking)
+               }
                err := t.Check()
                if err == nil {
                        log.Printf("%s: OK", t)
@@ -93,6 +124,7 @@ func runTasks(tasks []task) {
                        TaskState.Set(t, StateFailed)
                        continue
                }
+               TaskState.Set(t, StateFixing)
                if err = t.Fix(); err != nil {
                        log.Printf("%s: can't fix: %s", t, err)
                        TaskState.Set(t, StateFailed)
@@ -109,6 +141,7 @@ func runTasks(tasks []task) {
 }
 
 type task interface {
+       Init(*Config)
        ShortName() string
        String() string
        Check() error