You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2020/11/19 16:17:39 UTC
[plc4x] branch feature/plc4go updated: - Secured the access to the
valueCache in KNX against simultaneous access
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch feature/plc4go
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/feature/plc4go by this push:
new 11b542e - Secured the access to the valueCache in KNX against simultaneous access
11b542e is described below
commit 11b542eda23b6aab981bac353d60ec9b3e1f6426
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Thu Nov 19 17:17:26 2020 +0100
- Secured the access to the valueCache in KNX against simultaneous access
---
.../internal/plc4go/knxnetip/KncNetIpConnection.go | 10 +-
plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go | 240 +++++++++++----------
2 files changed, 131 insertions(+), 119 deletions(-)
diff --git a/plc4go/internal/plc4go/knxnetip/KncNetIpConnection.go b/plc4go/internal/plc4go/knxnetip/KncNetIpConnection.go
index 2eebff1..fcdd4d0 100644
--- a/plc4go/internal/plc4go/knxnetip/KncNetIpConnection.go
+++ b/plc4go/internal/plc4go/knxnetip/KncNetIpConnection.go
@@ -33,6 +33,7 @@ import (
apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
"net"
"strconv"
+ "sync"
"time"
)
@@ -62,7 +63,9 @@ type KnxNetIpConnection struct {
leve3AddressCache map[uint16]*driverModel.KnxGroupAddress3Level
leve2AddressCache map[uint16]*driverModel.KnxGroupAddress2Level
leve1AddressCache map[uint16]*driverModel.KnxGroupAddressFreeLevel
- valueCache map[uint16][]int8
+
+ valueCache map[uint16][]int8
+ valueCacheMutex sync.RWMutex
GatewayKnxAddress *driverModel.KnxAddress
GatewayName string
@@ -85,6 +88,7 @@ func NewKnxNetIpConnection(messageCodec spi.MessageCodec, options map[string][]s
leve2AddressCache: map[uint16]*driverModel.KnxGroupAddress2Level{},
leve1AddressCache: map[uint16]*driverModel.KnxGroupAddressFreeLevel{},
valueCache: map[uint16][]int8{},
+ valueCacheMutex: sync.RWMutex{},
}
}
@@ -378,13 +382,17 @@ func (m *KnxNetIpConnection) handleIncomingTunnelingRequest(tunnelingRequestChan
cemiDataInd := driverModel.CastCEMIDataInd(tunnelingRequest.Cemi.Child)
if cemiDataInd != nil {
addressData := uint16(cemiDataInd.CemiDataFrame.DestinationAddress[0])<<8 | (uint16(cemiDataInd.CemiDataFrame.DestinationAddress[1]) & 0xFF)
+ m.valueCacheMutex.RLock()
val, ok := m.valueCache[addressData]
+ m.valueCacheMutex.RUnlock()
changed := false
var payload []int8
payload = append(payload, cemiDataInd.CemiDataFrame.DataFirstByte)
payload = append(payload, cemiDataInd.CemiDataFrame.Data...)
if !ok || !m.sliceEqual(val, payload) {
+ m.valueCacheMutex.Lock()
m.valueCache[addressData] = payload
+ m.valueCacheMutex.Unlock()
// If this is a new value, we have to also provide the 3 different types of addresses.
if !ok {
destinationAddress := cemiDataInd.CemiDataFrame.DestinationAddress
diff --git a/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go b/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
index d6b43b6..79961fd 100644
--- a/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
+++ b/plc4go/internal/plc4go/knxnetip/KnxNetIpReader.go
@@ -19,141 +19,145 @@
package knxnetip
import (
- driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
- internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/model"
- internalValues "github.com/apache/plc4x/plc4go/internal/plc4go/model/values"
- "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
- "github.com/apache/plc4x/plc4go/internal/plc4go/utils"
- apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
- apiValues "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+ driverModel "github.com/apache/plc4x/plc4go/internal/plc4go/knxnetip/readwrite/model"
+ internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/model"
+ internalValues "github.com/apache/plc4x/plc4go/internal/plc4go/model/values"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+ "github.com/apache/plc4x/plc4go/internal/plc4go/utils"
+ apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+ apiValues "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
)
type KnxNetIpReader struct {
- connection *KnxNetIpConnection
- spi.PlcReader
+ connection *KnxNetIpConnection
+ spi.PlcReader
}
func NewKnxNetIpReader(connection *KnxNetIpConnection) *KnxNetIpReader {
- return &KnxNetIpReader{
- connection: connection,
- }
+ return &KnxNetIpReader{
+ connection: connection,
+ }
}
func (m KnxNetIpReader) Read(readRequest apiModel.PlcReadRequest) <-chan apiModel.PlcReadRequestResult {
- resultChan := make(chan apiModel.PlcReadRequestResult)
- go func() {
- responseCodes := map[string]apiModel.PlcResponseCode{}
- plcValues := map[string]apiValues.PlcValue{}
- for _, fieldName := range readRequest.GetFieldNames() {
- // Get the knx field
- field, err := CastToKnxNetIpFieldFromPlcField(readRequest.GetField(fieldName))
- if err != nil {
- responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
- plcValues[fieldName] = nil
- continue
- }
+ resultChan := make(chan apiModel.PlcReadRequestResult)
+ go func() {
+ responseCodes := map[string]apiModel.PlcResponseCode{}
+ plcValues := map[string]apiValues.PlcValue{}
+ for _, fieldName := range readRequest.GetFieldNames() {
+ // Get the knx field
+ field, err := CastToKnxNetIpFieldFromPlcField(readRequest.GetField(fieldName))
+ if err != nil {
+ responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+ plcValues[fieldName] = nil
+ continue
+ }
- // Pattern fields can match more than one value, therefore we have to handle things differently
- if field.IsPatternField() {
- // Depending on the type of field, get the uint16 ids of all values that match the current field
- matchedAddresses := map[uint16]*driverModel.KnxGroupAddress{}
- switch field.(type) {
- case KnxNetIpGroupAddress3LevelPlcField:
- for key, value := range m.connection.leve3AddressCache {
- if field.matches(value.Parent) {
- matchedAddresses[key] = value.Parent
- }
- }
- case KnxNetIpGroupAddress2LevelPlcField:
- for key, value := range m.connection.leve2AddressCache {
- if field.matches(value.Parent) {
- matchedAddresses[key] = value.Parent
- }
- }
- case KnxNetIpGroupAddress1LevelPlcField:
- for key, value := range m.connection.leve1AddressCache {
- if field.matches(value.Parent) {
- matchedAddresses[key] = value.Parent
- }
- }
- }
+ // Pattern fields can match more than one value, therefore we have to handle things differently
+ if field.IsPatternField() {
+ // Depending on the type of field, get the uint16 ids of all values that match the current field
+ matchedAddresses := map[uint16]*driverModel.KnxGroupAddress{}
+ switch field.(type) {
+ case KnxNetIpGroupAddress3LevelPlcField:
+ for key, value := range m.connection.leve3AddressCache {
+ if field.matches(value.Parent) {
+ matchedAddresses[key] = value.Parent
+ }
+ }
+ case KnxNetIpGroupAddress2LevelPlcField:
+ for key, value := range m.connection.leve2AddressCache {
+ if field.matches(value.Parent) {
+ matchedAddresses[key] = value.Parent
+ }
+ }
+ case KnxNetIpGroupAddress1LevelPlcField:
+ for key, value := range m.connection.leve1AddressCache {
+ if field.matches(value.Parent) {
+ matchedAddresses[key] = value.Parent
+ }
+ }
+ }
- // If not a single match was found, we'll return a "not found" message
- if len(matchedAddresses) == 0 {
- responseCodes[fieldName] = apiModel.PlcResponseCode_NOT_FOUND
- plcValues[fieldName] = nil
- continue
- }
+ // If not a single match was found, we'll return a "not found" message
+ if len(matchedAddresses) == 0 {
+ responseCodes[fieldName] = apiModel.PlcResponseCode_NOT_FOUND
+ plcValues[fieldName] = nil
+ continue
+ }
- // Go through all of the values and create a plc-struct from them
- // where the string version of the address becomes the property name
- // and the property value is the corresponding value (Other wise it
- // would be impossible to know which of the fields the pattern matched
- // a given value belongs to)
- values := map[string]apiValues.PlcValue{}
- for numericAddress, address := range matchedAddresses {
- // Get the raw data from the cache
- int8s := m.connection.valueCache[numericAddress]
+ // Go through all of the values and create a plc-struct from them
+ // where the string version of the address becomes the property name
+ // and the property value is the corresponding value (Other wise it
+ // would be impossible to know which of the fields the pattern matched
+ // a given value belongs to)
+ values := map[string]apiValues.PlcValue{}
+ for numericAddress, address := range matchedAddresses {
+ // Get the raw data from the cache
+ m.connection.valueCacheMutex.RLock()
+ int8s, _ := m.connection.valueCache[numericAddress]
+ m.connection.valueCacheMutex.RUnlock()
- // Decode the data according to the fields type
- rb := utils.NewReadBuffer(utils.Int8ToUint8(int8s))
- plcValue, err := driverModel.KnxDatapointParse(rb, field.GetTypeName())
- // If any of the values doesn't decode correctly, we can't return any
- if err != nil {
- responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATA
- plcValues[fieldName] = nil
- continue
- }
+ // Decode the data according to the fields type
+ rb := utils.NewReadBuffer(utils.Int8ToUint8(int8s))
+ plcValue, err := driverModel.KnxDatapointParse(rb, field.GetTypeName())
+ // If any of the values doesn't decode correctly, we can't return any
+ if err != nil {
+ responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATA
+ plcValues[fieldName] = nil
+ continue
+ }
- values[GroupAddressToString(address)] = plcValue
- }
+ values[GroupAddressToString(address)] = plcValue
+ }
- // Add it to the result
- responseCodes[fieldName] = apiModel.PlcResponseCode_OK
- plcValues[fieldName] = internalValues.NewPlcStruct(values)
- continue
- } else {
- // If it's not a pattern field, we can access the cached value a lot simpler
+ // Add it to the result
+ responseCodes[fieldName] = apiModel.PlcResponseCode_OK
+ plcValues[fieldName] = internalValues.NewPlcStruct(values)
+ continue
+ } else {
+ // If it's not a pattern field, we can access the cached value a lot simpler
- // Serialize the field to an uint16
- wb := utils.NewWriteBuffer()
- err = field.toGroupAddress().Serialize(*wb)
- if err != nil {
- responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
- plcValues[fieldName] = nil
- continue
- }
- rawAddress := wb.GetBytes()
- address := (uint16(rawAddress[0]) << 8) | uint16(rawAddress[1] & 0xFF)
+ // Serialize the field to an uint16
+ wb := utils.NewWriteBuffer()
+ err = field.toGroupAddress().Serialize(*wb)
+ if err != nil {
+ responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_ADDRESS
+ plcValues[fieldName] = nil
+ continue
+ }
+ rawAddress := wb.GetBytes()
+ address := (uint16(rawAddress[0]) << 8) | uint16(rawAddress[1]&0xFF)
- // Get the value form the cache
- int8s, ok := m.connection.valueCache[address]
- if !ok {
- responseCodes[fieldName] = apiModel.PlcResponseCode_NOT_FOUND
- plcValues[fieldName] = nil
- continue
- }
+ // Get the value form the cache
+ m.connection.valueCacheMutex.RLock()
+ int8s, ok := m.connection.valueCache[address]
+ m.connection.valueCacheMutex.RUnlock()
+ if !ok {
+ responseCodes[fieldName] = apiModel.PlcResponseCode_NOT_FOUND
+ plcValues[fieldName] = nil
+ continue
+ }
- // Decode the data according to the fields type
- rb := utils.NewReadBuffer(utils.Int8ToUint8(int8s))
- plcValue, err := driverModel.KnxDatapointParse(rb, field.GetTypeName())
- if err != nil {
- responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATA
- plcValues[fieldName] = nil
- continue
- }
+ // Decode the data according to the fields type
+ rb := utils.NewReadBuffer(utils.Int8ToUint8(int8s))
+ plcValue, err := driverModel.KnxDatapointParse(rb, field.GetTypeName())
+ if err != nil {
+ responseCodes[fieldName] = apiModel.PlcResponseCode_INVALID_DATA
+ plcValues[fieldName] = nil
+ continue
+ }
- // Add it to the result
- responseCodes[fieldName] = apiModel.PlcResponseCode_OK
- plcValues[fieldName] = plcValue
- }
- }
- result := internalModel.NewDefaultPlcReadResponse(readRequest, responseCodes, plcValues)
- resultChan <- apiModel.PlcReadRequestResult{
- Request: readRequest,
- Response: result,
- Err: nil,
- }
- }()
- return resultChan
+ // Add it to the result
+ responseCodes[fieldName] = apiModel.PlcResponseCode_OK
+ plcValues[fieldName] = plcValue
+ }
+ }
+ result := internalModel.NewDefaultPlcReadResponse(readRequest, responseCodes, plcValues)
+ resultChan <- apiModel.PlcReadRequestResult{
+ Request: readRequest,
+ Response: result,
+ Err: nil,
+ }
+ }()
+ return resultChan
}