blob: 07327b42dc4096476c5bdc637a829f45eedbdb3f [file] [log] [blame]
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +02001/*
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/*
20Timer 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'
Anssi Mannila8046c702020-01-02 13:39:05 +020024 4) tryCount uint64 'tryCount'
25 5) timerFunction func(string, int) 'function to be executed when timer expires'
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +020026
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
Anssi Mannila8046c702020-01-02 13:39:05 +020029 "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.
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +020033
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
Anssi Mannila8046c702020-01-02 13:39:05 +020057 var tryCount uint64 = 1
58 timerMap.StartTimer("RIC_SUB_REQ", int(subId), subReqTime, FirstTry, handleSubscriptionRequestTimer)
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +020059 timerMap.StopTimer("RIC_SUB_REQ", int(subId))
Juha Hyttinenff8dccd2019-12-10 14:34:07 +020060
Anssi Mannila8046c702020-01-02 13:39:05 +020061
62 StartTimer() retry example.
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +020063 2)
64 subReqTime := 2 * time.Second
Anssi Mannila8046c702020-01-02 13:39:05 +020065 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))
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +020069
70 3)
71 subReqTime := 2 * time.Second
72 strId := "1UHSUwNqxiVgUWXvC4zFaatpZFF"
Anssi Mannila8046c702020-01-02 13:39:05 +020073 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)
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +020082 timerMap.timerMap.StopTimer("RIC_SUB_REQ_" + strId, 0)
83
84 Timer function example. This is run if any of the above started timer expires.
Anssi Mannila8046c702020-01-02 13:39:05 +020085 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
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +020096 }
97*/
98
99package control
100
101import (
102 "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
103 "sync"
104 "time"
105)
106
Anssi Mannila8046c702020-01-02 13:39:05 +0200107const FirstTry = 1
108
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +0200109type TimerKey struct {
Juha Hyttinenff8dccd2019-12-10 14:34:07 +0200110 strId string
111 nbrId int
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +0200112}
113
114type TimerInfo struct {
Juha Hyttinenff8dccd2019-12-10 14:34:07 +0200115 timerAddress *time.Timer
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +0200116 timerFunctionAddress func()
117}
118
119type TimerMap struct {
Juha Hyttinenff8dccd2019-12-10 14:34:07 +0200120 timer map[TimerKey]TimerInfo
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +0200121 mutex sync.Mutex
122}
123
124// This method should run as a constructor
125func (t *TimerMap) Init() {
Juha Hyttinenff8dccd2019-12-10 14:34:07 +0200126 t.timer = make(map[TimerKey]TimerInfo)
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +0200127}
128
Anssi Mannila8046c702020-01-02 13:39:05 +0200129func (t *TimerMap) StartTimer(strId string, nbrId int, expireAfterTime time.Duration, tryCount uint64, timerFunction func(srtId string, nbrId int, tryCount uint64)) bool {
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +0200130 t.mutex.Lock()
131 defer t.mutex.Unlock()
Juha Hyttinenff8dccd2019-12-10 14:34:07 +0200132 if timerFunction == nil {
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +0200133 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
Anssi Mannila8046c702020-01-02 13:39:05 +0200147 // Store in timerMap in-build Go "timer", timer function executor and the function to be executed when the timer expires
Juha Hyttinenff8dccd2019-12-10 14:34:07 +0200148 t.timer[timerKey] = TimerInfo{timerAddress: time.AfterFunc(expireAfterTime, func() { t.timerFunctionExecutor(strId, nbrId) }),
Anssi Mannila8046c702020-01-02 13:39:05 +0200149 timerFunctionAddress: func() { timerFunction(strId, nbrId, tryCount) }}
Anssi Mannilaf1d0eb62019-12-17 15:29:55 +0200150 return true
151}
152
153func (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
172func (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}