-
Notifications
You must be signed in to change notification settings - Fork 63
Closed
Description
Description
Multiple goroutines calling MQObject.Inq at the same time (even on different MQObjects!) can result in fatal error: concurrent map writes.
Why
Inq calls getAttrInfo which writes the global mqInqLength map guarded by charAttrsAdded. However, such a guard is not reliable when there are concurrent calls.
Dump would look something like:
fatal error: concurrent map writes
goroutine 92 [running]:
github.com/ibm-messaging/mq-golang/v5/ibmmq.getAttrInfo({0xc00100a008, 0x2, 0x46efee?})
/go/src/github.com/kalmant/my-repo/vendor/github.com/ibm-messaging/mq-golang/v5/ibmmq/mqiattrs.go:156 +0x12f
github.com/ibm-messaging/mq-golang/v5/ibmmq.MQObject.Inq({0xc0cb40?, 0xc000e0a0f0?, {0xc001006030?, 0xc001004000?}}, {0xc00100a008?, 0x2, 0x2})
/go/src/github.com/kalmant/my-repo/vendor/github.com/ibm-messaging/mq-golang/v5/ibmmq/mqi.go:814 +0xc5
...
Suggested solution
If the goal is to ensure the code block in mqiattrs.go L144-159 only gets called once then using sync.Once would be a perfect solution.
I'm opening a PR (#204) with this solution. Feel free to close that if you believe there's a better solution or I misunderstood the root cause.
Versions
- go version go1.21.X linux/amd64
- mq-golang version: v5.5.1
- MQ version: 9.3.X (but I don't think this matters)
Small code sample that demonstrates the issue
package main
import (
"github.com/ibm-messaging/mq-golang/v5/ibmmq"
)
func getBackoutParameters(queue *ibmmq.MQObject) error {
selectors := []int32{
ibmmq.MQCA_BACKOUT_REQ_Q_NAME,
ibmmq.MQIA_BACKOUT_THRESHOLD,
}
// values are not important from the POV of reproducing the issue
_, err := queue.Inq(selectors)
return err
}
func initializeQueues() []*ibmmq.MQObject {
// NOTE: these queues would be created by opening them on *different* `ibmmq.MQQueueManager` instances
// using e.g. `ibmmq.MQOO_INPUT_EXCLUSIVE|ibmmq.MQOO_INQUIRE` as options
// let's say we return a slice of 10 pointers
return nil
}
func main() {
queues := initializeQueues()
for _, q := range queues {
q := q
go func() {
if err := getBackoutParameters(q); err != nil {
panic(err)
}
}()
}
}Metadata
Metadata
Assignees
Labels
No labels