blob: cf77d59412807c202d7223967a2f026e7fcdf112 [file] [log] [blame]
khenaidoo1ce37ad2019-03-24 22:07:24 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package utils
17
khenaidoo2c6a0992019-04-29 13:46:56 -040018import (
19 "google.golang.org/grpc/codes"
20 "google.golang.org/grpc/status"
21 "reflect"
22 "time"
23)
24
khenaidoo1ce37ad2019-03-24 22:07:24 -040025type DeviceID struct {
26 Id string
27}
28
29type LogicalDeviceID struct {
30 Id string
31}
khenaidoo2c6a0992019-04-29 13:46:56 -040032
33//WaitForNilOrErrorResponses waits on a variadic number of channels for either a nil response or an error
34//response. If an error is received from a given channel then the returned error array will contain that error.
35//The error will be at the index corresponding to the order in which the channel appear in the parameter list.
36//If no errors is found then nil is returned. This method also takes in a timeout in milliseconds. If a
37//timeout is obtained then this function will stop waiting for the remaining responses and abort.
38func WaitForNilOrErrorResponses(timeout int64, chnls ...chan interface{}) []error {
39 // Create a timeout channel
40 tChnl := make(chan *interface{})
41 go func() {
42 time.Sleep(time.Duration(timeout) * time.Millisecond)
43 tChnl <- nil
44 }()
45
46 errorsReceived := false
47 errors := make([]error, len(chnls))
48 cases := make([]reflect.SelectCase, len(chnls)+1)
49 for i, ch := range chnls {
50 cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)}
51 }
52 // Add the timeout channel
53 cases[len(chnls)] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(tChnl)}
54
55 resultsReceived := make([]bool, len(errors)+1)
56 remaining := len(cases) - 1
57 for remaining > 0 {
58 index, value, ok := reflect.Select(cases)
59 if !ok { // closed channel
60 //Set the channel at that index to nil to disable this case, hence preventing it from interfering with other cases.
61 cases[index].Chan = reflect.ValueOf(nil)
62 errors[index] = status.Errorf(codes.Internal, "channel closed")
63 errorsReceived = true
64 } else if index == len(chnls) { // Timeout has occurred
65 for k := range errors {
66 if !resultsReceived[k] {
67 errors[k] = status.Errorf(codes.Aborted, "timeout")
68 }
69 }
70 errorsReceived = true
71 break
72 } else if value.IsNil() { // Nil means a good response
73 //do nothing
74 } else if err, ok := value.Interface().(error); ok { // error returned
75 errors[index] = err
76 errorsReceived = true
77 } else { // unknown value
78 errors[index] = status.Errorf(codes.Internal, "%s", value)
79 errorsReceived = true
80 }
81 resultsReceived[index] = true
82 remaining -= 1
83 }
84
85 if errorsReceived {
86 return errors
87 }
88 return nil
89}