RICPLT-2801, RICPLT-2802
[ric-plt/submgr.git] / pkg / control / timer.go
1 /*
2 ==================================================================================
3   Copyright (c) 2019 AT&T Intellectual Property.
4   Copyright (c) 2019 Nokia
5
6    Licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8    You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17 ==================================================================================
18 */
19 /*
20 Timer takes four parameters:
21          1) strId                       string                          'string format timerMap key'
22          2) nbrId                       int                             'numeric format timerMap key'
23          3) timerDuration       time.Duration           'timer duration'
24          4) tryCount        uint64              'tryCount'
25          5) timerFunction       func(string, int)       'function to be executed when timer expires'
26
27         Timer function is put inside in-build time.AfterFunc() Go function, where it is run inside own Go routine
28         when the timer expires. Timer are two key values. Both are used always, but the other one can be left
29         "empty", i.e. strId = "" or  nbrId = 0. Fourth parameter is for tryCount. Fifth parameter, the timer
30         function is bare function name without  any function parameters and parenthesis! Filling first parameter
31         strId with related name can improve code readability and robustness, even the numeric Id would be enough
32         from functionality point of view.
33
34         TimerStart() function starts the timer. If TimerStart() function is called again with same key values
35         while earlier started timer is still in the timerMap, i.e. it has not been stopped or the timer has not
36         yet expired, the old timer is deleted and new timer is started with the given time value.
37
38         StopTimer() function stops the timer. There is no need to call StopTimer() function after the timer has
39         expired. Timer is removed automatically from the timeMap. Calling StopTimer() function with key values not
40         existing in the timerMap, has no effect.
41
42         NOTE: Each timer is run in separate Go routine. Therefore, the function that is executed when timer expires
43         MUST be designed to be able run concurrently! Also, function run order of simultaneously expired timers cannot
44         guaranteed anyway!
45
46         If you need to transport more information to the timer function, consider to use another map to store the
47         information with same key value, as the started timer.
48
49         Init timerMap example:
50                 timerMap := new(TimerMap)
51                 timerMap.Init()
52
53         StartTimer() and StartTimer() function usage examples.
54         1)
55                 subReqTime := 2 * time.Second
56                 subId := 123
57                 var tryCount uint64 = 1
58                 timerMap.StartTimer("RIC_SUB_REQ", int(subId), subReqTime, FirstTry, handleSubscriptionRequestTimer)
59                 timerMap.StopTimer("RIC_SUB_REQ", int(subId))
60
61
62         StartTimer() retry example.
63         2)
64                 subReqTime := 2 * time.Second
65                 subId := 123
66                 var tryCount uint64 = 1
67                 timerMap.StartTimer("RIC_SUB_REQ", int(subId), subReqTime, FirstTry, handleSubscriptionRequestTimer)
68                 timerMap.StopTimer("RIC_SUB_REQ", int(subId))
69
70         3)
71                 subReqTime := 2 * time.Second
72                 strId := "1UHSUwNqxiVgUWXvC4zFaatpZFF"
73                 var tryCount uint64 = 1
74                 timerMap.StartTimer(strId, 0, subReqTime, FirstTry, handleSubscriptionRequestTimer)
75                 timerMap.StopTimer(strId, 0)
76
77         4)
78                 subReqTime := 2 * time.Second
79                 strId := "1UHSUwNqxiVgUWXvC4zFaatpZFF"
80                 var tryCount uint64 = 1
81                 timerMap.StartTimer(RIC_SUB_REQ_" + strId, 0, subReqTime, FirstTry, handleSubscriptionRequestTimer)
82                 timerMap.timerMap.StopTimer("RIC_SUB_REQ_" + strId, 0)
83
84         Timer function example. This is run if any of the above started timer expires.
85                 func handleSubscriptionRequestTimer1(strId string, nbrId int, tryCount uint64) {
86                         fmt.Printf("Subscription Request timer expired. Name: %v, SubId: %v, tryCount: %v\n",strId, nbrId, tryCount)
87                         ...
88
89                         // Retry
90                         ....
91
92                         tryCount++
93                     timerMap.StartTimer("RIC_SUB_REQ", int(subId), subReqTime, tryCount, handleSubscriptionRequestTimer)
94                         ...
95
96                 }
97 */
98
99 package control
100
101 import (
102         "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
103         "sync"
104         "time"
105 )
106
107 const FirstTry = 1
108
109 type TimerKey struct {
110         strId string
111         nbrId int
112 }
113
114 type TimerInfo struct {
115         timerAddress         *time.Timer
116         timerFunctionAddress func()
117 }
118
119 type TimerMap struct {
120         timer map[TimerKey]TimerInfo
121         mutex sync.Mutex
122 }
123
124 // This method should run as a constructor
125 func (t *TimerMap) Init() {
126         t.timer = make(map[TimerKey]TimerInfo)
127 }
128
129 func (t *TimerMap) StartTimer(strId string, nbrId int, expireAfterTime time.Duration, tryCount uint64, timerFunction func(srtId string, nbrId int, tryCount uint64)) bool {
130         t.mutex.Lock()
131         defer t.mutex.Unlock()
132         if timerFunction == nil {
133                 xapp.Logger.Error("StartTimer() timerFunc == nil\n")
134                 return false
135         }
136         timerKey := TimerKey{strId, nbrId}
137         // Stop timer if there is already timer running with the same id
138         if val, ok := t.timer[timerKey]; ok {
139                 xapp.Logger.Debug("StartTimer() old timer found")
140                 if val.timerAddress != nil {
141                         xapp.Logger.Debug("StartTimer() deleting old timer")
142                         val.timerAddress.Stop()
143                 }
144                 delete(t.timer, timerKey)
145         }
146
147         // Store in timerMap in-build Go "timer", timer function executor and the function to be executed when the timer expires
148         t.timer[timerKey] = TimerInfo{timerAddress: time.AfterFunc(expireAfterTime, func() { t.timerFunctionExecutor(strId, nbrId) }),
149                 timerFunctionAddress: func() { timerFunction(strId, nbrId, tryCount) }}
150         return true
151 }
152
153 func (t *TimerMap) StopTimer(strId string, nbrId int) bool {
154         t.mutex.Lock()
155         defer t.mutex.Unlock()
156         timerKey := TimerKey{strId, nbrId}
157         if val, ok := t.timer[timerKey]; ok {
158                 if val.timerAddress != nil {
159                         val.timerAddress.Stop()
160                         delete(t.timer, timerKey)
161                         return true
162                 } else {
163                         xapp.Logger.Error("StopTimer() timerAddress == nil")
164                         return false
165                 }
166         } else {
167                 xapp.Logger.Debug("StopTimer() Timer not found. May be expired or stopped already. timerKey.strId: %v, timerKey.strId: %v\n", timerKey.strId, timerKey.nbrId)
168                 return false
169         }
170 }
171
172 func (t *TimerMap) timerFunctionExecutor(strId string, nbrId int) {
173         t.mutex.Lock()
174         timerKey := TimerKey{strId, nbrId}
175         if val, ok := t.timer[timerKey]; ok {
176                 if val.timerFunctionAddress != nil {
177                         // Take local copy of timer function address
178                         f := val.timerFunctionAddress
179                         // Delete timer instance from map
180                         delete(t.timer, timerKey)
181                         t.mutex.Unlock()
182                         // Execute the timer function
183                         f()
184                         return
185                 } else {
186                         xapp.Logger.Error("timerExecutorFunc() timerFunctionAddress == nil")
187                         t.mutex.Unlock()
188                         return
189                 }
190         } else {
191                 xapp.Logger.Error("timerExecutorFunc() Timer is not anymore in map. timerKey.strId: %v, timerKey.strId: %v\n", timerKey.strId, timerKey.nbrId)
192                 t.mutex.Unlock()
193                 return
194         }
195 }