From ec003006b008111a9e1b60ccac6c675606b462db Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 14 Dec 2015 16:53:51 +0200 Subject: [PATCH] improved guardian --- engine/cdrs.go | 2 +- engine/guardian.go | 28 ++++++++++++++++++---------- engine/guardian_test.go | 40 +++++++++++++++++++++------------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index 07056d539..c09808467 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -67,7 +67,7 @@ func fsCdrHandler(w http.ResponseWriter, r *http.Request) { } func NewCdrServer(cgrCfg *config.CGRConfig, cdrDb CdrStorage, rater Connector, pubsub PublisherSubscriber, users UserService, aliases AliasService, stats StatsInterface) (*CdrServer, error) { - return &CdrServer{cgrCfg: cgrCfg, cdrDb: cdrDb, rater: rater, pubsub: pubsub, users: users, aliases: aliases, stats: stats, guard: &GuardianLock{queue: make(map[string]chan bool)}}, nil + return &CdrServer{cgrCfg: cgrCfg, cdrDb: cdrDb, rater: rater, pubsub: pubsub, users: users, aliases: aliases, stats: stats, guard: &GuardianLock{locksMap: make(map[string]chan bool)}}, nil } type CdrServer struct { diff --git a/engine/guardian.go b/engine/guardian.go index 0e11cff67..dc2975aff 100644 --- a/engine/guardian.go +++ b/engine/guardian.go @@ -24,28 +24,35 @@ import ( ) // global package variable -var Guardian = &GuardianLock{queue: make(map[string]chan bool)} +var Guardian = &GuardianLock{locksMap: make(map[string]chan bool)} func NewGuardianLock() *GuardianLock { - return &GuardianLock{queue: make(map[string]chan bool)} + return &GuardianLock{locksMap: make(map[string]chan bool)} } type GuardianLock struct { - queue map[string]chan bool - mu sync.Mutex + locksMap map[string]chan bool + mu sync.RWMutex } func (cm *GuardianLock) Guard(handler func() (interface{}, error), timeout time.Duration, names ...string) (reply interface{}, err error) { + var locks []chan bool // take existing locks out of the mutex cm.mu.Lock() for _, name := range names { - lock, exists := Guardian.queue[name] - if !exists { + if lock, exists := Guardian.locksMap[name]; !exists { lock = make(chan bool, 1) - Guardian.queue[name] = lock + Guardian.locksMap[name] = lock + lock <- true + } else { + locks = append(locks, lock) } - lock <- true } cm.mu.Unlock() + + for _, lock := range locks { + lock <- true + } + funcWaiter := make(chan bool) go func() { // execute @@ -62,9 +69,10 @@ func (cm *GuardianLock) Guard(handler func() (interface{}, error), timeout time. <-funcWaiter } // release + cm.mu.RLock() for _, name := range names { - lock := Guardian.queue[name] - <-lock + <-Guardian.locksMap[name] } + cm.mu.RUnlock() return } diff --git a/engine/guardian_test.go b/engine/guardian_test.go index 402f12d66..a8e3cc4f8 100644 --- a/engine/guardian_test.go +++ b/engine/guardian_test.go @@ -25,23 +25,25 @@ import ( ) func ATestAccountLock(t *testing.T) { - go Guardian.Guard(func() (interface{}, error) { - log.Print("first 1") - time.Sleep(1 * time.Second) - log.Print("end first 1") - return 0, nil - }, 0, "1") - go Guardian.Guard(func() (interface{}, error) { - log.Print("first 2") - time.Sleep(1 * time.Second) - log.Print("end first 2") - return 0, nil - }, 0, "2") - go Guardian.Guard(func() (interface{}, error) { - log.Print("second 1") - time.Sleep(1 * time.Second) - log.Print("end second 1") - return 0, nil - }, 0, "1") - time.Sleep(3 * time.Second) + for i := 0; i < 100; i++ { + go Guardian.Guard(func() (interface{}, error) { + log.Print("first 1") + time.Sleep(1 * time.Millisecond) + log.Print("end first 1") + return 0, nil + }, 0, "1") + go Guardian.Guard(func() (interface{}, error) { + log.Print("first 2") + time.Sleep(1 * time.Millisecond) + log.Print("end first 2") + return 0, nil + }, 0, "2") + go Guardian.Guard(func() (interface{}, error) { + log.Print("second 1") + time.Sleep(1 * time.Millisecond) + log.Print("end second 1") + return 0, nil + }, 0, "1") + } + time.Sleep(10 * time.Second) }