cornelk / hashmap

A Golang lock-free thread-safe HashMap optimized for fastest read access.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Race detector warnings

lunemec opened this issue · comments

Hi, I'm getting race detector warnings when accessing a hashmap from multiple goroutines:

WARNING: DATA RACE
Read at 0x00c001b7dbd0 by goroutine 64:
  sync/atomic.LoadInt64()
      /usr/local/Cellar/go/1.11.1/libexec/src/runtime/race_amd64.s:211 +0xb
  .../vendor/github.com/cornelk/hashmap.(*ListElement).Next()
      /.../vendor/github.com/cornelk/hashmap/listelement.go:25 +0x3e
  .../vendor/github.com/cornelk/hashmap.(*HashMap).fillIndexItems()
      /.../vendor/github.com/cornelk/hashmap/hashmap.go:373 +0x77
  .../vendor/github.com/cornelk/hashmap.(*HashMap).grow()
      /.../vendor/github.com/cornelk/hashmap/hashmap.go:339 +0x292

Previous write at 0x00c001b7dbd0 by main goroutine:
  .../vendor/github.com/cornelk/hashmap.(*List).insertAt()
      /.../vendor/github.com/cornelk/hashmap/list.go:128 +0x234
  .../vendor/github.com/cornelk/hashmap.(*List).AddOrUpdate()
      /.../vendor/github.com/cornelk/hashmap/list.go:65 +0xcf
  .../vendor/github.com/cornelk/hashmap.(*HashMap).insertListElement()
      /.../vendor/github.com/cornelk/hashmap/hashmap.go:237 +0xc8
  core-next/vendor/github.com/cornelk/hashmap.(*HashMap).Set()
      /.../vendor/github.com/cornelk/hashmap/hashmap.go:211 +0x172
      /.../main.go:45 +0xd3

But the strange part is, this is code running in a single goroutine - just iterating over set of data and calling .Set() on the hashmap.Hashmap.

Example:

var dataMap hashmap.HashMap
for _, data := range listOfData {
	var innerMap hashmap.HashMap
	for _, innerData := range data.InnerList {
		innerMap.Set(innerData, struct{}{})
	}
	dataMap.Set(data.ID, innerMap)
}

Any ideas what to do here? Am I using the hashmap.Hashmap wrong?

@lunemec are you using the current master branch that included a race fix? if so, can you create a unit test to replicate that behavior?

Well, I only noticed when creating kinda huge hashmaps. I tested this with my tests, but it did not trigger. Only when I load real data 70MB in heap, then it triggers this. My current revision is this: b893f49

I'll try to force the newest commit with the fix - dep automatically takes released version.

Nope, the current master does not work either. I'll write a test case for this.

Well, I kinda created this problem:

func TestHashMap_SetWithContainer(t *testing.T) {
	type Container struct {
		Map HashMap
	}
	type mapItem struct {
		InnerData HashMap
	}
	var container Container
	for i := 0; i < 10; i++ {
		var innerMap HashMap
		for z := 0; z < 10; z++ {
			innerMap.Set(z, struct{}{})
		}
		container.Map.Set(i, mapItem{InnerData: innerMap})
	}
}

Well at least I can now fix it by simply moving the HashMap outside of the struct, but I have some metadata packed with it, so that would not be ideal.

@cornelk do you think a fix is possible? Or should I work around this?

this is the fixed unit test without a race condition:

func TestHashMap_SetWithContainer(t *testing.T) {
	type Container struct {
		Map *HashMap
	}
	type mapItem struct {
		InnerData *HashMap
	}
	container := Container{Map: &HashMap{}}
	for i := 0; i < 10; i++ {
		innerMap := &HashMap{}
		for z := 0; z < 10; z++ {
			innerMap.Set(z, struct{}{})
		}
		container.Map.Set(i, mapItem{InnerData: innerMap})
	}
}

Okay, so it was misunderstanding of usage on my part. Could you maybe update readme/doc with a example of this?

The readme and tests already contain code that shows how to initiate the map: m := &HashMap{}