Skip to content

Commit

Permalink
Only lock fields once during message parsing
Browse files Browse the repository at this point in the history
Signed-off-by: Sylvain Rabot <[email protected]>
  • Loading branch information
sylr committed Jul 17, 2024
1 parent e3a2994 commit b81d077
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 5 deletions.
28 changes: 25 additions & 3 deletions field_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ func (m FieldMap) GetBytes(tag Tag) ([]byte, MessageRejectError) {
return f[0].value, nil
}

// getBytesNoLock is a lock free zero-copy GetField wrapper for []bytes fields.
func (m FieldMap) getBytesNoLock(tag Tag) ([]byte, MessageRejectError) {
f, ok := m.tagLookup[tag]
if !ok {
return nil, ConditionallyRequiredFieldMissing(tag)
}

return f[0].value, nil
}

// GetBool is a GetField wrapper for bool fields.
func (m FieldMap) GetBool(tag Tag) (bool, MessageRejectError) {
var val FIXBoolean
Expand All @@ -152,6 +162,21 @@ func (m FieldMap) GetInt(tag Tag) (int, MessageRejectError) {
return int(val), err
}

// GetInt is a lock free GetField wrapper for int fields.
func (m FieldMap) getIntNoLock(tag Tag) (int, MessageRejectError) {
bytes, err := m.getBytesNoLock(tag)
if err != nil {
return 0, err
}

var val FIXInt
if val.Read(bytes) != nil {
err = IncorrectDataFormatForValue(tag)
}

return int(val), err
}

// GetTime is a GetField wrapper for utc timestamp fields.
func (m FieldMap) GetTime(tag Tag) (t time.Time, err MessageRejectError) {
m.rwLock.RLock()
Expand Down Expand Up @@ -263,9 +288,6 @@ func (m *FieldMap) CopyInto(to *FieldMap) {
}

func (m *FieldMap) add(f field) {
m.rwLock.Lock()
defer m.rwLock.Unlock()

t := fieldTag(f)
if _, ok := m.tagLookup[t]; !ok {
m.tags = append(m.tags, t)
Expand Down
11 changes: 9 additions & 2 deletions message.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ func doParsing(mp *msgParser) (err error) {
mp.msg.Body.Clear()
mp.msg.Trailer.Clear()

mp.msg.Header.rwLock.Lock()
defer mp.msg.Header.rwLock.Unlock()
mp.msg.Body.rwLock.Lock()
defer mp.msg.Body.rwLock.Unlock()
mp.msg.Trailer.rwLock.Lock()
defer mp.msg.Trailer.rwLock.Unlock()

// Allocate expected message fields in one chunk.
fieldCount := 0
for _, b := range mp.rawBytes {
Expand Down Expand Up @@ -267,7 +274,7 @@ func doParsing(mp *msgParser) (err error) {
}

if mp.parsedFieldBytes.tag == tagXMLDataLen {
xmlDataLen, _ = mp.msg.Header.GetInt(tagXMLDataLen)
xmlDataLen, _ = mp.msg.Header.getIntNoLock(tagXMLDataLen)
}
mp.fieldIndex++
}
Expand All @@ -292,7 +299,7 @@ func doParsing(mp *msgParser) (err error) {
}
}

bodyLength, err := mp.msg.Header.GetInt(tagBodyLength)
bodyLength, err := mp.msg.Header.getIntNoLock(tagBodyLength)
if err != nil {
err = parseError{OrigError: err.Error()}
} else if length != bodyLength && !xmlDataMsg {
Expand Down

0 comments on commit b81d077

Please sign in to comment.