Skip to content

Commit 6e564c1

Browse files
authored
Merge pull request #658 from sylr/parse-no-lock
Only lock fieldmap once during message parsing
2 parents f8a53b0 + af66cc8 commit 6e564c1

File tree

2 files changed

+73
-10
lines changed

2 files changed

+73
-10
lines changed

field_map.go

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,20 @@ func (m FieldMap) GetField(tag Tag, parser FieldValueReader) MessageRejectError
115115
return nil
116116
}
117117

118+
// GetField parses of a field with Tag tag. Returned reject may indicate the field is not present, or the field value is invalid.
119+
func (m FieldMap) getFieldNoLock(tag Tag, parser FieldValueReader) MessageRejectError {
120+
f, ok := m.tagLookup[tag]
121+
if !ok {
122+
return ConditionallyRequiredFieldMissing(tag)
123+
}
124+
125+
if err := parser.Read(f[0].value); err != nil {
126+
return IncorrectDataFormatForValue(tag)
127+
}
128+
129+
return nil
130+
}
131+
118132
// GetBytes is a zero-copy GetField wrapper for []bytes fields.
119133
func (m FieldMap) GetBytes(tag Tag) ([]byte, MessageRejectError) {
120134
m.rwLock.RLock()
@@ -128,6 +142,16 @@ func (m FieldMap) GetBytes(tag Tag) ([]byte, MessageRejectError) {
128142
return f[0].value, nil
129143
}
130144

145+
// getBytesNoLock is a lock free zero-copy GetField wrapper for []bytes fields.
146+
func (m FieldMap) getBytesNoLock(tag Tag) ([]byte, MessageRejectError) {
147+
f, ok := m.tagLookup[tag]
148+
if !ok {
149+
return nil, ConditionallyRequiredFieldMissing(tag)
150+
}
151+
152+
return f[0].value, nil
153+
}
154+
131155
// GetBool is a GetField wrapper for bool fields.
132156
func (m FieldMap) GetBool(tag Tag) (bool, MessageRejectError) {
133157
var val FIXBoolean
@@ -152,6 +176,21 @@ func (m FieldMap) GetInt(tag Tag) (int, MessageRejectError) {
152176
return int(val), err
153177
}
154178

179+
// GetInt is a lock free GetField wrapper for int fields.
180+
func (m FieldMap) getIntNoLock(tag Tag) (int, MessageRejectError) {
181+
bytes, err := m.getBytesNoLock(tag)
182+
if err != nil {
183+
return 0, err
184+
}
185+
186+
var val FIXInt
187+
if val.Read(bytes) != nil {
188+
err = IncorrectDataFormatForValue(tag)
189+
}
190+
191+
return int(val), err
192+
}
193+
155194
// GetTime is a GetField wrapper for utc timestamp fields.
156195
func (m FieldMap) GetTime(tag Tag) (t time.Time, err MessageRejectError) {
157196
m.rwLock.RLock()
@@ -179,6 +218,15 @@ func (m FieldMap) GetString(tag Tag) (string, MessageRejectError) {
179218
return string(val), nil
180219
}
181220

221+
// GetString is a GetField wrapper for string fields.
222+
func (m FieldMap) getStringNoLock(tag Tag) (string, MessageRejectError) {
223+
var val FIXString
224+
if err := m.getFieldNoLock(tag, &val); err != nil {
225+
return "", err
226+
}
227+
return string(val), nil
228+
}
229+
182230
// GetGroup is a Get function specific to Group Fields.
183231
func (m FieldMap) GetGroup(parser FieldGroupReader) MessageRejectError {
184232
m.rwLock.RLock()
@@ -246,6 +294,13 @@ func (m *FieldMap) Clear() {
246294
}
247295
}
248296

297+
func (m *FieldMap) clearNoLock() {
298+
m.tags = m.tags[0:0]
299+
for k := range m.tagLookup {
300+
delete(m.tagLookup, k)
301+
}
302+
}
303+
249304
// CopyInto overwrites the given FieldMap with this one.
250305
func (m *FieldMap) CopyInto(to *FieldMap) {
251306
m.rwLock.RLock()
@@ -263,9 +318,6 @@ func (m *FieldMap) CopyInto(to *FieldMap) {
263318
}
264319

265320
func (m *FieldMap) add(f field) {
266-
m.rwLock.Lock()
267-
defer m.rwLock.Unlock()
268-
269321
t := fieldTag(f)
270322
if _, ok := m.tagLookup[t]; !ok {
271323
m.tags = append(m.tags, t)

message.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,17 @@ func ParseMessageWithDataDictionary(
181181

182182
// doParsing executes the message parsing process.
183183
func doParsing(mp *msgParser) (err error) {
184+
mp.msg.Header.rwLock.Lock()
185+
defer mp.msg.Header.rwLock.Unlock()
186+
mp.msg.Body.rwLock.Lock()
187+
defer mp.msg.Body.rwLock.Unlock()
188+
mp.msg.Trailer.rwLock.Lock()
189+
defer mp.msg.Trailer.rwLock.Unlock()
190+
184191
// Initialize for parsing.
185-
mp.msg.Header.Clear()
186-
mp.msg.Body.Clear()
187-
mp.msg.Trailer.Clear()
192+
mp.msg.Header.clearNoLock()
193+
mp.msg.Body.clearNoLock()
194+
mp.msg.Trailer.clearNoLock()
188195

189196
// Allocate expected message fields in one chunk.
190197
fieldCount := bytes.Count(mp.rawBytes, []byte{'\001'})
@@ -262,7 +269,7 @@ func doParsing(mp *msgParser) (err error) {
262269
}
263270

264271
if mp.parsedFieldBytes.tag == tagXMLDataLen {
265-
xmlDataLen, _ = mp.msg.Header.GetInt(tagXMLDataLen)
272+
xmlDataLen, _ = mp.msg.Header.getIntNoLock(tagXMLDataLen)
266273
}
267274
mp.fieldIndex++
268275
}
@@ -287,7 +294,7 @@ func doParsing(mp *msgParser) (err error) {
287294
}
288295
}
289296

290-
bodyLength, err := mp.msg.Header.GetInt(tagBodyLength)
297+
bodyLength, err := mp.msg.Header.getIntNoLock(tagBodyLength)
291298
if err != nil {
292299
err = parseError{OrigError: err.Error()}
293300
} else if length != bodyLength && !xmlDataMsg {
@@ -368,7 +375,7 @@ func parseGroup(mp *msgParser, tags []Tag) {
368375
// tags slice will contain multiple tags if the tag in question is found while processing a group already.
369376
func isNumInGroupField(msg *Message, tags []Tag, appDataDictionary *datadictionary.DataDictionary) bool {
370377
if appDataDictionary != nil {
371-
msgt, err := msg.MsgType()
378+
msgt, err := msg.msgTypeNoLock()
372379
if err != nil {
373380
return false
374381
}
@@ -401,7 +408,7 @@ func isNumInGroupField(msg *Message, tags []Tag, appDataDictionary *datadictiona
401408
// tags slice will contain multiple tags if the tag in question is found while processing a group already.
402409
func getGroupFields(msg *Message, tags []Tag, appDataDictionary *datadictionary.DataDictionary) (fields []*datadictionary.FieldDef) {
403410
if appDataDictionary != nil {
404-
msgt, err := msg.MsgType()
411+
msgt, err := msg.msgTypeNoLock()
405412
if err != nil {
406413
return
407414
}
@@ -471,6 +478,10 @@ func (m *Message) MsgType() (string, MessageRejectError) {
471478
return m.Header.GetString(tagMsgType)
472479
}
473480

481+
func (m *Message) msgTypeNoLock() (string, MessageRejectError) {
482+
return m.Header.getStringNoLock(tagMsgType)
483+
}
484+
474485
// IsMsgTypeOf returns true if the Header contains MsgType (tag 35) field and its value is the specified one.
475486
func (m *Message) IsMsgTypeOf(msgType string) bool {
476487
if v, err := m.MsgType(); err == nil {

0 commit comments

Comments
 (0)