Example #1
0
// Close detaches from the external process and waits for it to exit.
func (c *Client) Close() error {
	if !c.running.Swap(false) {
		return nil // already stopped
	}

	var errors []error
	if err := c.stdout.Close(); err != nil {
		errors = append(errors, fmt.Errorf("failed to detach stdout from %q: %v", c.cmd.Path, err))
	}
	if err := c.stdin.Close(); err != nil {
		errors = append(errors, fmt.Errorf("failed to detach stdin from %q: %v", c.cmd.Path, err))
	}
	if err := c.cmd.Wait(); err != nil {
		errors = append(errors, fmt.Errorf("%q failed with: %v", c.cmd.Path, err))
	}
	return internal.MultiError(errors)
}
Example #2
0
// Range calls the function fn on all items in coll concurrently and waits for
// all calls to finish.
//
// coll may be a slice or a map. If coll is a map, fn must accept the key and
// the value as its arguments, and otherwise it must accept an int index and
// the value as its arguments.
//
// fn may return nothing or error.
func Range(coll, fn interface{}) error {
	if coll == nil || fn == nil {
		log.Panicf("ConcurrentRange(%T, %T): both arguments must be non-nil", coll, fn)
	}

	cv := reflect.ValueOf(coll)
	fv := reflect.ValueOf(fn)
	ct := cv.Type()
	ft := fv.Type()

	if ft.NumIn() != 2 {
		log.Panicf("ConcurrentRange(%T, %T): fn must accept exactly two arguments", coll, fn)
	}

	switch ft.NumOut() {
	case 0:
		// adapt into a function that always returns a nil error
		fv = alwaysReturnNoError(fv)
		ft = fv.Type()
	case 1:
		if ft.Out(0) != _typeOfError {
			log.Panicf("ConcurrentRange(%T, %T): fn may only return error or nothing", coll, fn)
		}
	case 2:
		log.Panicf("ConcurrentRange(%T, %T): fn may only return error or nothing", coll, fn)
	}

	var (
		wg     sync.WaitGroup
		lock   sync.Mutex
		errors []error
	)

	switch ct.Kind() {
	case reflect.Map:
		if ft.In(0) != ct.Key() {
			log.Panicf("ConcurrentRange(%T, %T): fn's first argument must be a %v", coll, fn, ct.Key())
		}

		if ft.In(1) != ct.Elem() {
			log.Panicf("ConcurrentRange(%T, %T): fn's second argument must be a %v", coll, fn, ct.Elem())
		}

		for _, key := range cv.MapKeys() {
			value := cv.MapIndex(key)
			wg.Add(1)
			go func(key, value reflect.Value) {
				defer wg.Done()
				err, ok := fv.Call([]reflect.Value{key, value})[0].Interface().(error)
				if ok && err != nil {
					lock.Lock()
					errors = append(errors, err)
					lock.Unlock()
				}
			}(key, value)
		}

	case reflect.Slice:
		if ft.In(0) != _typeOfInt {
			log.Panicf("ConcurrentRange(%T, %T): fn's first argument must be an int", coll, fn)
		}

		if ft.In(1) != ct.Elem() {
			log.Panicf("ConcurrentRange(%T, %T): fn's second argument must be a %v", coll, fn, ct.Elem())
		}

		for i := 0; i < cv.Len(); i++ {
			value := cv.Index(i)
			wg.Add(1)
			go func(key, value reflect.Value) {
				defer wg.Done()
				err, ok := fv.Call([]reflect.Value{key, value})[0].Interface().(error)
				if ok && err != nil {
					lock.Lock()
					errors = append(errors, err)
					lock.Unlock()
				}
			}(reflect.ValueOf(i), value)
		}

	default:
		log.Panicf("ConcurrentRange(%T, %T): called with a type that is not a slice or a map", coll, fn)
	}

	wg.Wait()
	return internal.MultiError(errors)
}