Fixed crash with multi threaded parsers warming up at the same time.
The lock for the shared DFA state needs to protect a few more operations than just the addDFAState stuff and had to move up one call level. This in turn requires now 2 places where the lock must be aquired.
This commit is contained in:
parent
d8826368d4
commit
e002b177c6
|
@ -32,8 +32,7 @@ PredictionContextCache& ATNSimulator::getSharedContextCache() {
|
|||
}
|
||||
|
||||
Ref<PredictionContext> ATNSimulator::getCachedContext(Ref<PredictionContext> const& context) {
|
||||
// This function requires a lock as it might modify the cache, however the only path so far where it is called from
|
||||
// (addDFAState -> optimizeConfigs) already has _stateLock aquired. Adding another lock here would then deadlock.
|
||||
// This function must only be called with an active state lock, as we are going to change a shared structure.
|
||||
std::map<Ref<PredictionContext>, Ref<PredictionContext>> visited;
|
||||
return PredictionContext::getCachedContext(context, _sharedContextCache, visited);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "atn/SingletonPredictionContext.h"
|
||||
#include "atn/PredicateTransition.h"
|
||||
#include "atn/ActionTransition.h"
|
||||
#include "atn/TokensStartState.h"
|
||||
#include "misc/Interval.h"
|
||||
#include "dfa/DFA.h"
|
||||
#include "Lexer.h"
|
||||
|
@ -102,7 +103,7 @@ void LexerATNSimulator::clearDFA() {
|
|||
}
|
||||
|
||||
size_t LexerATNSimulator::matchATN(CharStream *input) {
|
||||
ATNState *startState = (ATNState *)atn.modeToStartState[_mode];
|
||||
ATNState *startState = atn.modeToStartState[_mode];
|
||||
|
||||
std::unique_ptr<ATNConfigSet> s0_closure = computeStartState(input, startState);
|
||||
|
||||
|
|
|
@ -107,6 +107,8 @@ size_t ParserATNSimulator::adaptivePredict(TokenStream *input, size_t decision,
|
|||
bool fullCtx = false;
|
||||
std::unique_ptr<ATNConfigSet> s0_closure = computeStartState(dynamic_cast<ATNState *>(dfa.atnStartState),
|
||||
&ParserRuleContext::EMPTY, fullCtx);
|
||||
|
||||
_stateLock.writeLock();
|
||||
if (dfa.isPrecedenceDfa()) {
|
||||
/* If this is a precedence DFA, we use applyPrecedenceFilter
|
||||
* to convert the computed start state to a precedence start
|
||||
|
@ -129,6 +131,7 @@ size_t ParserATNSimulator::adaptivePredict(TokenStream *input, size_t decision,
|
|||
delete newState; // If there was already a state with this config set we don't need the new one.
|
||||
}
|
||||
}
|
||||
_stateLock.writeUnlock();
|
||||
}
|
||||
|
||||
// We can start with an existing DFA.
|
||||
|
@ -1239,7 +1242,9 @@ dfa::DFAState *ParserATNSimulator::addDFAEdge(dfa::DFA &dfa, dfa::DFAState *from
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
_stateLock.writeLock();
|
||||
to = addDFAState(dfa, to); // used existing if possible not incoming
|
||||
_stateLock.writeUnlock();
|
||||
if (from == nullptr || t > (int)atn.maxTokenType) {
|
||||
return to;
|
||||
}
|
||||
|
@ -1268,11 +1273,8 @@ dfa::DFAState *ParserATNSimulator::addDFAState(dfa::DFA &dfa, dfa::DFAState *D)
|
|||
return D;
|
||||
}
|
||||
|
||||
_stateLock.writeLock();
|
||||
|
||||
auto existing = dfa.states.find(D);
|
||||
if (existing != dfa.states.end()) {
|
||||
_stateLock.writeUnlock();
|
||||
return *existing;
|
||||
}
|
||||
|
||||
|
@ -1283,7 +1285,6 @@ dfa::DFAState *ParserATNSimulator::addDFAState(dfa::DFA &dfa, dfa::DFAState *D)
|
|||
}
|
||||
|
||||
dfa.states.insert(D);
|
||||
_stateLock.writeUnlock();
|
||||
|
||||
#if DEBUG_DFA == 1
|
||||
std::cout << "adding new DFA state: " << D << std::endl;
|
||||
|
|
|
@ -251,11 +251,12 @@ namespace atn {
|
|||
|
||||
std::vector<dfa::DFA> &decisionToDFA;
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// SLL, LL, or LL + exact ambig detection? </summary>
|
||||
private:
|
||||
PredictionMode mode;
|
||||
|
||||
protected:
|
||||
/// <summary>
|
||||
/// Each prediction operation uses a cache for merge of prediction contexts.
|
||||
/// Don't keep around as it wastes huge amounts of memory. The merge cache
|
||||
|
@ -265,7 +266,6 @@ namespace atn {
|
|||
/// the merge if we ever see a and b again. Note that (b,a)->c should
|
||||
/// also be examined during cache lookup.
|
||||
/// </summary>
|
||||
protected:
|
||||
PredictionContextMergeCache mergeCache;
|
||||
|
||||
// LAME globals to avoid parameters!!!!! I need these down deep in predTransition
|
||||
|
@ -286,6 +286,7 @@ namespace atn {
|
|||
virtual void clearDFA() override;
|
||||
virtual size_t adaptivePredict(TokenStream *input, size_t decision, ParserRuleContext *outerContext);
|
||||
|
||||
protected:
|
||||
/// <summary>
|
||||
/// Performs ATN simulation to compute a predicted alternative based
|
||||
/// upon the remaining input, but also updates the DFA cache to avoid
|
||||
|
@ -317,7 +318,6 @@ namespace atn {
|
|||
/// conflict
|
||||
/// conflict + preds
|
||||
/// </summary>
|
||||
protected:
|
||||
virtual size_t execATN(dfa::DFA &dfa, dfa::DFAState *s0, TokenStream *input, size_t startIndex,
|
||||
ParserRuleContext *outerContext);
|
||||
|
||||
|
|
Loading…
Reference in New Issue