Beispiel #1
0
func Example_ThrottledReader() {

	totalSize := 10 * iocontrol.KiB
	readPerSec := 100 * iocontrol.KiB
	maxBurst := 10 * time.Millisecond

	input := randBytes(totalSize)
	src := bytes.NewReader(input)
	measured := iocontrol.NewMeasuredReader(src)
	throttled := iocontrol.ThrottledReader(measured, readPerSec, maxBurst)

	done := make(chan []byte)
	go func() {
		start := time.Now()
		output, err := ioutil.ReadAll(throttled)
		fmt.Printf("done in %.1fs", time.Since(start).Seconds())
		if err != nil {
			log.Fatalf("error reading: %v", err)
		}
		done <- output
	}()

	for {
		select {
		case <-time.Tick(time.Millisecond * 10):
			log.Printf("reading at %s/s", humanize.IBytes(measured.BytesPerSec()))

		case output := <-done:
			if !bytes.Equal(input, output) {
				log.Print("==== input ====\n", hex.Dump(input))
				log.Print("==== output ====\n", hex.Dump(output))
				log.Fatalf("mismatch between input and output")
			}
			return
		}
	}

	// Output:
	// done in 0.1s
}
Beispiel #2
0
// NewControlledProcess creates the child proc.
func NewControlledProcess(cmd string, arguments []string, doneChan chan error, stdoutLimit int64) (JobControl, error) {
	var err error

	j := &Job{
		nil,
		nil,
		0,
		0,
		doneChan,
		stdoutLimit,
		nil,
	}

	// Drop command from cmdline arguments and pass the rest as arguments separately
	var args []string
	if len(arguments) > 0 {
		args = arguments[1:]
	}
	j.Cmd = exec.Command(cmd, args...)

	// Collect stdout from the process to redirect to real stdout
	stdoutpipe, err := j.Cmd.StdoutPipe()
	if err != nil {
		return nil, fmt.Errorf("Failed to acquire stdout: %s", err)
	}
	stdout := iocontrol.NewMeasuredReader(stdoutpipe)
	j.stdoutReader = stdout

	var wg sync.WaitGroup

	stdin, err := j.Cmd.StdinPipe()
	if err != nil {
		return nil, fmt.Errorf("Failed to acquire stdin: %s", err)
	}

	stderr, err := j.Cmd.StderrPipe()
	if err != nil {
		return nil, fmt.Errorf("Failed to acquire stderr: %s", err)
	}

	// Map all child processes under this tree so Kill really ends everything.
	j.Cmd.SysProcAttr = &syscall.SysProcAttr{
		Setpgid: true, // Set process group ID
	}

	log.Debugf("%#v\n", j.Cmd)

	// Start the sub-process but don't wait for completion to pickup the Pid
	// for resource monitoring.
	err = j.Cmd.Start()
	if err != nil {
		return nil, fmt.Errorf("Failed to execute sub-process: %s\n", err)
	}

	j.Pid = j.Cmd.Process.Pid
	j.Pgid, err = syscall.Getpgid(j.Pid)
	if err != nil {
		return nil, fmt.Errorf("Failed syscall.Getpgid: %s\n", err)
	}

	j.Proc, err = process.NewProcess(int32(j.Pgid))
	if err != nil {
		return nil, fmt.Errorf("Unable to create process.NewProcess: %s\n", err)
	}

	wg.Add(1)
	go func(wg *sync.WaitGroup, r io.Reader) {
		defer wg.Done()
		io.Copy(os.Stdout, r)
		log.Debugln("child closed stdout")
	}(&wg, stdout)

	go func(w io.WriteCloser) {
		io.Copy(w, os.Stdin)
	}(stdin)

	wg.Add(1)
	go func(wg *sync.WaitGroup, r io.Reader) {
		defer wg.Done()
		io.Copy(os.Stderr, r)
		log.Debugln("child closed stderr")
	}(&wg, stderr)

	// Background waiting for the job to finish and emit a done channel message
	// when complete.
	go func(wg *sync.WaitGroup, j *Job) {
		log.Debugln("Waiting on wg.Wait()")
		wg.Wait()
		log.Debugln("Waiting on Cmd.Wait()")
		err := j.Cmd.Wait()
		log.Debugf("Job finished: %q\n", err)
		j.done <- err
	}(&wg, j)

	return j, nil
}