blob: bffd9c4d26632924eba70e3dba199e41c0cc4ff6 [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 */
npujar1d86a522019-11-14 17:11:16 +053016
khenaidoo1ce37ad2019-03-24 22:07:24 -040017package utils
18
khenaidoo2c6a0992019-04-29 13:46:56 -040019import (
khenaidoo442e7c72020-03-10 16:13:48 -040020 "context"
khenaidoo631fe542019-05-31 15:44:43 -040021 "os"
khenaidoo442e7c72020-03-10 16:13:48 -040022 "sync"
khenaidoo2c6a0992019-04-29 13:46:56 -040023 "time"
npujar1d86a522019-11-14 17:11:16 +053024
khenaidoo442e7c72020-03-10 16:13:48 -040025 "github.com/opencord/voltha-lib-go/v3/pkg/log"
npujar1d86a522019-11-14 17:11:16 +053026 "google.golang.org/grpc/codes"
27 "google.golang.org/grpc/status"
khenaidoo2c6a0992019-04-29 13:46:56 -040028)
29
khenaidoo442e7c72020-03-10 16:13:48 -040030// ResponseCallback is the function signature for callbacks to execute after a response is received.
31type ResponseCallback func(rpc string, response interface{}, reqArgs ...interface{})
32
npujar1d86a522019-11-14 17:11:16 +053033// DeviceID represent device id attribute
khenaidoo1ce37ad2019-03-24 22:07:24 -040034type DeviceID struct {
npujar1d86a522019-11-14 17:11:16 +053035 ID string
khenaidoo1ce37ad2019-03-24 22:07:24 -040036}
37
npujar1d86a522019-11-14 17:11:16 +053038// LogicalDeviceID rpresent logical device id attribute
khenaidoo1ce37ad2019-03-24 22:07:24 -040039type LogicalDeviceID struct {
npujar1d86a522019-11-14 17:11:16 +053040 ID string
khenaidoo1ce37ad2019-03-24 22:07:24 -040041}
khenaidoo2c6a0992019-04-29 13:46:56 -040042
npujar1d86a522019-11-14 17:11:16 +053043// GetHostName returns host name
khenaidoo631fe542019-05-31 15:44:43 -040044func GetHostName() string {
45 return os.Getenv("HOSTNAME")
46}
47
khenaidoo442e7c72020-03-10 16:13:48 -040048type request struct {
49 channel chan struct{}
50 done chan struct{}
51}
52
53func newRequest() *request {
54 return &request{
55 channel: make(chan struct{}),
56 done: make(chan struct{}),
57 }
58}
59
60// RequestQueue represents a request processing queue where each request is processed to completion before another
61// request is given the green light to proceed.
62type RequestQueue struct {
63 queue chan *request
64 requestCompleteIndication chan struct{}
65 queueID string
66 stopOnce sync.Once
67 stopped bool
68}
69
70// NewRequestQueue creates a new request queue. maxQueueSize is the maximum size of the queue. queueID is used mostly
71// for logging.
72func NewRequestQueue(queueID string, maxQueueSize int) *RequestQueue {
73 return &RequestQueue{
74 queueID: queueID,
75 queue: make(chan *request, maxQueueSize),
76 requestCompleteIndication: make(chan struct{}),
77 }
78}
79
80// Start starts the request processing queue in its own go routine
81func (rq *RequestQueue) Start() {
82 go func() {
83 for {
84 req, ok := <-rq.queue
85 if !ok {
86 log.Warnw("request-sequencer-queue-closed", log.Fields{"id": rq.queueID})
87 break
88 }
89 // If the request is waiting then closing the reqChnl will trigger the request to proceed. Otherwise,
90 // if the request was cancelled then this will just clean up.
91 close(req.channel)
92
93 // Wait for either a request complete indication or a request aborted due to timeout
94 select {
95 case <-req.done:
96 case <-rq.requestCompleteIndication:
97 }
98 }
99 }()
100}
101
102// WaitForGreenLight is invoked by a function processing a request to receive the green light before
103// proceeding. The caller can also provide a context with timeout. The timeout will be triggered if the wait is
104// too long (previous requests taking too long)
105func (rq *RequestQueue) WaitForGreenLight(ctx context.Context) error {
106 if rq.stopped {
107 return status.Errorf(codes.Aborted, "queue-already-stopped-%s", rq.queueID)
108 }
109 request := newRequest()
110 // Queue the request
111 rq.queue <- request
112 select {
113 case <-request.channel:
114 return nil
115 case <-ctx.Done():
116 close(request.done)
117 return ctx.Err()
118 }
119}
120
121// RequestComplete must be invoked by a process when it completes processing the request. That process must have
122// invoked WaitForGreenLight() before.
123func (rq *RequestQueue) RequestComplete() {
124 if !rq.stopped {
125 rq.requestCompleteIndication <- struct{}{}
126 }
127}
128
129// Stop must only be invoked by the process that started the request queue. Prior to invoking Stop, WaitForGreenLight
130// must be invoked.
131func (rq *RequestQueue) Stop() {
132 rq.stopOnce.Do(func() {
133 rq.stopped = true
134 close(rq.requestCompleteIndication)
135 close(rq.queue)
136 })
137}
138
npujar1d86a522019-11-14 17:11:16 +0530139// Response -
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500140type Response struct {
141 *response
142}
143type response struct {
144 err error
145 ch chan struct{}
146 done bool
147}
148
npujar1d86a522019-11-14 17:11:16 +0530149// NewResponse -
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500150func NewResponse() Response {
151 return Response{
152 &response{
153 ch: make(chan struct{}),
154 },
155 }
156}
157
A R Karthick5c28f552019-12-11 22:47:44 -0800158// Fake a completed response.
159func DoneResponse() Response {
160 r := Response{
161 &response{
162 err: nil,
163 ch: make(chan struct{}),
164 done: true,
165 },
166 }
167 close(r.ch)
168 return r
169}
170
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500171// Error sends a response with the given error. It may only be called once.
172func (r Response) Error(err error) {
173 // if this is called twice, it will panic; this is intentional
174 r.err = err
175 r.done = true
176 close(r.ch)
177}
178
179// Done sends a non-error response unless Error has already been called, in which case this is a no-op.
180func (r Response) Done() {
181 if !r.done {
182 close(r.ch)
183 }
184}
185
khenaidoo2c6a0992019-04-29 13:46:56 -0400186//WaitForNilOrErrorResponses waits on a variadic number of channels for either a nil response or an error
187//response. If an error is received from a given channel then the returned error array will contain that error.
188//The error will be at the index corresponding to the order in which the channel appear in the parameter list.
189//If no errors is found then nil is returned. This method also takes in a timeout in milliseconds. If a
190//timeout is obtained then this function will stop waiting for the remaining responses and abort.
khenaidoo442e7c72020-03-10 16:13:48 -0400191func WaitForNilOrErrorResponses(timeout time.Duration, responses ...Response) []error {
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500192 timedOut := make(chan struct{})
khenaidoo442e7c72020-03-10 16:13:48 -0400193 timer := time.AfterFunc(timeout, func() { close(timedOut) })
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500194 defer timer.Stop()
khenaidoo2c6a0992019-04-29 13:46:56 -0400195
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500196 gotError := false
197 errors := make([]error, 0, len(responses))
198 for _, response := range responses {
199 var err error
200 select {
201 case <-response.ch:
202 // if a response is already available, use it
203 err = response.err
204 default:
205 // otherwise, wait for either a response or a timeout
206 select {
207 case <-response.ch:
208 err = response.err
209 case <-timedOut:
210 err = status.Error(codes.Aborted, "timeout")
khenaidoo2c6a0992019-04-29 13:46:56 -0400211 }
khenaidoo2c6a0992019-04-29 13:46:56 -0400212 }
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500213 gotError = gotError || err != nil
214 errors = append(errors, err)
khenaidoo2c6a0992019-04-29 13:46:56 -0400215 }
216
Kent Hagerman8da2f1e2019-11-25 17:28:09 -0500217 if gotError {
khenaidoo2c6a0992019-04-29 13:46:56 -0400218 return errors
219 }
220 return nil
221}