Skip to content

Commit

Permalink
fix: make static data variable threadsafe
Browse files Browse the repository at this point in the history
I've hit a bug developing a go parser and running multiple instances of
it with t.Parallel()

Turned out that staticData var was a global and would throw
concurrent data access errors when running the test suite
with -race

This commit fixes it by doing the following:

1. turn <parser.grammarName; format="cap">ParserStaticData into a struct
2. Teach <parser.name; format="cap">Init() to return a pointer to new instance of this struct
3. Teach <parser.name; format="cap">Init() to initialize it upon construction

I've been able to run 10000 concurrent parsers in this setup without
having any performance/memory usage problem

Signed-off-by: Tomasz Nguyen <me@swistofon.pl>
Signed-off-by: Tomasz Nguyen <tonguyen@confluent.io>
  • Loading branch information
swist committed Dec 13, 2023
1 parent d25d421 commit 35d155e
Showing 1 changed file with 8 additions and 8 deletions.
16 changes: 8 additions & 8 deletions tool/resources/org/antlr/v4/tool/templates/codegen/Go/Go.stg
Expand Up @@ -166,7 +166,7 @@ type <parser.name> struct {
<endif>
}

var <parser.grammarName; format="cap">ParserStaticData struct {
type <parser.grammarName; format="cap">ParserStaticData struct {
once sync.Once
serializedATN []int32
LiteralNames []string
Expand Down Expand Up @@ -198,10 +198,9 @@ func <parser.grammarName; format="lower">ParserInit() {
staticData.serializedATN = <atn>
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
atn := staticData.atn
staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
staticData.decisionToDFA = make([]*antlr.DFA, len(staticData.atn.DecisionToState))
decisionToDFA := staticData.decisionToDFA
for index, state := range atn.DecisionToState {
for index, state := range staticData.atn.DecisionToState {
decisionToDFA[index] = antlr.NewDFA(state, index)
}
}
Expand All @@ -210,14 +209,15 @@ func <parser.grammarName; format="lower">ParserInit() {
// static state used to implement the parser is lazily initialized during the first call to
// New<parser.name>(). You can call this function if you wish to initialize the static state ahead
// of time.
func <parser.name; format="cap">Init() {
staticData := &<parser.grammarName; format="cap">ParserStaticData
staticData.once.Do(<parser.grammarName; format="lower">ParserInit)
func <parser.name; format="cap">Init() *<parser.grammarName; format="cap">ParserStaticData {
staticData := &<parser.grammarName; format="cap">ParserStaticData{}
<parser.grammarName; format="lower">ParserInit(staticData)
return staticData
}

// New<parser.name> produces a new parser instance for the optional input antlr.TokenStream.
func New<parser.name>(input antlr.TokenStream) *<parser.name> {
<parser.name; format="cap">Init()
staticData := <parser.name; format="cap">Init()
this := new(<parser.name>)
this.BaseParser = antlr.NewBaseParser(input)
staticData := &<parser.grammarName; format="cap">ParserStaticData
Expand Down

0 comments on commit 35d155e

Please sign in to comment.