shithub: hugo

Download patch

ref: aedb13b2195fbc0d535d366e5b2fa6369b11677c
parent: f6c3ca8b2aba93945490d46e802667e98c68a398
author: Cameron Moore <[email protected]>
date: Sun Mar 13 19:17:42 EDT 2016

tpl: Fix race condition in regexp cache

Protect regular expression cache with a mutex.

Fixes #1973

--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -33,6 +33,7 @@
 	"sort"
 	"strconv"
 	"strings"
+	"sync"
 	"time"
 	"unicode/utf8"
 
@@ -1244,8 +1245,43 @@
 	return strings.Replace(aStr, bStr, cStr, -1), nil
 }
 
-var regexpCache = make(map[string]*regexp.Regexp)
+// regexpCache represents a cache of regexp objects protected by a mutex.
+type regexpCache struct {
+	mu sync.RWMutex
+	re map[string]*regexp.Regexp
+}
 
+// Get retrieves a regexp object from the cache based upon the pattern.
+// If the pattern is not found in the cache, create one
+func (rc *regexpCache) Get(pattern string) (re *regexp.Regexp, err error) {
+	var ok bool
+
+	if re, ok = rc.get(pattern); !ok {
+		re, err = regexp.Compile(pattern)
+		if err != nil {
+			return nil, err
+		}
+		rc.set(pattern, re)
+	}
+
+	return re, nil
+}
+
+func (rc *regexpCache) get(key string) (re *regexp.Regexp, ok bool) {
+	rc.mu.RLock()
+	re, ok = rc.re[key]
+	rc.mu.RUnlock()
+	return
+}
+
+func (rc *regexpCache) set(key string, re *regexp.Regexp) {
+	rc.mu.Lock()
+	rc.re[key] = re
+	rc.mu.Unlock()
+}
+
+var reCache = regexpCache{re: make(map[string]*regexp.Regexp)}
+
 // replaceRE exposes a regular expression replacement function to the templates.
 func replaceRE(pattern, repl, src interface{}) (_ string, err error) {
 	patternStr, err := cast.ToStringE(pattern)
@@ -1263,16 +1299,11 @@
 		return
 	}
 
-	if _, ok := regexpCache[patternStr]; !ok {
-		re, err2 := regexp.Compile(patternStr)
-		if err2 != nil {
-			return "", err2
-		}
-
-		regexpCache[patternStr] = re
+	re, err := reCache.Get(patternStr)
+	if err != nil {
+		return "", err
 	}
-
-	return regexpCache[patternStr].ReplaceAllString(srcStr, replStr), err
+	return re.ReplaceAllString(srcStr, replStr), nil
 }
 
 // dateFormat converts the textual representation of the datetime string into