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  }