// 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) }
// 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) }