fix concurrent map write issue by init all regexp when startup

This commit is contained in:
xiaofan 2016-11-28 20:30:52 +08:00
parent f0984319b4
commit 3462c6a153
1 changed files with 54 additions and 39 deletions

View File

@ -19,6 +19,7 @@ import (
"regexp" "regexp"
"sort" "sort"
"strings" "strings"
"sync"
) )
/******************************************************************************/ /******************************************************************************/
@ -107,15 +108,17 @@ func atoi(s string) int {
} }
type fieldDescriptor struct { type fieldDescriptor struct {
name string name string
min, max int min, max int
defaultList []int defaultList []int
valuePattern string valuePattern string
atoi func(string) int atoi func(string) int
layoutInitOnce sync.Once
layoutMap map[string]*regexp.Regexp
} }
var ( var (
secondDescriptor = fieldDescriptor{ secondDescriptor = &fieldDescriptor{
name: "second", name: "second",
min: 0, min: 0,
max: 59, max: 59,
@ -123,7 +126,7 @@ var (
valuePattern: `0?[0-9]|[1-5][0-9]`, valuePattern: `0?[0-9]|[1-5][0-9]`,
atoi: atoi, atoi: atoi,
} }
minuteDescriptor = fieldDescriptor{ minuteDescriptor = &fieldDescriptor{
name: "minute", name: "minute",
min: 0, min: 0,
max: 59, max: 59,
@ -131,7 +134,7 @@ var (
valuePattern: `0?[0-9]|[1-5][0-9]`, valuePattern: `0?[0-9]|[1-5][0-9]`,
atoi: atoi, atoi: atoi,
} }
hourDescriptor = fieldDescriptor{ hourDescriptor = &fieldDescriptor{
name: "hour", name: "hour",
min: 0, min: 0,
max: 23, max: 23,
@ -139,7 +142,7 @@ var (
valuePattern: `0?[0-9]|1[0-9]|2[0-3]`, valuePattern: `0?[0-9]|1[0-9]|2[0-3]`,
atoi: atoi, atoi: atoi,
} }
domDescriptor = fieldDescriptor{ domDescriptor = &fieldDescriptor{
name: "day-of-month", name: "day-of-month",
min: 1, min: 1,
max: 31, max: 31,
@ -147,7 +150,7 @@ var (
valuePattern: `0?[1-9]|[12][0-9]|3[01]`, valuePattern: `0?[1-9]|[12][0-9]|3[01]`,
atoi: atoi, atoi: atoi,
} }
monthDescriptor = fieldDescriptor{ monthDescriptor = &fieldDescriptor{
name: "month", name: "month",
min: 1, min: 1,
max: 12, max: 12,
@ -157,7 +160,7 @@ var (
return monthTokens[s] return monthTokens[s]
}, },
} }
dowDescriptor = fieldDescriptor{ dowDescriptor = &fieldDescriptor{
name: "day-of-week", name: "day-of-week",
min: 0, min: 0,
max: 6, max: 6,
@ -167,7 +170,7 @@ var (
return dowTokens[s] return dowTokens[s]
}, },
} }
yearDescriptor = fieldDescriptor{ yearDescriptor = &fieldDescriptor{
name: "year", name: "year",
min: 1970, min: 1970,
max: 2099, max: 2099,
@ -177,6 +180,17 @@ var (
} }
) )
func (f *fieldDescriptor) makeLayoutRegexp(layout string) *regexp.Regexp {
f.layoutInitOnce.Do(func() {
f.layoutMap = make(map[string]*regexp.Regexp)
for _, l := range allLayouts {
realLayoutString := strings.Replace(l, `%value%`, f.valuePattern, -1)
f.layoutMap[l] = regexp.MustCompile(realLayoutString)
}
})
return f.layoutMap[layout]
}
/******************************************************************************/ /******************************************************************************/
var ( var (
@ -193,9 +207,22 @@ var (
layoutDowOfSpecificWeek = `^(%value%)#([1-5])$` layoutDowOfSpecificWeek = `^(%value%)#([1-5])$`
fieldFinder = regexp.MustCompile(`\S+`) fieldFinder = regexp.MustCompile(`\S+`)
entryFinder = regexp.MustCompile(`[^,]+`) entryFinder = regexp.MustCompile(`[^,]+`)
layoutRegexp = make(map[string]*regexp.Regexp)
) )
var allLayouts = []string{
layoutWildcard,
layoutValue,
layoutRange,
layoutWildcardAndInterval,
layoutValueAndInterval,
layoutRangeAndInterval,
layoutLastDom,
layoutWorkdom,
layoutLastWorkdom,
layoutDowOfLastWeek,
layoutDowOfSpecificWeek,
}
/******************************************************************************/ /******************************************************************************/
var cronNormalizer = strings.NewReplacer( var cronNormalizer = strings.NewReplacer(
@ -264,7 +291,7 @@ type cronDirective struct {
send int send int
} }
func genericFieldHandler(s string, desc fieldDescriptor) ([]int, error) { func genericFieldHandler(s string, desc *fieldDescriptor) ([]int, error) {
directives, err := genericFieldParse(s, desc) directives, err := genericFieldParse(s, desc)
if err != nil { if err != nil {
return nil, err return nil, err
@ -302,12 +329,12 @@ func (expr *Expression) dowFieldHandler(s string) error {
sdirective := s[directive.sbeg:directive.send] sdirective := s[directive.sbeg:directive.send]
snormal := strings.ToLower(sdirective) snormal := strings.ToLower(sdirective)
// `5L` // `5L`
pairs := makeLayoutRegexp(layoutDowOfLastWeek, dowDescriptor.valuePattern).FindStringSubmatchIndex(snormal) pairs := dowDescriptor.makeLayoutRegexp(layoutDowOfLastWeek).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 { if len(pairs) > 0 {
populateOne(expr.lastWeekDaysOfWeek, dowDescriptor.atoi(snormal[pairs[2]:pairs[3]])) populateOne(expr.lastWeekDaysOfWeek, dowDescriptor.atoi(snormal[pairs[2]:pairs[3]]))
} else { } else {
// `5#3` // `5#3`
pairs := makeLayoutRegexp(layoutDowOfSpecificWeek, dowDescriptor.valuePattern).FindStringSubmatchIndex(snormal) pairs := dowDescriptor.makeLayoutRegexp(layoutDowOfSpecificWeek).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 { if len(pairs) > 0 {
populateOne(expr.specificWeekDaysOfWeek, (dowDescriptor.atoi(snormal[pairs[4]:pairs[5]])-1)*7+(dowDescriptor.atoi(snormal[pairs[2]:pairs[3]])%7)) populateOne(expr.specificWeekDaysOfWeek, (dowDescriptor.atoi(snormal[pairs[4]:pairs[5]])-1)*7+(dowDescriptor.atoi(snormal[pairs[2]:pairs[3]])%7))
} else { } else {
@ -344,15 +371,15 @@ func (expr *Expression) domFieldHandler(s string) error {
sdirective := s[directive.sbeg:directive.send] sdirective := s[directive.sbeg:directive.send]
snormal := strings.ToLower(sdirective) snormal := strings.ToLower(sdirective)
// `L` // `L`
if makeLayoutRegexp(layoutLastDom, domDescriptor.valuePattern).MatchString(snormal) { if domDescriptor.makeLayoutRegexp(layoutLastDom).MatchString(snormal) {
expr.lastDayOfMonth = true expr.lastDayOfMonth = true
} else { } else {
// `LW` // `LW`
if makeLayoutRegexp(layoutLastWorkdom, domDescriptor.valuePattern).MatchString(snormal) { if domDescriptor.makeLayoutRegexp(layoutLastWorkdom).MatchString(snormal) {
expr.lastWorkdayOfMonth = true expr.lastWorkdayOfMonth = true
} else { } else {
// `15W` // `15W`
pairs := makeLayoutRegexp(layoutWorkdom, domDescriptor.valuePattern).FindStringSubmatchIndex(snormal) pairs := domDescriptor.makeLayoutRegexp(layoutWorkdom).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 { if len(pairs) > 0 {
populateOne(expr.workdaysOfMonth, domDescriptor.atoi(snormal[pairs[2]:pairs[3]])) populateOne(expr.workdaysOfMonth, domDescriptor.atoi(snormal[pairs[2]:pairs[3]]))
} else { } else {
@ -397,7 +424,7 @@ func toList(set map[int]bool) []int {
/******************************************************************************/ /******************************************************************************/
func genericFieldParse(s string, desc fieldDescriptor) ([]*cronDirective, error) { func genericFieldParse(s string, desc *fieldDescriptor) ([]*cronDirective, error) {
// At least one entry must be present // At least one entry must be present
indices := entryFinder.FindAllStringIndex(s, -1) indices := entryFinder.FindAllStringIndex(s, -1)
if len(indices) == 0 { if len(indices) == 0 {
@ -414,7 +441,7 @@ func genericFieldParse(s string, desc fieldDescriptor) ([]*cronDirective, error)
snormal := strings.ToLower(s[indices[i][0]:indices[i][1]]) snormal := strings.ToLower(s[indices[i][0]:indices[i][1]])
// `*` // `*`
if makeLayoutRegexp(layoutWildcard, desc.valuePattern).MatchString(snormal) { if desc.makeLayoutRegexp(layoutWildcard).MatchString(snormal) {
directive.kind = all directive.kind = all
directive.first = desc.min directive.first = desc.min
directive.last = desc.max directive.last = desc.max
@ -423,14 +450,14 @@ func genericFieldParse(s string, desc fieldDescriptor) ([]*cronDirective, error)
continue continue
} }
// `5` // `5`
if makeLayoutRegexp(layoutValue, desc.valuePattern).MatchString(snormal) { if desc.makeLayoutRegexp(layoutValue).MatchString(snormal) {
directive.kind = one directive.kind = one
directive.first = desc.atoi(snormal) directive.first = desc.atoi(snormal)
directives = append(directives, &directive) directives = append(directives, &directive)
continue continue
} }
// `5-20` // `5-20`
pairs := makeLayoutRegexp(layoutRange, desc.valuePattern).FindStringSubmatchIndex(snormal) pairs := desc.makeLayoutRegexp(layoutRange).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 { if len(pairs) > 0 {
directive.kind = span directive.kind = span
directive.first = desc.atoi(snormal[pairs[2]:pairs[3]]) directive.first = desc.atoi(snormal[pairs[2]:pairs[3]])
@ -440,7 +467,7 @@ func genericFieldParse(s string, desc fieldDescriptor) ([]*cronDirective, error)
continue continue
} }
// `*/2` // `*/2`
pairs = makeLayoutRegexp(layoutWildcardAndInterval, desc.valuePattern).FindStringSubmatchIndex(snormal) pairs = desc.makeLayoutRegexp(layoutWildcardAndInterval).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 { if len(pairs) > 0 {
directive.kind = span directive.kind = span
directive.first = desc.min directive.first = desc.min
@ -450,7 +477,7 @@ func genericFieldParse(s string, desc fieldDescriptor) ([]*cronDirective, error)
continue continue
} }
// `5/2` // `5/2`
pairs = makeLayoutRegexp(layoutValueAndInterval, desc.valuePattern).FindStringSubmatchIndex(snormal) pairs = desc.makeLayoutRegexp(layoutValueAndInterval).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 { if len(pairs) > 0 {
directive.kind = span directive.kind = span
directive.first = desc.atoi(snormal[pairs[2]:pairs[3]]) directive.first = desc.atoi(snormal[pairs[2]:pairs[3]])
@ -460,7 +487,7 @@ func genericFieldParse(s string, desc fieldDescriptor) ([]*cronDirective, error)
continue continue
} }
// `5-20/2` // `5-20/2`
pairs = makeLayoutRegexp(layoutRangeAndInterval, desc.valuePattern).FindStringSubmatchIndex(snormal) pairs = desc.makeLayoutRegexp(layoutRangeAndInterval).FindStringSubmatchIndex(snormal)
if len(pairs) > 0 { if len(pairs) > 0 {
directive.kind = span directive.kind = span
directive.first = desc.atoi(snormal[pairs[2]:pairs[3]]) directive.first = desc.atoi(snormal[pairs[2]:pairs[3]])
@ -475,15 +502,3 @@ func genericFieldParse(s string, desc fieldDescriptor) ([]*cronDirective, error)
} }
return directives, nil return directives, nil
} }
/******************************************************************************/
func makeLayoutRegexp(layout, value string) *regexp.Regexp {
layout = strings.Replace(layout, `%value%`, value, -1)
re := layoutRegexp[layout]
if re == nil {
re = regexp.MustCompile(layout)
layoutRegexp[layout] = re
}
return re
}