| /* |
| ================================================================================== |
| Copyright (c) 2019 AT&T Intellectual Property. |
| Copyright (c) 2019 Nokia |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ================================================================================== |
| */ |
| /* |
| Timer takes four parameters: |
| 1) strId string 'string format timerMap key' |
| 2) nbrId int 'numeric format timerMap key' |
| 3) timerDuration time.Duration 'timer duration' |
| 4) tryCount uint64 'tryCount' |
| 5) timerFunction func(string, int) 'function to be executed when timer expires' |
| |
| Timer function is put inside in-build time.AfterFunc() Go function, where it is run inside own Go routine |
| when the timer expires. Timer are two key values. Both are used always, but the other one can be left |
| "empty", i.e. strId = "" or nbrId = 0. Fourth parameter is for tryCount. Fifth parameter, the timer |
| function is bare function name without any function parameters and parenthesis! Filling first parameter |
| strId with related name can improve code readability and robustness, even the numeric Id would be enough |
| from functionality point of view. |
| |
| TimerStart() function starts the timer. If TimerStart() function is called again with same key values |
| while earlier started timer is still in the timerMap, i.e. it has not been stopped or the timer has not |
| yet expired, the old timer is deleted and new timer is started with the given time value. |
| |
| StopTimer() function stops the timer. There is no need to call StopTimer() function after the timer has |
| expired. Timer is removed automatically from the timeMap. Calling StopTimer() function with key values not |
| existing in the timerMap, has no effect. |
| |
| NOTE: Each timer is run in separate Go routine. Therefore, the function that is executed when timer expires |
| MUST be designed to be able run concurrently! Also, function run order of simultaneously expired timers cannot |
| guaranteed anyway! |
| |
| If you need to transport more information to the timer function, consider to use another map to store the |
| information with same key value, as the started timer. |
| |
| Init timerMap example: |
| timerMap := new(TimerMap) |
| timerMap.Init() |
| |
| StartTimer() and StartTimer() function usage examples. |
| 1) |
| subReqTime := 2 * time.Second |
| subId := 123 |
| var tryCount uint64 = 1 |
| timerMap.StartTimer("RIC_SUB_REQ", int(subId), subReqTime, FirstTry, handleSubscriptionRequestTimer) |
| timerMap.StopTimer("RIC_SUB_REQ", int(subId)) |
| |
| |
| StartTimer() retry example. |
| 2) |
| subReqTime := 2 * time.Second |
| subId := 123 |
| var tryCount uint64 = 1 |
| timerMap.StartTimer("RIC_SUB_REQ", int(subId), subReqTime, FirstTry, handleSubscriptionRequestTimer) |
| timerMap.StopTimer("RIC_SUB_REQ", int(subId)) |
| |
| 3) |
| subReqTime := 2 * time.Second |
| strId := "1UHSUwNqxiVgUWXvC4zFaatpZFF" |
| var tryCount uint64 = 1 |
| timerMap.StartTimer(strId, 0, subReqTime, FirstTry, handleSubscriptionRequestTimer) |
| timerMap.StopTimer(strId, 0) |
| |
| 4) |
| subReqTime := 2 * time.Second |
| strId := "1UHSUwNqxiVgUWXvC4zFaatpZFF" |
| var tryCount uint64 = 1 |
| timerMap.StartTimer(RIC_SUB_REQ_" + strId, 0, subReqTime, FirstTry, handleSubscriptionRequestTimer) |
| timerMap.timerMap.StopTimer("RIC_SUB_REQ_" + strId, 0) |
| |
| Timer function example. This is run if any of the above started timer expires. |
| func handleSubscriptionRequestTimer1(strId string, nbrId int, tryCount uint64) { |
| fmt.Printf("Subscription Request timer expired. Name: %v, SubId: %v, tryCount: %v\n",strId, nbrId, tryCount) |
| ... |
| |
| // Retry |
| .... |
| |
| tryCount++ |
| timerMap.StartTimer("RIC_SUB_REQ", int(subId), subReqTime, tryCount, handleSubscriptionRequestTimer) |
| ... |
| |
| } |
| */ |
| |
| package control |
| |
| import ( |
| "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp" |
| "sync" |
| "time" |
| ) |
| |
| const FirstTry = 1 |
| |
| type TimerKey struct { |
| strId string |
| nbrId int |
| } |
| |
| type TimerInfo struct { |
| timerAddress *time.Timer |
| timerFunctionAddress func() |
| } |
| |
| type TimerMap struct { |
| timer map[TimerKey]TimerInfo |
| mutex sync.Mutex |
| } |
| |
| // This method should run as a constructor |
| func (t *TimerMap) Init() { |
| t.timer = make(map[TimerKey]TimerInfo) |
| } |
| |
| func (t *TimerMap) StartTimer(strId string, nbrId int, expireAfterTime time.Duration, tryCount uint64, timerFunction func(srtId string, nbrId int, tryCount uint64)) bool { |
| t.mutex.Lock() |
| defer t.mutex.Unlock() |
| if timerFunction == nil { |
| xapp.Logger.Error("StartTimer() timerFunc == nil\n") |
| return false |
| } |
| timerKey := TimerKey{strId, nbrId} |
| // Stop timer if there is already timer running with the same id |
| if val, ok := t.timer[timerKey]; ok { |
| xapp.Logger.Debug("StartTimer() old timer found") |
| if val.timerAddress != nil { |
| xapp.Logger.Debug("StartTimer() deleting old timer") |
| val.timerAddress.Stop() |
| } |
| delete(t.timer, timerKey) |
| } |
| |
| // Store in timerMap in-build Go "timer", timer function executor and the function to be executed when the timer expires |
| t.timer[timerKey] = TimerInfo{timerAddress: time.AfterFunc(expireAfterTime, func() { t.timerFunctionExecutor(strId, nbrId) }), |
| timerFunctionAddress: func() { timerFunction(strId, nbrId, tryCount) }} |
| return true |
| } |
| |
| func (t *TimerMap) StopTimer(strId string, nbrId int) bool { |
| t.mutex.Lock() |
| defer t.mutex.Unlock() |
| timerKey := TimerKey{strId, nbrId} |
| if val, ok := t.timer[timerKey]; ok { |
| if val.timerAddress != nil { |
| val.timerAddress.Stop() |
| delete(t.timer, timerKey) |
| return true |
| } else { |
| xapp.Logger.Error("StopTimer() timerAddress == nil") |
| return false |
| } |
| } else { |
| xapp.Logger.Debug("StopTimer() Timer not found. May be expired or stopped already. timerKey.strId: %v, timerKey.strId: %v\n", timerKey.strId, timerKey.nbrId) |
| return false |
| } |
| } |
| |
| func (t *TimerMap) timerFunctionExecutor(strId string, nbrId int) { |
| t.mutex.Lock() |
| timerKey := TimerKey{strId, nbrId} |
| if val, ok := t.timer[timerKey]; ok { |
| if val.timerFunctionAddress != nil { |
| // Take local copy of timer function address |
| f := val.timerFunctionAddress |
| // Delete timer instance from map |
| delete(t.timer, timerKey) |
| t.mutex.Unlock() |
| // Execute the timer function |
| f() |
| return |
| } else { |
| xapp.Logger.Error("timerExecutorFunc() timerFunctionAddress == nil") |
| t.mutex.Unlock() |
| return |
| } |
| } else { |
| xapp.Logger.Error("timerExecutorFunc() Timer is not anymore in map. timerKey.strId: %v, timerKey.strId: %v\n", timerKey.strId, timerKey.nbrId) |
| t.mutex.Unlock() |
| return |
| } |
| } |