/* ================================================================================== 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) 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, 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 timerMap.StartTimer("RIC_SUB_REQ", int(subId), subReqTime, handleSubscriptionRequestTimer) timerMap.StopTimer("RIC_SUB_REQ", int(subId)) 2) subReqTime := 2 * time.Second strId := "1UHSUwNqxiVgUWXvC4zFaatpZFF" timerMap.StartTimer(strId, 0, subReqTime, handleSubscriptionRequestTimer) timerMap.StopTimer(strId, 0) 3) subReqTime := 2 * time.Second strId := "1UHSUwNqxiVgUWXvC4zFaatpZFF" timerMap.StartTimer(RIC_SUB_REQ_" + strId, 0, subReqTime, 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) { fmt.Printf("Subscription Request timer expired. Name: %v, SubId: %v\n",strId, nbrId) } */ package control import ( "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp" "sync" "time" ) 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, timerFunction func(srtId string, nbrId int)) 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)}} 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 } }