Protect shared DFA state with mutexes

This commit is contained in:
Peter Boyer 2017-03-16 12:43:21 -04:00
parent ae3aadce3f
commit 11b4ee61f0
4 changed files with 64 additions and 26 deletions

View File

@ -4,7 +4,10 @@
package antlr
import "sort"
import (
"sort"
"sync"
)
type DFA struct {
// atnStartState is the ATN state in which this was created
@ -15,8 +18,10 @@ type DFA struct {
// states is all the DFA states. Use Map to get the old state back; Set can only
// indicate whether it is there.
states map[int]*DFAState
statesMu sync.RWMutex
s0 *DFAState
s0Mu sync.RWMutex
// precedenceDfa is the backing field for isPrecedenceDfa and setPrecedenceDfa.
// True if the DFA is for a precedence decision and false otherwise.
@ -40,6 +45,9 @@ func (d *DFA) getPrecedenceStartState(precedence int) *DFAState {
panic("only precedence DFAs may contain a precedence start state")
}
d.s0Mu.RLock()
defer d.s0Mu.RUnlock()
// s0.edges is never nil for a precedence DFA
if precedence < 0 || precedence >= len(d.s0.edges) {
return nil
@ -59,6 +67,9 @@ func (d *DFA) setPrecedenceStartState(precedence int, startState *DFAState) {
return
}
d.s0Mu.Lock()
defer d.s0Mu.Unlock()
// Synchronization on s0 here is ok. When the DFA is turned into a
// precedence DFA, s0 will be initialized once and not updated again. s0.edges
// is never nil for a precedence DFA.
@ -93,15 +104,42 @@ func (d *DFA) setPrecedenceDfa(precedenceDfa bool) {
}
}
func (d *DFA) GetStates() map[int]*DFAState {
return d.states
func (d *DFA) getS0() *DFAState {
d.s0Mu.RLock()
defer d.s0Mu.RUnlock()
return d.s0
}
type DFAStateList []*DFAState
func (d *DFA) setS0(s *DFAState) {
d.s0Mu.Lock()
defer d.s0Mu.Unlock()
d.s0 = s
}
func (d DFAStateList) Len() int { return len(d) }
func (d DFAStateList) Less(i, j int) bool { return d[i].stateNumber < d[j].stateNumber }
func (d DFAStateList) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
func (d *DFA) getState(hash int) (*DFAState, bool) {
d.statesMu.RLock()
defer d.statesMu.RUnlock()
s, ok := d.states[hash]
return s, ok
}
func (d *DFA) setState(hash int, state *DFAState) {
d.statesMu.Lock()
defer d.statesMu.Unlock()
d.states[hash] = state
}
func (d *DFA) numStates() int {
d.statesMu.RLock()
defer d.statesMu.RUnlock()
return len(d.states)
}
type dfaStateList []*DFAState
func (d dfaStateList) Len() int { return len(d) }
func (d dfaStateList) Less(i, j int) bool { return d[i].stateNumber < d[j].stateNumber }
func (d dfaStateList) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
// sortedStates returns the states in d sorted by their state number.
func (d *DFA) sortedStates() []*DFAState {
@ -111,7 +149,7 @@ func (d *DFA) sortedStates() []*DFAState {
vs = append(vs, v)
}
sort.Sort(DFAStateList(vs))
sort.Sort(dfaStateList(vs))
return vs
}

View File

@ -119,7 +119,7 @@ func (l *LexerATNSimulator) MatchATN(input CharStream) int {
next := l.addDFAState(s0Closure)
if !suppressEdge {
l.decisionToDFA[l.mode].s0 = next
l.decisionToDFA[l.mode].setS0(next)
}
predict := l.execATN(input, next)
@ -584,15 +584,15 @@ func (l *LexerATNSimulator) addDFAState(configs ATNConfigSet) *DFAState {
}
hash := proposed.hash()
dfa := l.decisionToDFA[l.mode]
existing := dfa.GetStates()[hash]
if existing != nil {
existing, ok := dfa.getState(hash)
if ok {
return existing
}
newState := proposed
newState.stateNumber = len(dfa.GetStates())
newState.stateNumber = dfa.numStates()
configs.SetReadOnly(true)
newState.configs = configs
dfa.GetStates()[hash] = newState
dfa.setState(hash, newState)
return newState
}

View File

@ -686,7 +686,7 @@ func (p *BaseParser) GetDFAStrings() string {
func (p *BaseParser) DumpDFA() {
seenOne := false
for _, dfa := range p.Interpreter.decisionToDFA {
if len(dfa.GetStates()) > 0 {
if dfa.numStates() > 0 {
if seenOne {
fmt.Println()
}

View File

@ -1416,25 +1416,25 @@ func (p *ParserATNSimulator) addDFAEdge(dfa *DFA, from *DFAState, t int, to *DFA
// state if {@code D} is already in the DFA, or {@code D} itself if the
// state was not already present.
//
func (p *ParserATNSimulator) addDFAState(dfa *DFA, D *DFAState) *DFAState {
if D == ATNSimulatorError {
return D
func (p *ParserATNSimulator) addDFAState(dfa *DFA, d *DFAState) *DFAState {
if d == ATNSimulatorError {
return d
}
hash := D.hash()
var existing, ok = dfa.GetStates()[hash]
hash := d.hash()
existing, ok := dfa.getState(hash)
if ok {
return existing
}
D.stateNumber = len(dfa.GetStates())
if !D.configs.ReadOnly() {
D.configs.OptimizeConfigs(p.BaseATNSimulator)
D.configs.SetReadOnly(true)
d.stateNumber = dfa.numStates()
if !d.configs.ReadOnly() {
d.configs.OptimizeConfigs(p.BaseATNSimulator)
d.configs.SetReadOnly(true)
}
dfa.GetStates()[hash] = D
dfa.setState(hash, d)
if ParserATNSimulatorDebug {
fmt.Println("adding NewDFA state: " + D.String())
fmt.Println("adding NewDFA state: " + d.String())
}
return D
return d
}
func (p *ParserATNSimulator) ReportAttemptingFullContext(dfa *DFA, conflictingAlts *BitSet, configs ATNConfigSet, startIndex, stopIndex int) {