That is not correct. See https://go.dev/play/p/aZ7LostRuKR
You can run this under the race detector without failure. Still, the race detector cannot detect all races so better to look at the source for sync.Map at 105 <https://cs.opensource.google/go/go/+/master:src/sync/map.go;drc=2580d0e08d5e9f979b943758d3c49877fb2324cb;l=105> You can see that it reads the underlying shared maps while not under lock/exclusive access. Starting a go routine creates a “happens before” relationship which allows this to work. > On Feb 23, 2022, at 12:10 AM, Jason E. Aten <j.e.a...@gmail.com> wrote: > > Unfortunately, that's not how Go maps work. You'll still get races unless you > synchronize even "read-only" maps. > > On Tuesday, February 22, 2022 at 11:59:37 PM UTC-6 ren...@ix.netcom.com wrote: > The top level map locks doesn’t matter - it is write once at init and read > only after. > >> On Feb 22, 2022, at 11:51 PM, Jason E. Aten <j.e....@gmail.com >> <applewebdata://5154297F-AADE-4DDF-B6C1-9D23F74248D4>> wrote: >> >> You have not synchronized access to the top level mapLocks map itself. >> Thus you will get races. You need to have a mutex that you lock before you >> access mapLocks; not just the maps that are inside it. > >> >> I would also recommend being a little object oriented about this design, >> putting each map inside its own struct, and make a set of accessor methods >> that Lock() and defer Unlock() on a mutex that protects the maps inside. >> Read code like this for an idea of what I mean. >> https://github.com/glycerine/atomicmap/blob/master/amap.go >> <https://github.com/glycerine/atomicmap/blob/master/amap.go> >> >> On Tuesday, February 22, 2022 at 9:55:04 PM UTC-6 ren...@ix.netcom.com >> <applewebdata://5154297F-AADE-4DDF-B6C1-9D23F74248D4> wrote: >> Something else is wrong - because marketMaps is read-only after init… so >> unless you have some other code - that is not the map causing the panic. >> >> My guess is because you are returning the map from ReadMaps (or RWMaps) that >> you are using THAT map by multiple threads outside the lock (the map is a >> reference not a copy) and that is causing the panic. >> >> >>> On Feb 22, 2022, at 9:39 PM, Zhaoxun Yan <yan.z...@gmail.com <>> wrote: >>> >> >>> Hi guys! >>> I know this is quite challenging, but it makes sense in io-heavy >>> applications. >>> >>> I need to test the responsiveness of different financial future quotation >>> sources. They return the quotations spontaneously, and my program respond >>> to each one of the CGO call backs from the quotation library and save the >>> current prices. Meanwhile, a timer ensures to check the current quotations >>> of each available sources. And obviously, it raised up a panic due to >>> synchronization of maps on the call of ReadMaps function. Unfortunately >>> this came up occasionally and `go build -race` cannot raise the problem. >>> >>> Here is a piece of the code, only the read/write part: >>> >>> ------------------------------------------------------------------------------------------ >>> >>> // Please Ignore SyChan to alert the timer that all available sources are >>> collected >>> var SyChan chan int = make(chan int, 3) >>> >>> // Make up a map of 5 mutex locks and a map of 5 data structs, indexed by >>> 1-5 >>> var marketMaps = make(map[int]map[string]Market) >>> var mapLocks = make(map[int]*sync.Mutex) >>> >>> // quotation data >>> type Market struct { >>> Price float64 `json:"price"` >>> Timestamp string `json:"timestamp"` >>> } >>> >>> func init() { >>> for i := 1; i <= 5; i++ { >>> mapLocks[i] = new(sync.Mutex) >>> marketMaps[i] = make(map[string]Market) >>> } >>> } >>> >>> //Make sure that for each source, R/W has no race, only took place as >>> acquiring the particular Mutex inside the map of Mutex. >>> func RWMaps(targetnum int, purpose, future_id string, market Market) >>> map[string]Market { >>> >>> mapLocks[targetnum].Lock() >>> defer mapLocks[targetnum].Unlock() >>> >>> if purpose == "update" {//The original Write part >>> marketMaps[targetnum][future_id] = market >>> return nil >>> } else { //The read part, has been extracted to ReadMaps >>> SyChan <- 1 >>> return marketMaps[targetnum] >>> } >>> } >>> >>> //Here is why I use map: not all 5 sources must be available, some may have >>> connection failure and would be marked as false in Usable[i] , i being its >>> source No. >>> func ReadMaps(targetnum, checkTime int) map[string]Market { >>> mapLocks[targetnum].Lock() >>> defer mapLocks[targetnum].Unlock() >>> if Usable[targetnum] { >>> fmt.Printf("%d-th time to read source %d \n", checkTime, >>> targetnum) >>> } >>> SyChan <- 1 >>> return marketMaps[targetnum] >>> } >>> >>> -------------------------------------------------------------------------------------------------- >>> >>> My problem is : >>> >>> I still want to keep the map structure, rather than naming mutex1, mutex2, >>> mutex3,... , marketmsg1, marketmsg2, .... And obviously if the map >>> prohibits reading or writing spontaneously, the writing by each usable >>> sources is not fair - They must line up as one queue (like using one Mutex >>> for all instead) hence the checking of quotation snap on a time-spot is >>> defected. I have also checked the sync.Map and it seems to allow >>> spontaneous reading but still prohibits spontaneous writing. >>> >>> My instinct is to make map on the pointer of structs. On that way, if I >>> could use sync.Map, both the read and write function only read the map to >>> find the address of data structs, then writing several data structs on the >>> same time won't violate the rule on sync.Map. >>> >>> Is the proposal above legitimate? And anyone could come up a test case to >>> mimic the quotation sources? Because with CGO to complicate the program, go >>> cannot raise the race problem on -race check, and the successful build can >>> panic occasionally. >>> >>> Thanks in advance! >>> >> >>> -- >>> You received this message because you are subscribed to the Google Groups >>> "golang-nuts" group. >>> To unsubscribe from this group and stop receiving emails from it, send an >>> email to golang-nuts...@googlegroups.com <>. >>> To view this discussion on the web visit >>> https://groups.google.com/d/msgid/golang-nuts/9a981200-23f8-4dae-8c20-7acfdcd3f2fcn%40googlegroups.com >>> >>> <https://groups.google.com/d/msgid/golang-nuts/9a981200-23f8-4dae-8c20-7acfdcd3f2fcn%40googlegroups.com?utm_medium=email&utm_source=footer>. >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "golang-nuts" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to golang-nuts...@googlegroups.com >> <applewebdata://5154297F-AADE-4DDF-B6C1-9D23F74248D4>. > >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/golang-nuts/215a541a-ecc6-49e2-9427-81f62c5b168en%40googlegroups.com >> >> <https://groups.google.com/d/msgid/golang-nuts/215a541a-ecc6-49e2-9427-81f62c5b168en%40googlegroups.com?utm_medium=email&utm_source=footer>. > > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com > <mailto:golang-nuts+unsubscr...@googlegroups.com>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/0b709930-8fbb-4fce-8cdf-0cf3bb401d22n%40googlegroups.com > > <https://groups.google.com/d/msgid/golang-nuts/0b709930-8fbb-4fce-8cdf-0cf3bb401d22n%40googlegroups.com?utm_medium=email&utm_source=footer>. -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/A04B8C4C-3C0C-4077-9FCF-5632D359C664%40ix.netcom.com.