giffer

Create .gif images from sites like youtube.com
Log | Files | Refs | LICENSE

task.go (2018B)


      1 package main
      2 
      3 import (
      4 	"fmt"
      5 	"sync"
      6 
      7 	"github.com/hashicorp/go-multierror"
      8 )
      9 
     10 // Task is an independent unit of work.
     11 type Task struct {
     12 	Name     string
     13 	Op       func() error
     14 	Requires []string
     15 }
     16 
     17 // FanOut executes all the independant tasks in parallel and waits for them to
     18 // finish.
     19 type FanOut []Task
     20 
     21 // Run all the tasks.
     22 func (tasks FanOut) Run() (err error) {
     23 	var (
     24 		failures = make(chan error)
     25 		wg       = &sync.WaitGroup{}
     26 		// index contains all the Tasks and allows us to resolve a Task
     27 		// object from it's name alone.
     28 		// This is used for building the graph.
     29 		index = map[string]Task{}
     30 		// Each chain is a serialised list of tasks to execute.
     31 		// Chains can be executed independently because they do not
     32 		// share dependencies - thus parallelism.
     33 		chains [][]Task
     34 	)
     35 	for _, t := range tasks {
     36 		index[t.Name] = t
     37 	}
     38 	g := &Graph{}
     39 	for _, t := range tasks {
     40 		g.Append(taskNode{Task: t, Index: index})
     41 	}
     42 	resolved := g.Resolve()
     43 	// Since the resolved slice is an ordered list of tasks, we can slice it
     44 	// into independent chains delimited by tasks that have no dependencies.
     45 	for _, n := range resolved {
     46 		if len(n.Requires()) > 0 {
     47 			// Append to the current chain.
     48 			current := len(chains) - 1
     49 			chains[current] = append(chains[current], index[n.ID()])
     50 		} else {
     51 			// Start a new chain.
     52 			chains = append(chains, []Task{index[n.ID()]})
     53 		}
     54 	}
     55 	wg.Add(len(chains))
     56 	for _, chain := range chains {
     57 		chain := chain
     58 		go func() {
     59 			defer wg.Done()
     60 			for _, t := range chain {
     61 				fmt.Printf("run: %v\n", t.Name)
     62 				if err := t.Op(); err != nil {
     63 					failures <- TaskError{Task: t, Err: err}
     64 					break
     65 				}
     66 			}
     67 		}()
     68 	}
     69 	go func() {
     70 		wg.Wait()
     71 		close(failures)
     72 	}()
     73 	for failure := range failures {
     74 		err = multierror.Append(err, failure)
     75 	}
     76 	return err
     77 }
     78 
     79 // TaskError associates an error with the task that produced it.
     80 type TaskError struct {
     81 	Task Task
     82 	Err  error
     83 }
     84 
     85 func (err TaskError) Error() string {
     86 	return fmt.Sprintf("task %q: %v", err.Task.Name, err.Err)
     87 }