fix concurrent map write issue by init all regexp when startup
This commit is contained in:
parent
f0984319b4
commit
3462c6a153
|
@ -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]])
|
||||||
|
@ -474,16 +501,4 @@ func genericFieldParse(s string, desc fieldDescriptor) ([]*cronDirective, error)
|
||||||
directives = append(directives, &directive)
|
directives = append(directives, &directive)
|
||||||
}
|
}
|
||||||
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
|
|
||||||
}
|
|
Loading…
Reference in New Issue