Skip to content

Instantly share code, notes, and snippets.

@BrianHicks
Created October 4, 2016 16:12
Show Gist options
  • Save BrianHicks/55644a2f2452dce57f0e2a95632f43dd to your computer and use it in GitHub Desktop.
Save BrianHicks/55644a2f2452dce57f0e2a95632f43dd to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"io"
"log"
"os"
"sort"
"sync"
"time"
"github.com/gosuri/uilive"
"golang.org/x/sync/errgroup"
)
func main() {
var durations []time.Duration
for _, dur := range os.Args[1:] {
parsed, err := time.ParseDuration(dur)
if err != nil {
log.Fatal(err)
}
durations = append(durations, parsed)
}
waiter := new(Waiter)
waiter.Start()
log.SetOutput(waiter.Bypass())
log.Println("starting waits")
group := new(errgroup.Group)
for _, duration := range durations {
duration := duration
group.Go(func() error {
waiter.Begin(duration.String())
defer waiter.End(duration.String())
log.Printf("waiting for %s", duration)
time.Sleep(duration / 2)
log.Printf("halfway done with %s", duration)
time.Sleep(duration / 2)
return nil
})
}
group.Wait()
waiter.Stop()
log.SetOutput(os.Stderr)
log.Println("finished waits")
}
type Waiter struct {
writer *uilive.Writer
once sync.Once
started bool
waiting map[string]time.Time
done map[string]time.Time
wlock *sync.RWMutex
}
func (w *Waiter) init() {
w.writer = uilive.New()
w.writer.RefreshInterval = 250 * time.Millisecond
w.waiting = make(map[string]time.Time)
w.done = make(map[string]time.Time)
w.wlock = new(sync.RWMutex)
}
func (w *Waiter) Message() string {
w.once.Do(w.init)
out := "\n"
w.wlock.RLock()
defer w.wlock.RUnlock()
var keys []string
for k := range w.waiting {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
var status string
if finishedAt, ok := w.done[k]; ok {
status = fmt.Sprintf("done (took %s)", w.roundDuration(finishedAt.Sub(w.waiting[k])))
} else {
status = fmt.Sprintf("running (%s)", w.roundDuration(time.Since(w.waiting[k])))
}
out += fmt.Sprintf("%s: %s\n", k, status)
}
return out
}
func (w *Waiter) roundDuration(t time.Duration) time.Duration {
return t - (t % time.Second)
}
func (w *Waiter) Begin(name string) {
w.once.Do(w.init)
w.wlock.Lock()
defer w.wlock.Unlock()
w.waiting[name] = time.Now()
}
func (w *Waiter) End(name string) {
w.once.Do(w.init)
w.wlock.Lock()
defer w.wlock.Unlock()
w.done[name] = time.Now()
}
func (w *Waiter) Bypass() io.Writer {
w.once.Do(w.init)
return w.writer.Bypass()
}
func (w *Waiter) Start() {
w.once.Do(w.init)
w.writer.Start()
w.started = true
go func() {
for w.started {
fmt.Fprint(w.writer, w.Message())
time.Sleep(w.writer.RefreshInterval)
}
}()
}
func (w *Waiter) Stop() {
w.once.Do(w.init)
w.started = false
w.writer.Stop()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment