github.com/kazu/loncha@v0.6.3/list_head/list_head_test.go (about) 1 package list_head_test 2 3 import ( 4 "errors" 5 "fmt" 6 "math/rand" 7 "sync/atomic" 8 "testing" 9 "unsafe" 10 11 "github.com/stretchr/testify/assert" 12 13 "github.com/kazu/loncha" 14 "github.com/kazu/loncha/list_head" 15 ) 16 17 func TestInit(t *testing.T) { 18 list := list_head.ListHead{} 19 list.Init() 20 21 assert.Equal(t, &list, list.Prev()) 22 assert.Equal(t, &list, list.Next()) 23 24 } 25 26 func TestAdd(t *testing.T) { 27 first := list_head.ListHead{} 28 first.Init() 29 30 second := list_head.ListHead{} 31 second.Init() 32 33 first.Add(&second) 34 35 assert.Equal(t, first.Prev(), &second) 36 assert.Equal(t, first.Next(), &second) 37 assert.Equal(t, second.Prev(), &first) 38 assert.Equal(t, second.Next(), &first) 39 40 } 41 42 func TestAddWithConcurrent(t *testing.T) { 43 list_head.MODE_CONCURRENT = true 44 45 first := list_head.ListHead{} 46 first.Init() 47 48 second := list_head.ListHead{} 49 second.Init() 50 51 first.Add(&second) 52 53 assert.Equal(t, first.Prev(), &second) 54 assert.Equal(t, first.Next(), &second) 55 assert.Equal(t, second.Prev(), &first) 56 assert.Equal(t, second.Next(), &first) 57 58 } 59 60 func TestDelete(t *testing.T) { 61 first := list_head.ListHead{} 62 first.Init() 63 64 second := list_head.ListHead{} 65 second.Init() 66 67 first.Add(&second) 68 69 assert.Equal(t, first.Prev(), &second) 70 assert.Equal(t, first.Next(), &second) 71 assert.Equal(t, second.Prev(), &first) 72 assert.Equal(t, second.Next(), &first) 73 74 second.Delete() 75 76 assert.Equal(t, first.Prev(), &first) 77 assert.Equal(t, first.Next(), 78 &first, fmt.Sprintf("first=%+v next=%+v", &first, first.Next())) 79 assert.True(t, first.Empty()) 80 assert.True(t, first.IsLast()) 81 assert.Equal(t, second.Prev(), &second) 82 assert.Equal(t, second.Next(), &second) 83 84 } 85 86 func TestDeleteWithConcurrent(t *testing.T) { 87 list_head.MODE_CONCURRENT = true 88 first := list_head.ListHead{} 89 first.Init() 90 91 second := list_head.ListHead{} 92 second.Init() 93 94 first.Add(&second) 95 96 assert.Equal(t, first.Prev(), &second) 97 assert.Equal(t, first.Next(), &second) 98 assert.Equal(t, second.Prev(), &first) 99 assert.Equal(t, second.Next(), &first) 100 101 second.Delete() 102 103 assert.Equal(t, first.Prev(), &first) 104 assert.Equal(t, first.Next(), 105 &first, fmt.Sprintf("first=%+v next=%+v", &first, first.Next())) 106 assert.True(t, first.Empty()) 107 assert.True(t, first.IsLast()) 108 assert.Equal(t, second.Prev(), &second) 109 assert.Equal(t, second.Next(), &second) 110 111 } 112 113 type Hoge struct { 114 ID int 115 Name string 116 list_head.ListHead 117 } 118 119 func NewHogeWithList(h *Hoge) *Hoge { 120 h.Init() 121 return h 122 } 123 124 func (d *Hoge) Init() { 125 d.ListHead.Init() 126 } 127 128 func (d *Hoge) Next() *Hoge { 129 if d.ListHead.Next() == nil { 130 panic(errors.New("d.next is nil")) 131 } 132 return (*Hoge)(unsafe.Pointer(uintptr(unsafe.Pointer(d.ListHead.Next())) - unsafe.Offsetof(d.ListHead))) 133 } 134 135 func (d *Hoge) Prev() *Hoge { 136 if d.ListHead.Next() == nil { 137 panic(errors.New("d.prev is nil")) 138 } 139 return (*Hoge)(unsafe.Pointer(uintptr(unsafe.Pointer(d.ListHead.Prev())) - unsafe.Offsetof(d.ListHead))) 140 } 141 142 func (d *Hoge) Add(n *Hoge) { 143 if n.ListHead.Next() == nil || n.ListHead.Prev() == nil { 144 panic(errors.New("d is initialized")) 145 } 146 d.ListHead.Add(&n.ListHead) 147 } 148 149 func (d *Hoge) Delete() *Hoge { 150 ptr := d.ListHead.Delete() 151 return (*Hoge)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) - unsafe.Offsetof(d.ListHead))) 152 } 153 154 func (d *Hoge) ContainOf(ptr *list_head.ListHead) *Hoge { 155 return (*Hoge)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) - unsafe.Offsetof(d.ListHead))) 156 } 157 158 func TestContainerListAdd(t *testing.T) { 159 list_head.MODE_CONCURRENT = true 160 var list Hoge 161 list.Init() 162 163 hoge := Hoge{ID: 1, Name: "aaa"} 164 hoge.Init() 165 list.Add(&hoge) 166 167 hoge2 := Hoge{ID: 2, Name: "bbb"} 168 hoge2.Init() 169 170 hoge.Add(&hoge2) 171 172 assert.Equal(t, hoge.Next().ID, 2) 173 assert.Equal(t, hoge.Len(), 2) 174 assert.Equal(t, hoge.Next().Len(), 2) 175 } 176 177 func TestNext(t *testing.T) { 178 list_head.MODE_CONCURRENT = true 179 180 var head list_head.ListHead 181 182 head.Init() 183 184 marked := 0 185 186 for i := 0; i < 10; i++ { 187 e := &list_head.ListHead{} 188 e.Init() 189 head.Add(e) 190 } 191 192 elm := &head 193 194 for { 195 fmt.Printf("1: elm=%s\n", elm.Pp()) 196 if elm == elm.Next() { 197 break 198 } 199 elm = elm.Next() 200 marked++ 201 } 202 203 assert.Equal(t, 10, marked) 204 fmt.Println("-----") 205 marked = 0 206 elm = head.Next() 207 //elm = &head 208 for { 209 fmt.Printf("2: elm=%s\n", elm.Pp()) 210 if elm == elm.Next() { 211 break 212 } 213 214 if rand.Intn(2) == 0 { 215 elm2 := elm.Next() 216 elm.MarkForDelete() 217 marked++ 218 elm = elm2 219 continue 220 } 221 elm = elm.Next() 222 223 } 224 fmt.Println("-----") 225 cnt := 0 226 elm = &head 227 for { 228 if elm == elm.Next() { 229 break 230 } 231 elm = elm.Next() 232 cnt++ 233 } 234 235 assert.Equal(t, 10-marked, cnt) 236 assert.Equal(t, cnt, head.Len()) 237 238 } 239 240 func TestNextNew(t *testing.T) { 241 242 tests := []struct { 243 Name string 244 Count int 245 marked []int 246 }{ 247 { 248 Name: "first middle last marked", 249 Count: 10, 250 marked: []int{0, 5, 9}, 251 }, 252 { 253 Name: "continus marked", 254 Count: 10, 255 marked: []int{4, 5, 6}, 256 }, 257 { 258 Name: "continus marked in last", 259 Count: 10, 260 marked: []int{3, 4, 5, 8, 9}, 261 }, 262 { 263 Name: "continus marked in first", 264 Count: 10, 265 marked: []int{0, 1, 2, 4, 5, 6}, 266 }, 267 { 268 Name: "all deleted", 269 Count: 3, 270 marked: []int{0, 1, 2}, 271 }, 272 } 273 274 makeElement := func() *list_head.ListHead { 275 e := &list_head.ListHead{} 276 e.Init() 277 return e 278 } 279 280 list_head.MODE_CONCURRENT = true 281 282 for _, test := range tests { 283 t.Run(test.Name, func(t *testing.T) { 284 fmt.Printf("====START TEST(%s)===\n", test.Name) 285 var list list_head.ListHead 286 list.Init() 287 for i := 0; i < test.Count; i++ { 288 e := makeElement() 289 list.Add(e) 290 291 found := loncha.Contain(&test.marked, func(idx int) bool { 292 return test.marked[idx] == i 293 }) 294 if found { 295 e.MarkForDelete() 296 } 297 } 298 //list.DeleteMarked() 299 if list.Len() != test.Count-len(test.marked) { 300 t.Errorf("missmatch len=%d cnt=%d marked=%d", list.Len(), test.Count, len(test.marked)) 301 } 302 fmt.Printf("====END TEST(%s)===\n", test.Name) 303 }) 304 } 305 } 306 307 func TestNext1(t *testing.T) { 308 309 list_head.MODE_CONCURRENT = true 310 311 var head list_head.ListHead 312 313 head.Init() 314 e := &list_head.ListHead{} 315 assert.Equal(t, &head, head.Next1()) 316 e.Init() 317 head.Add(e) 318 e.MarkForDelete() 319 320 assert.Equal(t, &head, head.Next1()) 321 assert.Equal(t, 0, head.Len()) 322 323 e2 := &list_head.ListHead{} 324 e2.Init() 325 head.Add(e2) 326 327 assert.Equal(t, e2, head.Next1()) 328 assert.Equal(t, 1, head.Len()) 329 330 } 331 332 func TestRaceCondtion(t *testing.T) { 333 list_head.MODE_CONCURRENT = true 334 const concurrent int = 10000 335 336 makeElement := func() *list_head.ListHead { 337 e := &list_head.ListHead{} 338 e.Init() 339 return e 340 } 341 342 tests := []struct { 343 Name string 344 Concurrent int 345 reader func(i int, e *list_head.ListHead) 346 writer func(i int, e *list_head.ListHead) 347 }{ 348 { 349 Name: "LoadPointer and Cas", 350 Concurrent: concurrent, 351 reader: func(i int, e *list_head.ListHead) { 352 if i > 1 { 353 354 next := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(e.Prev().PtrNext()))) 355 if uintptr(next)^1 > 0 { 356 fmt.Printf("markd %d\n", i-1) 357 } 358 } 359 }, 360 writer: func(i int, e *list_head.ListHead) { 361 //n := e.DirectNext() 362 if atomic.CompareAndSwapPointer( 363 (*unsafe.Pointer)(unsafe.Pointer(e.PtrNext())), 364 unsafe.Pointer(e.DirectNext()), 365 unsafe.Pointer(uintptr(unsafe.Pointer(e.DirectNext()))|1)) { 366 fmt.Printf("success %d\n", i) 367 } 368 }, 369 }, 370 { 371 Name: "LoadPointer and StorePointer", 372 Concurrent: concurrent, 373 reader: func(i int, e *list_head.ListHead) { 374 if i > 1 { 375 376 next := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(e.Prev().PtrNext()))) 377 if uintptr(next)^1 > 0 { 378 fmt.Printf("markd %d\n", i-1) 379 } 380 } 381 }, 382 writer: func(i int, e *list_head.ListHead) { 383 atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(e.PtrNext())), 384 unsafe.Pointer(uintptr(unsafe.Pointer(e.DirectNext()))|1)) 385 }, 386 }, 387 } 388 389 for _, test := range tests { 390 t.Run(test.Name, 391 func(t *testing.T) { 392 var head list_head.ListHead 393 head.Init() 394 395 doneCh := make(chan bool, test.Concurrent) 396 397 lists := []*list_head.ListHead{} 398 399 for i := 0; i < test.Concurrent; i++ { 400 e := makeElement() 401 head.Add(e) 402 lists = append(lists, e) 403 } 404 for i, e := range lists { 405 406 go func(i int, e *list_head.ListHead) { 407 408 test.reader(i, e) 409 test.writer(i, e) 410 411 doneCh <- true 412 }(i, e) 413 414 } 415 for i := 0; i < test.Concurrent; i++ { 416 <-doneCh 417 } 418 419 }) 420 } 421 422 } 423 func TestConcurrentAddAndDelete(t *testing.T) { 424 list_head.MODE_CONCURRENT = true 425 const concurrent int = 100 426 427 var head list_head.ListHead 428 var other list_head.ListHead 429 430 head.Init() 431 other.Init() 432 433 fmt.Printf("start head=%s other=%s\n", head.P(), other.P()) 434 435 doneCh := make(chan bool, concurrent) 436 437 cond := func() { 438 if concurrent < head.Len()+other.Len() { 439 //fmt.Println("invalid") 440 assert.True(t, false, head.Len()+other.Len()) 441 } 442 } 443 _ = cond 444 445 for i := 0; i < concurrent; i++ { 446 go func(i int) { 447 448 e := &list_head.ListHead{} 449 e.Init() 450 fmt.Printf("idx=%5d Init e=%s len(head)=%d len(other)=%d\n", 451 i, e.P(), head.Len(), other.Len()) 452 len := head.Len() 453 head.Add(e) 454 if e.Front() != &head { 455 fmt.Printf("!!!!\n") 456 } 457 458 assert.True(t, list_head.ContainOf(&head, e)) 459 460 for i := 0; i < 3; i++ { 461 ee := &list_head.ListHead{} 462 ee.Init() 463 head.Add(ee) 464 } 465 466 //cond() 467 fmt.Printf("idx=%5d Add e=%s last=%5v before_len(head)=%d len(head)=%d len(other)=%d\n", 468 i, e.P(), e.IsLast(), len, head.Len(), other.Len()) 469 before_len := head.Len() 470 for { 471 472 if e.Delete() != nil { 473 break 474 } 475 476 //if e.DeleteWithCas(e.Prev()) == nil { 477 // break 478 //} 479 fmt.Printf("delete all marked head=%s e=%s\n", head.Pp(), e.P()) 480 head.DeleteMarked() 481 fmt.Printf("after marked gc head=%s e=%s\n", head.Pp(), e.P()) 482 if !list_head.ContainOf(&head, e) { 483 break 484 } 485 //fmt.Printf("????") 486 } 487 if list_head.ContainOf(&head, e) { 488 fmt.Printf("!!!!\n") 489 } 490 if before_len < head.Len() { 491 fmt.Printf("invalid increase? idx=%d \n", i) 492 } 493 assert.False(t, list_head.ContainOf(&head, e)) 494 assert.Equal(t, e, e.Next()) 495 assert.Equal(t, e, e.Prev()) 496 497 //cond() 498 499 fmt.Printf("idx=%5d Delete e=%s len(head)=%d len(other)=%d\n", 500 i, e.Pp(), head.Len(), other.Len()) 501 e.Init() 502 //assert.False(t, ContainOf(&head, e)) 503 504 before_e := e.Pp() 505 other.Add(e) 506 assert.False(t, list_head.ContainOf(&head, e)) 507 assert.True(t, list_head.ContainOf(&other, e)) 508 //cond() 509 510 fmt.Printf("idx=%5d Move before_e=%s e=%s len(head)=%d len(other)=%d\n", 511 i, before_e, e.Pp(), head.Len(), other.Len()) 512 513 doneCh <- true 514 }(i) 515 516 } 517 for i := 0; i < concurrent; i++ { 518 <-doneCh 519 } 520 521 head.DeleteMarked() 522 assert.Equal(t, concurrent, other.Len()) 523 assert.Equal(t, 3*concurrent, head.Len(), fmt.Sprintf("head=%s head.Next()=%s", head.Pp(), head.Next().Pp())) 524 525 } 526 527 func TestUnsafe(t *testing.T) { 528 529 b := &struct { 530 a *int 531 }{ 532 a: nil, 533 } 534 b2 := &struct { 535 a *int 536 }{ 537 a: nil, 538 } 539 540 i := int(4) 541 b.a = &i 542 b2.a = &i 543 //b = nil 544 b.a = (*int)(unsafe.Pointer((uintptr(unsafe.Pointer(b.a)) ^ 1))) 545 546 cc := uintptr(unsafe.Pointer(b.a)) 547 _ = cc 548 fmt.Printf("cc=0x%x b.a=%d b2.a=%d\n", cc, *b.a, *b2.a) 549 assert.True(t, true) 550 551 }