func NewQueueRunner(
	exec execer.Execer,
	filerMap runner.RunTypeMap,
	output runner.OutputCreator,
	capacity int,
	stat stats.StatsReceiver,
	dirMonitor *stats.DirsMonitor,
	rID runner.RunnerID,
) runner.Service {
	if stat == nil {
		stat = stats.NilStatsReceiver()
	}
	history := 1
	if capacity > 0 {
		history = 0  
	} else if capacity == 0 {
		capacity = 1  
	}
	statusManager := NewStatusManager(history)
	inv := NewInvoker(exec, filerMap, output, stat, dirMonitor, rID)
	controller := &QueueController{
		statusManager: statusManager,
		inv:           inv,
		filerMap:      filerMap,
		updateReq:     make(map[runner.RunType]bool),
		capacity:      capacity,
		reqCh:         make(chan interface{}),
		updateCh:      make(chan interface{}),
		cancelTimerCh: make(chan interface{}),
	}
	run := &Service{controller, statusManager}
	var wg sync.WaitGroup
	var initErr error = nil
	wait := false
	for t, f := range filerMap {
		if f.IDC != nil {
			log.Infof("Starting goroutine to wait on init for filer %v", t)
			wg.Add(1)
			wait = true
			go func(rt runner.RunType, idc snapshot.InitDoneCh) {
				err := <-idc
				if err != nil {
					initErr = err
					log.Errorf("Init channel for filer %v returned error: %s", rt, err)
				}
				wg.Done()
			}(t, f.IDC)
		}
	}
	if wait {
		go func() {
			wg.Wait()
			if initErr != nil {
				stat.Counter(stats.WorkerDownloadInitFailure).Inc(1)
				statusManager.UpdateService(runner.ServiceStatus{Initialized: false, Error: initErr})
			} else {
				statusManager.UpdateService(runner.ServiceStatus{Initialized: true})
				controller.startUpdateTickers()
			}
		}()
	} else {
		statusManager.UpdateService(runner.ServiceStatus{Initialized: true})
		controller.startUpdateTickers()
	}
	go controller.loop()
	return run
}
/*
NewQueueRunner creates a new Service that uses a Queue
If the worker has an initialization step (indicated by non-nil in idc) the queue will wait for the
worker to finish initialization before accepting any run requests.
If the queue is full when a command is received, an empty RunStatus and a queue full error will be returned.

@param: exec - runs the command
@param: filerMap - mapping of runner.RunType's to filers and corresponding InitDoneCh's
that are used by underlying Invokers. The Controller waits on all non-nil InitDoneCh's
to complete successfully before serving requests.
@param: output
@param: tmp
@param: capacity - the maximum number of commands to support on the queue.  If 0 then the queue is unbounded.
@param: stats - the stats receiver the queue will use when reporting its metrics
@param: dirMonitor - monitor directory size changes from running the task's command
@param: rID - the runner id
package runners
import (
	"fmt"
	"os"
	"sync"
	"time"
	log "github.com/sirupsen/logrus"
	"github.com/twitter/scoot/common/errors"
	"github.com/twitter/scoot/common/log/hooks"
	"github.com/twitter/scoot/common/log/tags"
	"github.com/twitter/scoot/common/stats"
	"github.com/twitter/scoot/runner"
	"github.com/twitter/scoot/runner/execer"
	"github.com/twitter/scoot/snapshot"
)
func init() {
	if loglevel := os.Getenv("SCOOT_LOGLEVEL"); loglevel != "" {
		level, err := log.ParseLevel(loglevel)
		if err != nil {
			log.Error(err)
			return
		}
		log.SetLevel(level)
		log.AddHook(hooks.NewContextHook())
	} else {
		log.SetLevel(log.ErrorLevel)
	}
}
const QueueFullMsg = "No resources available. Please try later."
const QueueInitingMsg = "Queue is still initializing. Please try later."
const QueueInvalidMsg = "Failed initialization, queue permanently broken."
type result struct {
	st  runner.RunStatus
	err error
}
type runReq struct {
	cmd      *runner.Command
	resultCh chan result
}
type abortReq struct {
	runID    runner.RunID
	resultCh chan result
}
type cmdAndID struct {
	cmd *runner.Command
	id  runner.RunID
}