[core] Add locking to prevent concurrent rule executions

refs #59

ensures counter actions are not triggered concurrently by two persons

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2024-05-01 22:39:18 +02:00
parent a01ce9aa5f
commit 5dd6a5323c
Signed by: luzifer
SSH key fingerprint: SHA256:/xtE5lCgiRDQr8SLxHMS92ZBlACmATUmF1crK16Ks4E
2 changed files with 55 additions and 0 deletions

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"path"
"sync" "sync"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -8,6 +9,7 @@ import (
"gopkg.in/irc.v4" "gopkg.in/irc.v4"
"github.com/Luzifer/go_helpers/v2/fieldcollection" "github.com/Luzifer/go_helpers/v2/fieldcollection"
"github.com/Luzifer/twitch-bot/v3/internal/locker"
"github.com/Luzifer/twitch-bot/v3/plugins" "github.com/Luzifer/twitch-bot/v3/plugins"
) )
@ -79,6 +81,9 @@ func handleMessage(c *irc.Client, m *irc.Message, event *string, eventData *fiel
} }
func handleMessageRuleExecution(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *fieldcollection.FieldCollection) { func handleMessageRuleExecution(c *irc.Client, m *irc.Message, r *plugins.Rule, eventData *fieldcollection.FieldCollection) {
locker.LockByKey(path.Join("rule-execution", r.MatcherID()))
defer locker.UnlockByKey(path.Join("rule-execution", r.MatcherID()))
var ( var (
ruleEventData = fieldcollection.NewFieldCollection() ruleEventData = fieldcollection.NewFieldCollection()
preventCooldown bool preventCooldown bool

50
internal/locker/locker.go Normal file
View file

@ -0,0 +1,50 @@
// Package locker contains a way to interact with arbitrary locks
package locker
import "sync"
var (
locks = map[string]*sync.RWMutex{}
locksOLocks sync.RWMutex
)
// LockByKey takes a key to lock and locks the corresponding RWMutex
func LockByKey(key string) { getLockByKey(key).Lock() }
// RLockByKey takes a key to lock and read-locks the corresponding RWMutex
func RLockByKey(key string) { getLockByKey(key).RLock() }
// RUnlockByKey takes a key to lock and read-unlocks the corresponding RWMutex
func RUnlockByKey(key string) { getLockByKey(key).RUnlock() }
// UnlockByKey takes a key to lock and unlocks the corresponding RWMutex
func UnlockByKey(key string) { getLockByKey(key).Unlock() }
// WithLock takes a key to lock and a function to execute during the
// lock of this key
func WithLock(key string, fn func()) {
LockByKey(key)
defer UnlockByKey(key)
fn()
}
// WithRLock takes a key to lock and a function to execute during the
// read-lock of this key
func WithRLock(key string, fn func()) {
RLockByKey(key)
defer RUnlockByKey(key)
fn()
}
func getLockByKey(key string) *sync.RWMutex {
locksOLocks.Lock()
defer locksOLocks.Unlock()
if locks[key] == nil {
locks[key] = new(sync.RWMutex)
}
return locks[key]
}