1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-28 15:27:43 +00:00

memory: Use a string pool for Commands

This commit is contained in:
Kane York 2016-01-17 17:45:37 -08:00
parent c85e8b10c3
commit abb032f0c1
5 changed files with 65 additions and 6 deletions

View file

@ -62,7 +62,7 @@ func getCacheKey(remoteCommand, data string) string {
return fmt.Sprintf("%s/%s", remoteCommand, data)
}
// HBackendPublishRequest handles the /uncached_pub route.
// HTTPBackendUncachedPublish handles the /uncached_pub route.
// The backend can POST here to publish a message to clients with no caching.
// The POST arguments are `cmd`, `args`, `channel`, and `scope`.
// The `scope` argument is required because no attempt is made to infer the scope from the command, unlike /cached_pub.
@ -93,7 +93,7 @@ func HTTPBackendUncachedPublish(w http.ResponseWriter, r *http.Request) {
return
}
cm := ClientMessage{MessageID: -1, Command: Command(cmd), origArguments: json}
cm := ClientMessage{MessageID: -1, Command: CommandPool.Intern(cmd), origArguments: json}
cm.parseOrigArguments()
var count int
@ -219,7 +219,7 @@ type ErrBackendNotOK struct {
Code int
}
// Implements the error interface.
// Error Implements the error interface.
func (noe ErrBackendNotOK) Error() string {
return fmt.Sprintf("backend returned %d: %s", noe.Code, noe.Response)
}

View file

@ -41,6 +41,25 @@ var commandHandlers = map[Command]CommandHandler{
"user_history": C2SHandleRemoteCommand,
}
func internCommands() {
CommandPool = NewStringPool()
CommandPool._Intern_Setup(HelloCommand)
CommandPool._Intern_Setup("ping")
CommandPool._Intern_Setup(SetUserCommand)
CommandPool._Intern_Setup(ReadyCommand)
CommandPool._Intern_Setup("sub")
CommandPool._Intern_Setup("unsub")
CommandPool._Intern_Setup("track_follow")
CommandPool._Intern_Setup("emoticon_uses")
CommandPool._Intern_Setup("twitch_emote")
CommandPool._Intern_Setup("get_link")
CommandPool._Intern_Setup("get_display_name")
CommandPool._Intern_Setup("update_follow_buttons")
CommandPool._Intern_Setup("chat_history")
CommandPool._Intern_Setup("user_history")
CommandPool._Intern_Setup("adjacent_history")
}
// DispatchC2SCommand handles a C2S Command in the provided ClientMessage.
// It calls the correct CommandHandler function, catching panics.
// It sends either the returned Reply ClientMessage, setting the correct messageID, or sends an ErrorCommand

View file

@ -60,6 +60,8 @@ var Configuration *ConfigFile
var janitorsOnce sync.Once
var CommandPool StringPool
// SetupServerAndHandle starts all background goroutines and registers HTTP listeners on the given ServeMux.
// Essentially, this function completely preps the server for a http.ListenAndServe call.
// (Uses http.DefaultServeMux if `serveMux` is nil.)
@ -115,6 +117,7 @@ func SetupServerAndHandle(config *ConfigFile, serveMux *http.ServeMux) {
// startJanitors starts the 'is_init_func' goroutines
func startJanitors() {
loadUniqueUsers()
internCommands()
go authorizationJanitor()
go bunchCacheJanitor()
@ -508,11 +511,11 @@ func UnmarshalClientMessage(data []byte, payloadType int, v interface{}) (err er
spaceIdx = strings.IndexRune(dataStr, ' ')
if spaceIdx == -1 {
out.Command = Command(dataStr)
out.Command = CommandPool.Intern(dataStr)
out.Arguments = nil
return nil
} else {
out.Command = Command(dataStr[:spaceIdx])
out.Command = CommandPool.Intern(dataStr[:spaceIdx])
}
dataStr = dataStr[spaceIdx+1:]
argumentsJSON := dataStr

View file

@ -0,0 +1,37 @@
package server
import (
"sync"
)
type StringPool struct {
sync.RWMutex
lookup map[string]Command
}
func NewStringPool() *StringPool {
return &StringPool{lookup: make(map[string]Command)}
}
// doesn't lock, doesn't check for dupes.
func (p *StringPool) _Intern_Setup(s string) {
p.lookup[s] = Command(s)
}
func (p *StringPool) Intern(s string) Command {
p.RLock()
ss, exists := p.lookup[s]
p.RUnlock()
if exists {
return ss
}
p.Lock()
defer p.Unlock()
ss, exists = p.lookup[s]
if exists {
return ss
}
p.lookup[s] = Command(string([]byte(s)))
return s
}

View file

@ -236,7 +236,7 @@ func HTTPBackendCachedPublish(w http.ResponseWriter, r *http.Request) {
return
}
cmd := Command(formData.Get("cmd"))
cmd := CommandPool.Intern(formData.Get("cmd"))
json := formData.Get("args")
channel := formData.Get("channel")
deleteMode := formData.Get("delete") != ""