Better synchronization for multithreading - fixes issue #1435
- The shared mutexes were defined as normal instance variables in a simulator, so the locks didn't work (multiple instances of simulators active). By making them static (which is more appropriate anyway, since we are protecting static data) the synchroniation now works. - For better performance an own class for multiple read/single write locks was added and used instead of the shared mutexes. - Made the static wstring_covvert instance local variables in ws2s and s2ws. Otherwise we would also need a mutext to protect it. - Some other minor changes.
This commit is contained in:
parent
1751fac496
commit
4aaa0bfb5a
|
@ -1,8 +1,5 @@
|
|||
# Integrating ANTLR into Development Systems
|
||||
|
||||
The Java target is the reference implementation mirrored by other targets. The following pages help you integrate ANTLR into development environments and build systems appropriate for your target language. As of January 2015, we have Java, C#, Python 2, Python 3, and JavaScript targets.
|
||||
The Java target is the reference implementation mirrored by other targets. The following pages help you integrate ANTLR into development environments and build systems appropriate for your target language. As of December 2016, we have Java, C#, Python 2, Python 3, JavaScript, Go, C++, and Swift targets.
|
||||
|
||||
The easiest thing is probably just to use an [ANTLR plug-in](http://www.antlr.org/tools.html) for your favorite development environment.
|
||||
|
||||
Java IDE Integration
|
||||
C# IDE Integration
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Zip it
|
||||
rm -f antlr4-cpp-runtime-source.zip
|
||||
zip -r antlr4-cpp-runtime-source.zip "README.md" "cmake" "demo" "runtime" "CMakeLists.txt" "License.txt" "deploy-macos.sh" "deploy-source.sh" "deploy-windows.cmd" "VERSION" \
|
||||
-X -x "*.DS_Store*" "antlrcpp.xcodeproj/xcuserdata/*" "*Build*" "*DerivedData*" "*.jar" "demo/generated/*" "*.vscode*"
|
||||
-X -x "*.DS_Store*" "antlrcpp.xcodeproj/xcuserdata/*" "*Build*" "*DerivedData*" "*.jar" "demo/generated/*" "*.vscode*" "runtime/build/*"
|
||||
|
||||
# Deploy
|
||||
#cp antlr4-cpp-runtime-source.zip ~/antlr/sites/website-antlr4/download
|
||||
|
|
|
@ -1096,7 +1096,7 @@
|
|||
276E5CE51CDB57AA003FF4B4 /* Arrays.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Arrays.cpp; sourceTree = "<group>"; };
|
||||
276E5CE61CDB57AA003FF4B4 /* Arrays.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Arrays.h; sourceTree = "<group>"; };
|
||||
276E5CE71CDB57AA003FF4B4 /* BitSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BitSet.h; sourceTree = "<group>"; };
|
||||
276E5CE81CDB57AA003FF4B4 /* CPPUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPPUtils.cpp; sourceTree = "<group>"; };
|
||||
276E5CE81CDB57AA003FF4B4 /* CPPUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CPPUtils.cpp; sourceTree = "<group>"; wrapsLines = 0; };
|
||||
276E5CE91CDB57AA003FF4B4 /* CPPUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CPPUtils.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
276E5CEA1CDB57AA003FF4B4 /* Declarations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Declarations.h; sourceTree = "<group>"; };
|
||||
276E5CEB1CDB57AA003FF4B4 /* guid.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = guid.cpp; sourceTree = "<group>"; };
|
||||
|
|
|
@ -238,7 +238,7 @@ const atn::ATN& Parser::getATNWithBypassAlts() {
|
|||
throw UnsupportedOperationException("The current parser does not support an ATN with bypass alternatives.");
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
std::lock_guard<std::mutex> lck(_mutex);
|
||||
|
||||
// XXX: using the entire serialized ATN as key into the map is a big resource waste.
|
||||
// How large can that thing become?
|
||||
|
@ -570,7 +570,7 @@ std::vector<std::string> Parser::getRuleInvocationStack(RuleContext *p) {
|
|||
std::vector<std::string> Parser::getDFAStrings() {
|
||||
atn::ParserATNSimulator *simulator = getInterpreter<atn::ParserATNSimulator>();
|
||||
if (!simulator->decisionToDFA.empty()) {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
std::lock_guard<std::mutex> lck(_mutex);
|
||||
|
||||
std::vector<std::string> s;
|
||||
for (size_t d = 0; d < simulator->decisionToDFA.size(); d++) {
|
||||
|
@ -585,7 +585,7 @@ std::vector<std::string> Parser::getDFAStrings() {
|
|||
void Parser::dumpDFA() {
|
||||
atn::ParserATNSimulator *simulator = getInterpreter<atn::ParserATNSimulator>();
|
||||
if (!simulator->decisionToDFA.empty()) {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
std::lock_guard<std::mutex> lck(_mutex);
|
||||
bool seenOne = false;
|
||||
for (size_t d = 0; d < simulator->decisionToDFA.size(); d++) {
|
||||
dfa::DFA &dfa = simulator->decisionToDFA[d];
|
||||
|
|
|
@ -61,7 +61,7 @@ dfa::Vocabulary const& Recognizer::getVocabulary() const {
|
|||
std::map<std::string, size_t> Recognizer::getTokenTypeMap() {
|
||||
const dfa::Vocabulary& vocabulary = getVocabulary();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
std::lock_guard<std::mutex> lck(_mutex);
|
||||
std::map<std::string, size_t> result;
|
||||
auto iterator = _tokenTypeMapCache.find(&vocabulary);
|
||||
if (iterator != _tokenTypeMapCache.end()) {
|
||||
|
@ -91,7 +91,7 @@ std::map<std::string, size_t> Recognizer::getRuleIndexMap() {
|
|||
throw "The current recognizer does not provide a list of rule names.";
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
std::lock_guard<std::mutex> lck(_mutex);
|
||||
std::map<std::string, size_t> result;
|
||||
auto iterator = _ruleIndexMapCache.find(ruleNames);
|
||||
if (iterator != _ruleIndexMapCache.end()) {
|
||||
|
|
|
@ -168,7 +168,7 @@ namespace antlr4 {
|
|||
atn::ATNSimulator *_interpreter; // Set and deleted in descendants (or the profiler).
|
||||
|
||||
// Mutex to manage synchronized access for multithreading.
|
||||
std::recursive_mutex _mutex;
|
||||
std::mutex _mutex;
|
||||
|
||||
private:
|
||||
static std::map<const dfa::Vocabulary*, std::map<std::string, size_t>> _tokenTypeMapCache;
|
||||
|
|
|
@ -42,6 +42,8 @@ using namespace antlr4::dfa;
|
|||
using namespace antlr4::atn;
|
||||
|
||||
const Ref<DFAState> ATNSimulator::ERROR = std::make_shared<DFAState>(INT32_MAX);
|
||||
antlrcpp::SingleWriteMultipleReadLock ATNSimulator::_stateLock;
|
||||
antlrcpp::SingleWriteMultipleReadLock ATNSimulator::_edgeLock;
|
||||
|
||||
ATNSimulator::ATNSimulator(const ATN &atn, PredictionContextCache &sharedContextCache)
|
||||
: atn(atn), _sharedContextCache(sharedContextCache) {
|
||||
|
@ -56,7 +58,8 @@ PredictionContextCache& ATNSimulator::getSharedContextCache() {
|
|||
}
|
||||
|
||||
Ref<PredictionContext> ATNSimulator::getCachedContext(Ref<PredictionContext> const& context) {
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
// 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.
|
||||
std::map<Ref<PredictionContext>, Ref<PredictionContext>> visited;
|
||||
return PredictionContext::getCachedContext(context, _sharedContextCache, visited);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "atn/ATN.h"
|
||||
#include "misc/IntervalSet.h"
|
||||
#include "support/CPPUtils.h"
|
||||
#include "atn/PredictionContext.h"
|
||||
|
||||
namespace antlr4 {
|
||||
|
@ -81,8 +82,8 @@ namespace atn {
|
|||
static ATNState *stateFactory(int type, int ruleIndex);
|
||||
|
||||
protected:
|
||||
// Mutex to manage synchronized access for multithreading.
|
||||
std::recursive_mutex _mutex;
|
||||
static antlrcpp::SingleWriteMultipleReadLock _stateLock; // Lock for DFA states.
|
||||
static antlrcpp::SingleWriteMultipleReadLock _edgeLock; // Lock for the sparse edge map in DFA states.
|
||||
|
||||
/// <summary>
|
||||
/// The context cache maps all PredictionContext objects that are equals()
|
||||
|
|
|
@ -209,14 +209,19 @@ dfa::DFAState *LexerATNSimulator::getExistingTargetState(dfa::DFAState *s, size_
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
dfa::DFAState *target = s->edges[t - MIN_DFA_EDGE];
|
||||
_edgeLock.readLock();
|
||||
auto iterator = s->edges.find(t - MIN_DFA_EDGE);
|
||||
#if DEBUG_ATN == 1
|
||||
if (target != nullptr) {
|
||||
std::cout << std::string("reuse state ") << s->stateNumber << std::string(" edge to ") << target->stateNumber << std::endl;
|
||||
if (iterator != s->edges.end()) {
|
||||
std::cout << std::string("reuse state ") << s->stateNumber << std::string(" edge to ") << iterator->second->stateNumber << std::endl;
|
||||
}
|
||||
#endif
|
||||
_edgeLock.readUnlock();
|
||||
|
||||
return target;
|
||||
if (iterator == s->edges.end())
|
||||
return nullptr;
|
||||
|
||||
return iterator->second;
|
||||
}
|
||||
|
||||
dfa::DFAState *LexerATNSimulator::computeTargetState(CharStream *input, dfa::DFAState *s, size_t t) {
|
||||
|
@ -551,8 +556,9 @@ void LexerATNSimulator::addDFAEdge(dfa::DFAState *p, size_t t, dfa::DFAState *q)
|
|||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
_edgeLock.writeLock();
|
||||
p->edges[t - MIN_DFA_EDGE] = q; // connect
|
||||
_edgeLock.writeUnlock();
|
||||
}
|
||||
|
||||
dfa::DFAState *LexerATNSimulator::addDFAState(ATNConfigSet *configs) {
|
||||
|
@ -578,22 +584,23 @@ dfa::DFAState *LexerATNSimulator::addDFAState(ATNConfigSet *configs) {
|
|||
|
||||
dfa::DFA &dfa = _decisionToDFA[_mode];
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
|
||||
if (!dfa.states.empty()) {
|
||||
auto iterator = dfa.states.find(proposed);
|
||||
if (iterator != dfa.states.end()) {
|
||||
delete proposed;
|
||||
return *iterator;
|
||||
}
|
||||
_stateLock.writeLock();
|
||||
if (!dfa.states.empty()) {
|
||||
auto iterator = dfa.states.find(proposed);
|
||||
if (iterator != dfa.states.end()) {
|
||||
delete proposed;
|
||||
_stateLock.writeUnlock();
|
||||
return *iterator;
|
||||
}
|
||||
|
||||
proposed->stateNumber = (int)dfa.states.size();
|
||||
proposed->configs->setReadonly(true);
|
||||
dfa.states.insert(proposed);
|
||||
return proposed;
|
||||
}
|
||||
|
||||
proposed->stateNumber = (int)dfa.states.size();
|
||||
proposed->configs->setReadonly(true);
|
||||
|
||||
dfa.states.insert(proposed);
|
||||
_stateLock.writeUnlock();
|
||||
|
||||
return proposed;
|
||||
}
|
||||
|
||||
dfa::DFA& LexerATNSimulator::getDFA(size_t mode) {
|
||||
|
|
|
@ -137,7 +137,7 @@ size_t ParserATNSimulator::adaptivePredict(TokenStream *input, size_t decision,
|
|||
dfa.s0->configs = std::move(s0_closure); // not used for prediction but useful to know start configs anyway
|
||||
dfa::DFAState *newState = new dfa::DFAState(applyPrecedenceFilter(dfa.s0->configs.get())); /* mem-check: managed by the DFA or deleted below */
|
||||
s0 = addDFAState(dfa, newState);
|
||||
dfa.setPrecedenceStartState(parser->getPrecedence(), s0, _mutex);
|
||||
dfa.setPrecedenceStartState(parser->getPrecedence(), s0, _edgeLock);
|
||||
if (s0 != newState) {
|
||||
delete newState; // If there was already a state with this config set we don't need the new one.
|
||||
}
|
||||
|
@ -272,7 +272,9 @@ size_t ParserATNSimulator::execATN(dfa::DFA &dfa, dfa::DFAState *s0, TokenStream
|
|||
}
|
||||
|
||||
dfa::DFAState *ParserATNSimulator::getExistingTargetState(dfa::DFAState *previousD, size_t t) {
|
||||
_edgeLock.readLock();
|
||||
auto iterator = previousD->edges.find(t);
|
||||
_edgeLock.readUnlock();
|
||||
if (iterator == previousD->edges.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1182,8 +1184,9 @@ dfa::DFAState *ParserATNSimulator::addDFAEdge(dfa::DFA &dfa, dfa::DFAState *from
|
|||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
_edgeLock.writeLock();
|
||||
from->edges[t] = to; // connect
|
||||
_edgeLock.writeUnlock();
|
||||
}
|
||||
|
||||
#if DEBUG_DFA == 1
|
||||
|
@ -1204,26 +1207,28 @@ dfa::DFAState *ParserATNSimulator::addDFAState(dfa::DFA &dfa, dfa::DFAState *D)
|
|||
return D;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lck(_mutex);
|
||||
_stateLock.writeLock();
|
||||
|
||||
auto existing = dfa.states.find(D);
|
||||
if (existing != dfa.states.end()) {
|
||||
return *existing;
|
||||
}
|
||||
auto existing = dfa.states.find(D);
|
||||
if (existing != dfa.states.end()) {
|
||||
_stateLock.writeUnlock();
|
||||
return *existing;
|
||||
}
|
||||
|
||||
D->stateNumber = (int)dfa.states.size();
|
||||
if (!D->configs->isReadonly()) {
|
||||
D->configs->optimizeConfigs(this);
|
||||
D->configs->setReadonly(true);
|
||||
}
|
||||
|
||||
dfa.states.insert(D);
|
||||
_stateLock.writeUnlock();
|
||||
|
||||
D->stateNumber = (int)dfa.states.size();
|
||||
if (!D->configs->isReadonly()) {
|
||||
D->configs->optimizeConfigs(this);
|
||||
D->configs->setReadonly(true);
|
||||
}
|
||||
dfa.states.insert(D);
|
||||
#if DEBUG_DFA == 1
|
||||
std::cout << "adding new DFA state: " << D << std::endl;
|
||||
std::cout << "adding new DFA state: " << D << std::endl;
|
||||
#endif
|
||||
|
||||
return D;
|
||||
}
|
||||
return D;
|
||||
}
|
||||
|
||||
void ParserATNSimulator::reportAttemptingFullContext(dfa::DFA &dfa, const antlrcpp::BitSet &conflictingAlts,
|
||||
|
|
|
@ -97,7 +97,7 @@ DFAState* DFA::getPrecedenceStartState(int precedence) const {
|
|||
return iterator->second;
|
||||
}
|
||||
|
||||
void DFA::setPrecedenceStartState(int precedence, DFAState *startState, std::recursive_mutex &mutex) {
|
||||
void DFA::setPrecedenceStartState(int precedence, DFAState *startState, SingleWriteMultipleReadLock &lock) {
|
||||
if (!isPrecedenceDfa()) {
|
||||
throw IllegalStateException("Only precedence DFAs may contain a precedence start state.");
|
||||
}
|
||||
|
@ -107,8 +107,9 @@ void DFA::setPrecedenceStartState(int precedence, DFAState *startState, std::rec
|
|||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(mutex);
|
||||
lock.writeLock();
|
||||
s0->edges[precedence] = startState;
|
||||
lock.writeUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
|
||||
#include "dfa/DFAState.h"
|
||||
|
||||
namespace antlrcpp {
|
||||
class SingleWriteMultipleReadLock;
|
||||
}
|
||||
|
||||
namespace antlr4 {
|
||||
namespace dfa {
|
||||
|
||||
|
@ -88,7 +92,7 @@ namespace dfa {
|
|||
* @throws IllegalStateException if this is not a precedence DFA.
|
||||
* @see #isPrecedenceDfa()
|
||||
*/
|
||||
void setPrecedenceStartState(int precedence, DFAState *startState, std::recursive_mutex &mutex);
|
||||
void setPrecedenceStartState(int precedence, DFAState *startState, antlrcpp::SingleWriteMultipleReadLock &lock);
|
||||
|
||||
/// Return a list of all states in this DFA, ordered by state number.
|
||||
virtual std::vector<DFAState *> getStates() const;
|
||||
|
|
|
@ -191,7 +191,7 @@ namespace antlrcpp {
|
|||
std::string result;
|
||||
std::size_t nestCount = 0;
|
||||
|
||||
next: {
|
||||
next: {
|
||||
try {
|
||||
std::exception_ptr yeptr;
|
||||
std::swap(eptr, yeptr);
|
||||
|
@ -217,12 +217,52 @@ namespace antlrcpp {
|
|||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
result += std::string(nestCount, ')');
|
||||
return result;
|
||||
}
|
||||
|
||||
//----------------- FinallyAction ------------------------------------------------------------------------------------
|
||||
|
||||
FinalAction finally(std::function<void ()> f) {
|
||||
return FinalAction(f);
|
||||
}
|
||||
|
||||
//----------------- SingleWriteMultipleRead --------------------------------------------------------------------------
|
||||
|
||||
void SingleWriteMultipleReadLock::readLock() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
while (_waitingWriters != 0)
|
||||
_readerGate.wait(lock);
|
||||
++_activeReaders;
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
void SingleWriteMultipleReadLock::readUnlock() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
--_activeReaders;
|
||||
lock.unlock();
|
||||
_writerGate.notify_one();
|
||||
}
|
||||
|
||||
void SingleWriteMultipleReadLock::writeLock() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
++_waitingWriters;
|
||||
while (_activeReaders != 0 || _activeWriters != 0)
|
||||
_writerGate.wait(lock);
|
||||
++_activeWriters;
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
void SingleWriteMultipleReadLock::writeUnlock() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
--_waitingWriters;
|
||||
--_activeWriters;
|
||||
if (_waitingWriters > 0)
|
||||
_writerGate.notify_one();
|
||||
else
|
||||
_readerGate.notify_all();
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
} // namespace antlrcpp
|
||||
|
|
|
@ -84,4 +84,21 @@ namespace antlrcpp {
|
|||
// Get the error text from an exception pointer or the current exception.
|
||||
std::string what(std::exception_ptr eptr = std::current_exception());
|
||||
|
||||
class SingleWriteMultipleReadLock {
|
||||
public:
|
||||
void readLock();
|
||||
void readUnlock();
|
||||
void writeLock();
|
||||
void writeUnlock();
|
||||
|
||||
private:
|
||||
std::condition_variable _readerGate;
|
||||
std::condition_variable _writerGate;
|
||||
|
||||
std::mutex _mutex;
|
||||
size_t _activeReaders = 0;
|
||||
size_t _waitingWriters = 0;
|
||||
size_t _activeWriters = 0;
|
||||
};
|
||||
|
||||
} // namespace antlrcpp
|
||||
|
|
|
@ -44,14 +44,16 @@ void replaceAll(std::string& str, const std::string& from, const std::string& to
|
|||
}
|
||||
}
|
||||
|
||||
static std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
|
||||
std::string ws2s(const std::wstring &wstr) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
|
||||
std::string narrow = converter.to_bytes(wstr);
|
||||
return narrow;
|
||||
}
|
||||
|
||||
std::wstring s2ws(const std::string &str) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
|
||||
std::wstring wide = converter.from_bytes(str);
|
||||
return wide;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue