c8385a6920b0458e1926eaade743ce5704276541
[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) timerFunction       func(string, int)       'function to be executed when timer expires'
25
26         Timer function is put inside in-build time.AfterFunc() Go function, where it is run inside own Go routine
27         when the timer expires. Timer are two key values. Both are used always, but the other one can be left
28         "empty", i.e. strId = "" or  nbrId = 0. Fourth parameter, the timer function is bare function name without
29         any function parameters and parenthesis! Filling first parameter strId with related name can improve
30         code readability and robustness, even the numeric Id would be enough from functionality point of view.
31
32         TimerStart() function starts the timer. If TimerStart() function is called again with same key values
33         while earlier started timer is still in the timerMap, i.e. it has not been stopped or the timer has not
34         yet expired, the old timer is deleted and new timer is started with the given time value.
35
36         StopTimer() function stops the timer. There is no need to call StopTimer() function after the timer has
37         expired. Timer is removed automatically from the timeMap. Calling StopTimer() function with key values not
38         existing in the timerMap, has no effect.
39
40         NOTE: Each timer is run in separate Go routine. Therefore, the function that is executed when timer expires
41         MUST be designed to be able run concurrently! Also, function run order of simultaneously expired timers cannot
42         guaranteed anyway!
43
44         If you need to transport more information to the timer function, consider to use another map to store the
45         information with same key value, as the started timer.
46
47         Init timerMap example:
48                 timerMap := new(TimerMap)
49                 timerMap.Init()
50
51         StartTimer() and StartTimer() function usage examples.
52         1)
53                 subReqTime := 2 * time.Second
54                 subId := 123
55                 timerMap.StartTimer("RIC_SUB_REQ", int(subId), subReqTime, handleSubscriptionRequestTimer)
56                 timerMap.StopTimer("RIC_SUB_REQ", int(subId))
57
58         2)
59                 subReqTime := 2 * time.Second
60                 strId := "1UHSUwNqxiVgUWXvC4zFaatpZFF"
61                 timerMap.StartTimer(strId, 0, subReqTime, handleSubscriptionRequestTimer)
62                 timerMap.StopTimer(strId, 0)
63
64         3)
65                 subReqTime := 2 * time.Second
66                 strId := "1UHSUwNqxiVgUWXvC4zFaatpZFF"
67                 timerMap.StartTimer(RIC_SUB_REQ_" + strId, 0, subReqTime, handleSubscriptionRequestTimer)
68                 timerMap.timerMap.StopTimer("RIC_SUB_REQ_" + strId, 0)
69
70         Timer function example. This is run if any of the above started timer expires.
71                 func handleSubscriptionRequestTimer1(strId string, nbrId int) {
72                         fmt.Printf("Subscription Request timer expired. Name: %v, SubId: %v\n",strId, nbrId)
73                 }
74 */
75
76 package control
77
78 import (
79         "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
80         "sync"
81         "time"
82 )
83
84 type TimerKey struct {
85         strId string
86         nbrId int
87 }
88
89 type TimerInfo struct {
90         timerAddress         *time.Timer
91         timerFunctionAddress func()
92 }
93
94 type TimerMap struct {
95         timer map[TimerKey]TimerInfo
96         mutex sync.Mutex
97 }
98
99 // This method should run as a constructor
100 func (t *TimerMap) Init() {
101         t.timer = make(map[TimerKey]TimerInfo)
102 }
103
104 func (t *TimerMap) StartTimer(strId string, nbrId int, expireAfterTime time.Duration, timerFunction func(srtId string, nbrId int)) bool {
105         t.mutex.Lock()
106         defer t.mutex.Unlock()
107         if timerFunction == nil {
108                 xapp.Logger.Error("StartTimer() timerFunc == nil\n")
109                 return false
110         }
111         timerKey := TimerKey{strId, nbrId}
112         // Stop timer if there is already timer running with the same id
113         if val, ok := t.timer[timerKey]; ok {
114                 xapp.Logger.Debug("StartTimer() old timer found")
115                 if val.timerAddress != nil {
116                         xapp.Logger.Debug("StartTimer() deleting old timer")
117                         val.timerAddress.Stop()
118                 }
119                 delete(t.timer, timerKey)
120         }
121
122         // Store in timerMap in-build Go "timer", timer function executor, and the function to be executed when the timer expires
123         t.timer[timerKey] = TimerInfo{timerAddress: time.AfterFunc(expireAfterTime, func() { t.timerFunctionExecutor(strId, nbrId) }),
124                 timerFunctionAddress: func() { timerFunction(strId, nbrId) }}
125         return true
126 }
127
128 func (t *TimerMap) StopTimer(strId string, nbrId int) bool {
129         t.mutex.Lock()
130         defer t.mutex.Unlock()
131         timerKey := TimerKey{strId, nbrId}
132         if val, ok := t.timer[timerKey]; ok {
133                 if val.timerAddress != nil {
134                         val.timerAddress.Stop()
135                         delete(t.timer, timerKey)
136                         return true
137                 } else {
138                         xapp.Logger.Error("StopTimer() timerAddress == nil")
139                         return false
140                 }
141         } else {
142                 xapp.Logger.Debug("StopTimer() Timer not found. May be expired or stopped already. timerKey.strId: %v, timerKey.strId: %v\n", timerKey.strId, timerKey.nbrId)
143                 return false
144         }
145 }
146
147 func (t *TimerMap) timerFunctionExecutor(strId string, nbrId int) {
148         t.mutex.Lock()
149         timerKey := TimerKey{strId, nbrId}
150         if val, ok := t.timer[timerKey]; ok {
151                 if val.timerFunctionAddress != nil {
152                         // Take local copy of timer function address
153                         f := val.timerFunctionAddress
154                         // Delete timer instance from map
155                         delete(t.timer, timerKey)
156                         t.mutex.Unlock()
157                         // Execute the timer function
158                         f()
159                         return
160                 } else {
161                         xapp.Logger.Error("timerExecutorFunc() timerFunctionAddress == nil")
162                         t.mutex.Unlock()
163                         return
164                 }
165         } else {
166                 xapp.Logger.Error("timerExecutorFunc() Timer is not anymore in map. timerKey.strId: %v, timerKey.strId: %v\n", timerKey.strId, timerKey.nbrId)
167                 t.mutex.Unlock()
168                 return
169         }
170 }