quickfix icon indicating copy to clipboard operation
quickfix copied to clipboard

concurrent map read and map write

Open onsails opened this issue 7 years ago • 7 comments

Hello, sometime I receive concurrent read & write error when I try to log the whole struct or just a single value. I don't modify structs I received from quickfix, just read them.

fatal error: concurrent map read and map write

goroutine 51 [running]:
runtime.throw(0x497c353, 0x21)
	/usr/local/opt/go/libexec/src/runtime/panic.go:605 +0x95 fp=0xc4201d59b8 sp=0xc4201d5998 pc=0x402c9b5
runtime.mapaccess2_fast64(0x4857f80, 0xc420146960, 0x6, 0x1, 0x5f10028)
	/usr/local/opt/go/libexec/src/runtime/hashmap_fast.go:168 +0x1b7 fp=0xc4201d59e0 sp=0xc4201d59b8 pc=0x400e0e7
metabreak/vendor/github.com/quickfixgo/quickfix.FieldMap.GetField(0xc420146960, 0xc420074200, 0x16, 0x20, 0x498c898, 0x6, 0x5f10028, 0xc42019c1a0, 0x490d440, 0x1)
	/Users/wb/dev/go/src/metabreak/vendor/github.com/quickfixgo/quickfix/field_map.go:79 +0x48 fp=0xc4201d5a48 sp=0xc4201d59e0 pc=0x42391e8
metabreak/vendor/github.com/quickfixgo/quickfix.FieldMap.Get(0xc420146960, 0xc420074200, 0x16, 0x20, 0x498c898, 0x517fee0, 0xc42019c1a0, 0x0, 0x0)
	/Users/wb/dev/go/src/metabreak/vendor/github.com/quickfixgo/quickfix/field_map.go:68 +0xb8 fp=0xc4201d5ab0 sp=0xc4201d5a48 pc=0x4239108
metabreak/vendor/github.com/quickfixgo/fix44/executionreport.ExecutionReport.GetAvgPx(0xc420166270, 0xc4201662c0, 0xc420166298, 0xc420166270, 0xc4201a2329, 0x1, 0x0, 0x0)
	/Users/wb/dev/go/src/metabreak/vendor/github.com/quickfixgo/fix44/executionreport/ExecutionReport.generated.go:1165 +0x79 fp=0xc4201d5b10 sp=0xc4201d5ab0 pc=0x440a809
metabreak/fix.state.trade(0xc4200414d0, 0xc420076360, 0xc4200763c0, 0xc420076420, 0xc420076480)
	/Users/wb/dev/go/src/metabreak/fix/dumb.go:85 +0x807 fp=0xc4201d5fb8 sp=0xc4201d5b10 pc=0x46468f7
runtime.goexit()
	/usr/local/opt/go/libexec/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc4201d5fc0 sp=0xc4201d5fb8 pc=0x4059b91
created by metabreak/fix.Dumb
	/Users/wb/dev/go/src/metabreak/fix/dumb.go:59 +0x407
		case rep := <-s.execrep:
			clOrdID, _ := rep.GetClOrdID()
			avgPx, _ := rep.GetAvgPx()
			ordStatus, _ := rep.GetOrdStatus()
			netMoney, _ := rep.GetNetMoney() // here is dumb:85
			transactTime, _ := rep.GetTransactTime()
			rejectReason, _ := rep.GetOrdRejReason()
			execType, _ := rep.GetExecType()

It happens from time to time, in various places involving quickfix struct values reads.

onsails avatar Feb 05 '18 22:02 onsails

Can you try re-running with the -race option? This should help pinpoint where this is happening.

cbusbey avatar Feb 07 '18 13:02 cbusbey

@cbusbey hello, I also met this issue.

I think the reason is that Maps are not safe for concurrent use.

Please help to confirm, thanks a lot!

type FieldMap struct { tagLookup map[Tag]field tagSort }

zxwq avatar Apr 27 '18 07:04 zxwq

Maps are not safe for concurrent use. As FieldMaps are basically an embedded map, FieldMaps are also not safe for concurrent use.

cbusbey avatar Apr 27 '18 11:04 cbusbey

Maps are not safe for concurrent use. As FieldMaps are basically an embedded map, FieldMaps are also not safe for concurrent use.

Then why not to fix this? Currently it's impossible to write asynchronous applications with this lib out of the box.

demget avatar Oct 17 '20 16:10 demget

I'm also experiencing an intermittent panic around concurrent map access, but within store.go, line 99:

func (store memoryStore) GetMessages(beginSeqNum, endSeqNum int) ([][]byte, error) {
	var msgs [][]byte
	for seqNum := beginSeqNum; seqNum <= endSeqNum; seqNum++ {
		if m, ok := store.messageMap[seqNum]; ok {
			msgs = append(msgs, m)
		}
	}
	return msgs, nil
}

I just have a simple test acceptor that spits out the received messages to a table, and a test initiator that reads 1k messages from a raw FIX file, and pumps them onto the connection.

I'm not doing anything concurrent, just a line reader:

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		msg := quickfix.NewMessage()
		buf := bytes.NewBuffer(scanner.Bytes())
		err := quickfix.ParseMessage(msg, buf)
		checkErr(err)
		err = quickfix.Send(msg)
		checkErr(err)
	}

Simply adding in a delay <-time.After(5 * time.Millisecond) within the above for loop seems to alleviate it. Seems like the memorystore's internal map should be safe for concurrent use?

UPDATE For my particular case, I believe it's caused by the fact that there are 2 separate SenderCompID's within my file, and it looks like those are handled within separate go routines within quickfix.

juztin avatar Nov 05 '20 22:11 juztin

MarketDataIncrementalRefresh callback func There is a blocking problem

The data is delayed and cannot be processed concurrently.

Is there any good solution? @cbusbey

alonexy avatar Sep 27 '21 09:09 alonexy

Ran into this today, had issues using the quickfix.Message after I had received it from a channel - still have to investigate what quickfix was is doing with the message after it is received in the FromApp(...) function.

Workaround was to send a copy of the message like so.

original := newordersingle.New(...) // or wherever you get the message from

temp := new(quickfix.Message)
original.CopyInto(temp)
copy := newordersingle.FromMessage(temp)

outchan <- copy

✌️

kleinerhund avatar May 12 '22 21:05 kleinerhund