Home | History | Annotate | Download | only in runtime
      1 // Copyright 2013 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package runtime_test
      6 
      7 import (
      8 	"fmt"
      9 	"math"
     10 	"reflect"
     11 	"runtime"
     12 	"sort"
     13 	"strings"
     14 	"sync"
     15 	"testing"
     16 )
     17 
     18 // negative zero is a good test because:
     19 //  1) 0 and -0 are equal, yet have distinct representations.
     20 //  2) 0 is represented as all zeros, -0 isn't.
     21 // I'm not sure the language spec actually requires this behavior,
     22 // but it's what the current map implementation does.
     23 func TestNegativeZero(t *testing.T) {
     24 	m := make(map[float64]bool, 0)
     25 
     26 	m[+0.0] = true
     27 	m[math.Copysign(0.0, -1.0)] = true // should overwrite +0 entry
     28 
     29 	if len(m) != 1 {
     30 		t.Error("length wrong")
     31 	}
     32 
     33 	for k := range m {
     34 		if math.Copysign(1.0, k) > 0 {
     35 			t.Error("wrong sign")
     36 		}
     37 	}
     38 
     39 	m = make(map[float64]bool, 0)
     40 	m[math.Copysign(0.0, -1.0)] = true
     41 	m[+0.0] = true // should overwrite -0.0 entry
     42 
     43 	if len(m) != 1 {
     44 		t.Error("length wrong")
     45 	}
     46 
     47 	for k := range m {
     48 		if math.Copysign(1.0, k) < 0 {
     49 			t.Error("wrong sign")
     50 		}
     51 	}
     52 }
     53 
     54 // nan is a good test because nan != nan, and nan has
     55 // a randomized hash value.
     56 func TestNan(t *testing.T) {
     57 	m := make(map[float64]int, 0)
     58 	nan := math.NaN()
     59 	m[nan] = 1
     60 	m[nan] = 2
     61 	m[nan] = 4
     62 	if len(m) != 3 {
     63 		t.Error("length wrong")
     64 	}
     65 	s := 0
     66 	for k, v := range m {
     67 		if k == k {
     68 			t.Error("nan disappeared")
     69 		}
     70 		if (v & (v - 1)) != 0 {
     71 			t.Error("value wrong")
     72 		}
     73 		s |= v
     74 	}
     75 	if s != 7 {
     76 		t.Error("values wrong")
     77 	}
     78 }
     79 
     80 // Maps aren't actually copied on assignment.
     81 func TestAlias(t *testing.T) {
     82 	m := make(map[int]int, 0)
     83 	m[0] = 5
     84 	n := m
     85 	n[0] = 6
     86 	if m[0] != 6 {
     87 		t.Error("alias didn't work")
     88 	}
     89 }
     90 
     91 func TestGrowWithNaN(t *testing.T) {
     92 	m := make(map[float64]int, 4)
     93 	nan := math.NaN()
     94 	m[nan] = 1
     95 	m[nan] = 2
     96 	m[nan] = 4
     97 	cnt := 0
     98 	s := 0
     99 	growflag := true
    100 	for k, v := range m {
    101 		if growflag {
    102 			// force a hashtable resize
    103 			for i := 0; i < 100; i++ {
    104 				m[float64(i)] = i
    105 			}
    106 			growflag = false
    107 		}
    108 		if k != k {
    109 			cnt++
    110 			s |= v
    111 		}
    112 	}
    113 	if cnt != 3 {
    114 		t.Error("NaN keys lost during grow")
    115 	}
    116 	if s != 7 {
    117 		t.Error("NaN values lost during grow")
    118 	}
    119 }
    120 
    121 type FloatInt struct {
    122 	x float64
    123 	y int
    124 }
    125 
    126 func TestGrowWithNegativeZero(t *testing.T) {
    127 	negzero := math.Copysign(0.0, -1.0)
    128 	m := make(map[FloatInt]int, 4)
    129 	m[FloatInt{0.0, 0}] = 1
    130 	m[FloatInt{0.0, 1}] = 2
    131 	m[FloatInt{0.0, 2}] = 4
    132 	m[FloatInt{0.0, 3}] = 8
    133 	growflag := true
    134 	s := 0
    135 	cnt := 0
    136 	negcnt := 0
    137 	// The first iteration should return the +0 key.
    138 	// The subsequent iterations should return the -0 key.
    139 	// I'm not really sure this is required by the spec,
    140 	// but it makes sense.
    141 	// TODO: are we allowed to get the first entry returned again???
    142 	for k, v := range m {
    143 		if v == 0 {
    144 			continue
    145 		} // ignore entries added to grow table
    146 		cnt++
    147 		if math.Copysign(1.0, k.x) < 0 {
    148 			if v&16 == 0 {
    149 				t.Error("key/value not updated together 1")
    150 			}
    151 			negcnt++
    152 			s |= v & 15
    153 		} else {
    154 			if v&16 == 16 {
    155 				t.Error("key/value not updated together 2", k, v)
    156 			}
    157 			s |= v
    158 		}
    159 		if growflag {
    160 			// force a hashtable resize
    161 			for i := 0; i < 100; i++ {
    162 				m[FloatInt{3.0, i}] = 0
    163 			}
    164 			// then change all the entries
    165 			// to negative zero
    166 			m[FloatInt{negzero, 0}] = 1 | 16
    167 			m[FloatInt{negzero, 1}] = 2 | 16
    168 			m[FloatInt{negzero, 2}] = 4 | 16
    169 			m[FloatInt{negzero, 3}] = 8 | 16
    170 			growflag = false
    171 		}
    172 	}
    173 	if s != 15 {
    174 		t.Error("entry missing", s)
    175 	}
    176 	if cnt != 4 {
    177 		t.Error("wrong number of entries returned by iterator", cnt)
    178 	}
    179 	if negcnt != 3 {
    180 		t.Error("update to negzero missed by iteration", negcnt)
    181 	}
    182 }
    183 
    184 func TestIterGrowAndDelete(t *testing.T) {
    185 	m := make(map[int]int, 4)
    186 	for i := 0; i < 100; i++ {
    187 		m[i] = i
    188 	}
    189 	growflag := true
    190 	for k := range m {
    191 		if growflag {
    192 			// grow the table
    193 			for i := 100; i < 1000; i++ {
    194 				m[i] = i
    195 			}
    196 			// delete all odd keys
    197 			for i := 1; i < 1000; i += 2 {
    198 				delete(m, i)
    199 			}
    200 			growflag = false
    201 		} else {
    202 			if k&1 == 1 {
    203 				t.Error("odd value returned")
    204 			}
    205 		}
    206 	}
    207 }
    208 
    209 // make sure old bucket arrays don't get GCd while
    210 // an iterator is still using them.
    211 func TestIterGrowWithGC(t *testing.T) {
    212 	m := make(map[int]int, 4)
    213 	for i := 0; i < 16; i++ {
    214 		m[i] = i
    215 	}
    216 	growflag := true
    217 	bitmask := 0
    218 	for k := range m {
    219 		if k < 16 {
    220 			bitmask |= 1 << uint(k)
    221 		}
    222 		if growflag {
    223 			// grow the table
    224 			for i := 100; i < 1000; i++ {
    225 				m[i] = i
    226 			}
    227 			// trigger a gc
    228 			runtime.GC()
    229 			growflag = false
    230 		}
    231 	}
    232 	if bitmask != 1<<16-1 {
    233 		t.Error("missing key", bitmask)
    234 	}
    235 }
    236 
    237 func testConcurrentReadsAfterGrowth(t *testing.T, useReflect bool) {
    238 	if runtime.GOMAXPROCS(-1) == 1 {
    239 		defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(16))
    240 	}
    241 	numLoop := 10
    242 	numGrowStep := 250
    243 	numReader := 16
    244 	if testing.Short() {
    245 		numLoop, numGrowStep = 2, 500
    246 	}
    247 	for i := 0; i < numLoop; i++ {
    248 		m := make(map[int]int, 0)
    249 		for gs := 0; gs < numGrowStep; gs++ {
    250 			m[gs] = gs
    251 			var wg sync.WaitGroup
    252 			wg.Add(numReader * 2)
    253 			for nr := 0; nr < numReader; nr++ {
    254 				go func() {
    255 					defer wg.Done()
    256 					for range m {
    257 					}
    258 				}()
    259 				go func() {
    260 					defer wg.Done()
    261 					for key := 0; key < gs; key++ {
    262 						_ = m[key]
    263 					}
    264 				}()
    265 				if useReflect {
    266 					wg.Add(1)
    267 					go func() {
    268 						defer wg.Done()
    269 						mv := reflect.ValueOf(m)
    270 						keys := mv.MapKeys()
    271 						for _, k := range keys {
    272 							mv.MapIndex(k)
    273 						}
    274 					}()
    275 				}
    276 			}
    277 			wg.Wait()
    278 		}
    279 	}
    280 }
    281 
    282 func TestConcurrentReadsAfterGrowth(t *testing.T) {
    283 	testConcurrentReadsAfterGrowth(t, false)
    284 }
    285 
    286 func TestConcurrentReadsAfterGrowthReflect(t *testing.T) {
    287 	testConcurrentReadsAfterGrowth(t, true)
    288 }
    289 
    290 func TestBigItems(t *testing.T) {
    291 	var key [256]string
    292 	for i := 0; i < 256; i++ {
    293 		key[i] = "foo"
    294 	}
    295 	m := make(map[[256]string][256]string, 4)
    296 	for i := 0; i < 100; i++ {
    297 		key[37] = fmt.Sprintf("string%02d", i)
    298 		m[key] = key
    299 	}
    300 	var keys [100]string
    301 	var values [100]string
    302 	i := 0
    303 	for k, v := range m {
    304 		keys[i] = k[37]
    305 		values[i] = v[37]
    306 		i++
    307 	}
    308 	sort.Strings(keys[:])
    309 	sort.Strings(values[:])
    310 	for i := 0; i < 100; i++ {
    311 		if keys[i] != fmt.Sprintf("string%02d", i) {
    312 			t.Errorf("#%d: missing key: %v", i, keys[i])
    313 		}
    314 		if values[i] != fmt.Sprintf("string%02d", i) {
    315 			t.Errorf("#%d: missing value: %v", i, values[i])
    316 		}
    317 	}
    318 }
    319 
    320 type empty struct {
    321 }
    322 
    323 func TestEmptyKeyAndValue(t *testing.T) {
    324 	a := make(map[int]empty, 4)
    325 	b := make(map[empty]int, 4)
    326 	c := make(map[empty]empty, 4)
    327 	a[0] = empty{}
    328 	b[empty{}] = 0
    329 	b[empty{}] = 1
    330 	c[empty{}] = empty{}
    331 
    332 	if len(a) != 1 {
    333 		t.Errorf("empty value insert problem")
    334 	}
    335 	if b[empty{}] != 1 {
    336 		t.Errorf("empty key returned wrong value")
    337 	}
    338 }
    339 
    340 // Tests a map with a single bucket, with same-lengthed short keys
    341 // ("quick keys") as well as long keys.
    342 func TestSingleBucketMapStringKeys_DupLen(t *testing.T) {
    343 	testMapLookups(t, map[string]string{
    344 		"x":    "x1val",
    345 		"xx":   "x2val",
    346 		"foo":  "fooval",
    347 		"bar":  "barval", // same key length as "foo"
    348 		"xxxx": "x4val",
    349 		strings.Repeat("x", 128): "longval1",
    350 		strings.Repeat("y", 128): "longval2",
    351 	})
    352 }
    353 
    354 // Tests a map with a single bucket, with all keys having different lengths.
    355 func TestSingleBucketMapStringKeys_NoDupLen(t *testing.T) {
    356 	testMapLookups(t, map[string]string{
    357 		"x":                      "x1val",
    358 		"xx":                     "x2val",
    359 		"foo":                    "fooval",
    360 		"xxxx":                   "x4val",
    361 		"xxxxx":                  "x5val",
    362 		"xxxxxx":                 "x6val",
    363 		strings.Repeat("x", 128): "longval",
    364 	})
    365 }
    366 
    367 func testMapLookups(t *testing.T, m map[string]string) {
    368 	for k, v := range m {
    369 		if m[k] != v {
    370 			t.Fatalf("m[%q] = %q; want %q", k, m[k], v)
    371 		}
    372 	}
    373 }
    374 
    375 // Tests whether the iterator returns the right elements when
    376 // started in the middle of a grow, when the keys are NaNs.
    377 func TestMapNanGrowIterator(t *testing.T) {
    378 	m := make(map[float64]int)
    379 	nan := math.NaN()
    380 	const nBuckets = 16
    381 	// To fill nBuckets buckets takes LOAD * nBuckets keys.
    382 	nKeys := int(nBuckets * *runtime.HashLoad)
    383 
    384 	// Get map to full point with nan keys.
    385 	for i := 0; i < nKeys; i++ {
    386 		m[nan] = i
    387 	}
    388 	// Trigger grow
    389 	m[1.0] = 1
    390 	delete(m, 1.0)
    391 
    392 	// Run iterator
    393 	found := make(map[int]struct{})
    394 	for _, v := range m {
    395 		if v != -1 {
    396 			if _, repeat := found[v]; repeat {
    397 				t.Fatalf("repeat of value %d", v)
    398 			}
    399 			found[v] = struct{}{}
    400 		}
    401 		if len(found) == nKeys/2 {
    402 			// Halfway through iteration, finish grow.
    403 			for i := 0; i < nBuckets; i++ {
    404 				delete(m, 1.0)
    405 			}
    406 		}
    407 	}
    408 	if len(found) != nKeys {
    409 		t.Fatalf("missing value")
    410 	}
    411 }
    412 
    413 func TestMapIterOrder(t *testing.T) {
    414 	for _, n := range [...]int{3, 7, 9, 15} {
    415 		for i := 0; i < 1000; i++ {
    416 			// Make m be {0: true, 1: true, ..., n-1: true}.
    417 			m := make(map[int]bool)
    418 			for i := 0; i < n; i++ {
    419 				m[i] = true
    420 			}
    421 			// Check that iterating over the map produces at least two different orderings.
    422 			ord := func() []int {
    423 				var s []int
    424 				for key := range m {
    425 					s = append(s, key)
    426 				}
    427 				return s
    428 			}
    429 			first := ord()
    430 			ok := false
    431 			for try := 0; try < 100; try++ {
    432 				if !reflect.DeepEqual(first, ord()) {
    433 					ok = true
    434 					break
    435 				}
    436 			}
    437 			if !ok {
    438 				t.Errorf("Map with n=%d elements had consistent iteration order: %v", n, first)
    439 				break
    440 			}
    441 		}
    442 	}
    443 }
    444 
    445 // Issue 8410
    446 func TestMapSparseIterOrder(t *testing.T) {
    447 	// Run several rounds to increase the probability
    448 	// of failure. One is not enough.
    449 NextRound:
    450 	for round := 0; round < 10; round++ {
    451 		m := make(map[int]bool)
    452 		// Add 1000 items, remove 980.
    453 		for i := 0; i < 1000; i++ {
    454 			m[i] = true
    455 		}
    456 		for i := 20; i < 1000; i++ {
    457 			delete(m, i)
    458 		}
    459 
    460 		var first []int
    461 		for i := range m {
    462 			first = append(first, i)
    463 		}
    464 
    465 		// 800 chances to get a different iteration order.
    466 		// See bug 8736 for why we need so many tries.
    467 		for n := 0; n < 800; n++ {
    468 			idx := 0
    469 			for i := range m {
    470 				if i != first[idx] {
    471 					// iteration order changed.
    472 					continue NextRound
    473 				}
    474 				idx++
    475 			}
    476 		}
    477 		t.Fatalf("constant iteration order on round %d: %v", round, first)
    478 	}
    479 }
    480 
    481 func TestMapStringBytesLookup(t *testing.T) {
    482 	// Use large string keys to avoid small-allocation coalescing,
    483 	// which can cause AllocsPerRun to report lower counts than it should.
    484 	m := map[string]int{
    485 		"1000000000000000000000000000000000000000000000000": 1,
    486 		"2000000000000000000000000000000000000000000000000": 2,
    487 	}
    488 	buf := []byte("1000000000000000000000000000000000000000000000000")
    489 	if x := m[string(buf)]; x != 1 {
    490 		t.Errorf(`m[string([]byte("1"))] = %d, want 1`, x)
    491 	}
    492 	buf[0] = '2'
    493 	if x := m[string(buf)]; x != 2 {
    494 		t.Errorf(`m[string([]byte("2"))] = %d, want 2`, x)
    495 	}
    496 
    497 	var x int
    498 	n := testing.AllocsPerRun(100, func() {
    499 		x += m[string(buf)]
    500 	})
    501 	if n != 0 {
    502 		t.Errorf("AllocsPerRun for m[string(buf)] = %v, want 0", n)
    503 	}
    504 
    505 	x = 0
    506 	n = testing.AllocsPerRun(100, func() {
    507 		y, ok := m[string(buf)]
    508 		if !ok {
    509 			panic("!ok")
    510 		}
    511 		x += y
    512 	})
    513 	if n != 0 {
    514 		t.Errorf("AllocsPerRun for x,ok = m[string(buf)] = %v, want 0", n)
    515 	}
    516 }
    517 
    518 func TestMapLargeKeyNoPointer(t *testing.T) {
    519 	const (
    520 		I = 1000
    521 		N = 64
    522 	)
    523 	type T [N]int
    524 	m := make(map[T]int)
    525 	for i := 0; i < I; i++ {
    526 		var v T
    527 		for j := 0; j < N; j++ {
    528 			v[j] = i + j
    529 		}
    530 		m[v] = i
    531 	}
    532 	runtime.GC()
    533 	for i := 0; i < I; i++ {
    534 		var v T
    535 		for j := 0; j < N; j++ {
    536 			v[j] = i + j
    537 		}
    538 		if m[v] != i {
    539 			t.Fatalf("corrupted map: want %+v, got %+v", i, m[v])
    540 		}
    541 	}
    542 }
    543 
    544 func TestMapLargeValNoPointer(t *testing.T) {
    545 	const (
    546 		I = 1000
    547 		N = 64
    548 	)
    549 	type T [N]int
    550 	m := make(map[int]T)
    551 	for i := 0; i < I; i++ {
    552 		var v T
    553 		for j := 0; j < N; j++ {
    554 			v[j] = i + j
    555 		}
    556 		m[i] = v
    557 	}
    558 	runtime.GC()
    559 	for i := 0; i < I; i++ {
    560 		var v T
    561 		for j := 0; j < N; j++ {
    562 			v[j] = i + j
    563 		}
    564 		v1 := m[i]
    565 		for j := 0; j < N; j++ {
    566 			if v1[j] != v[j] {
    567 				t.Fatalf("corrupted map: want %+v, got %+v", v, v1)
    568 			}
    569 		}
    570 	}
    571 }
    572 
    573 func benchmarkMapPop(b *testing.B, n int) {
    574 	m := map[int]int{}
    575 	for i := 0; i < b.N; i++ {
    576 		for j := 0; j < n; j++ {
    577 			m[j] = j
    578 		}
    579 		for j := 0; j < n; j++ {
    580 			// Use iterator to pop an element.
    581 			// We want this to be fast, see issue 8412.
    582 			for k := range m {
    583 				delete(m, k)
    584 				break
    585 			}
    586 		}
    587 	}
    588 }
    589 
    590 func BenchmarkMapPop100(b *testing.B)   { benchmarkMapPop(b, 100) }
    591 func BenchmarkMapPop1000(b *testing.B)  { benchmarkMapPop(b, 1000) }
    592 func BenchmarkMapPop10000(b *testing.B) { benchmarkMapPop(b, 10000) }
    593 
    594 func TestNonEscapingMap(t *testing.T) {
    595 	n := testing.AllocsPerRun(1000, func() {
    596 		m := make(map[int]int)
    597 		m[0] = 0
    598 	})
    599 	if n != 0 {
    600 		t.Fatalf("want 0 allocs, got %v", n)
    601 	}
    602 }
    603