github.com/teepark/go-sysvipc@v0.0.0-20200817232735-d7ca6053ea29/sem_test.go (about)

     1  package sysvipc
     2  
     3  import (
     4  	"os"
     5  	"runtime"
     6  	"sync"
     7  	"syscall"
     8  	"testing"
     9  	"time"
    10  )
    11  
    12  func TestSemBadGet(t *testing.T) {
    13  	// no CREAT, doesn't exist
    14  	semset, err := GetSemSet(0xDA7ABA5E, 3, nil)
    15  	if err != syscall.ENOENT {
    16  		t.Error(err)
    17  	} else if err == nil {
    18  		semset.Remove()
    19  	}
    20  
    21  	// 0 count
    22  	semset, err = GetSemSet(0xDA7ABA5E, 0, &SemSetFlags{Create: true})
    23  	if err != syscall.EINVAL {
    24  		t.Error(err)
    25  	} else if err == nil {
    26  		semset.Remove()
    27  	}
    28  }
    29  
    30  func TestSemBadRemove(t *testing.T) {
    31  	s := &SemaphoreSet{5, 2} // 5 was never created
    32  	if err := s.Remove(); err != syscall.EIDRM {
    33  		t.Fatal(err)
    34  	}
    35  }
    36  
    37  func TestSemIncrements(t *testing.T) {
    38  	semSetup(t)
    39  	defer semTeardown(t)
    40  
    41  	target := []uint16{3, 2, 10, 4}
    42  
    43  	ops := NewSemOps()
    44  	for i, t := range target {
    45  		ops.Increment(uint16(i), int16(t), nil)
    46  	}
    47  
    48  	if err := ss.Run(ops, -1); err != nil {
    49  		t.Fatal(err)
    50  	}
    51  
    52  	vals, err := ss.Getall()
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  
    57  	for i, n := range target {
    58  		if vals[i] != n {
    59  			t.Error(i, vals[i], n)
    60  		}
    61  	}
    62  
    63  	if err := ops.Increment(0, -1, &SemOpFlags{DontWait: true}); err == nil {
    64  		t.Error("negative increment should fail")
    65  	}
    66  	if err := ops.Increment(0, 0, nil); err == nil {
    67  		t.Error("zero increment should fail")
    68  	}
    69  }
    70  
    71  func TestSemDecrements(t *testing.T) {
    72  	semSetup(t)
    73  	defer semTeardown(t)
    74  
    75  	if err := ss.Setval(0, 5); err != nil {
    76  		t.Fatal(err)
    77  	}
    78  
    79  	ops := NewSemOps()
    80  	ops.Decrement(0, 2, nil)
    81  	if err := ss.Run(ops, -1); err != nil {
    82  		t.Fatal(err)
    83  	}
    84  
    85  	val, err := ss.Getval(0)
    86  	if err != nil {
    87  		t.Fatal(err)
    88  	}
    89  	if val != 3 {
    90  		t.Error("decrement didn't take")
    91  	}
    92  
    93  	ops = NewSemOps()
    94  	ops.Decrement(0, 1, nil)
    95  	if err := ss.Run(ops, -1); err != nil {
    96  		t.Fatal(err)
    97  	}
    98  
    99  	val, err = ss.Getval(0)
   100  	if err != nil {
   101  		t.Fatal(err)
   102  	}
   103  	if val != 2 {
   104  		t.Error("decrement didn't take")
   105  	}
   106  
   107  	if err := ops.Decrement(0, -1, nil); err == nil {
   108  		t.Error("negative decrement should fail")
   109  	}
   110  	if err := ops.Decrement(0, 0, nil); err == nil {
   111  		t.Error("zero decrement should fail")
   112  	}
   113  }
   114  
   115  func TestSemBlockingDecrements(t *testing.T) {
   116  	semSetup(t)
   117  	defer semTeardown(t)
   118  
   119  	ops := NewSemOps()
   120  	if err := ops.Decrement(0, 1, nil); err != nil {
   121  		t.Fatal(err)
   122  	}
   123  
   124  	if err := ss.Run(ops, time.Millisecond); err != syscall.EAGAIN {
   125  		t.Error("Decrement against 0 should have timed out", err)
   126  	}
   127  }
   128  
   129  func TestSemNonBlockingDecrements(t *testing.T) {
   130  	semSetup(t)
   131  	defer semTeardown(t)
   132  
   133  	ops := NewSemOps()
   134  	if err := ops.Decrement(0, 1, &SemOpFlags{DontWait: true}); err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	// I'd love to skip the separate goroutine and do this with Run's timeout,
   139  	// but that would also fail with EAGAIN and I wouldn't know which
   140  	// (IPC_NOWAIT or the timeout) caused it.
   141  	done := make(chan struct{})
   142  	go func() {
   143  		defer close(done)
   144  		if err := ss.Run(ops, -1); err != syscall.EAGAIN {
   145  			t.Error("non-blocking decrement against 0 should fail", err)
   146  		}
   147  	}()
   148  
   149  	select {
   150  	case <-done:
   151  	case <-time.After(time.Millisecond):
   152  		t.Error("timeout passed")
   153  	}
   154  }
   155  
   156  func TestSemWaitZero(t *testing.T) {
   157  	semSetup(t)
   158  	defer semTeardown(t)
   159  
   160  	if err := ss.Setval(0, 3); err != nil {
   161  		t.Fatal(err)
   162  	}
   163  
   164  	done := make(chan struct{})
   165  	go func() {
   166  		defer close(done)
   167  		ops := NewSemOps()
   168  		if err := ops.WaitZero(0, nil); err != nil {
   169  			t.Fatal(err)
   170  		}
   171  		if err := ss.Run(ops, -1); err != nil {
   172  			t.Error(err)
   173  		}
   174  	}()
   175  
   176  	runtime.Gosched()
   177  
   178  	select {
   179  	case <-done:
   180  		t.Error("WaitZero returned before setting sem to 0")
   181  	default:
   182  	}
   183  
   184  	if err := ss.Setval(0, 0); err != nil {
   185  		t.Fatal(err)
   186  	}
   187  
   188  	start := time.Now()
   189  	<-done
   190  	if elapsed := time.Since(start); elapsed > 1*time.Millisecond {
   191  		t.Error("WaitZero didn't unblock fast enough:", elapsed)
   192  	}
   193  }
   194  
   195  func TestSemWaitZeroTimeout(t *testing.T) {
   196  	semSetup(t)
   197  	defer semTeardown(t)
   198  
   199  	if err := ss.Setval(0, 1); err != nil {
   200  		t.Fatal(err)
   201  	}
   202  
   203  	ops := NewSemOps()
   204  	if err := ops.WaitZero(0, nil); err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	if err := ss.Run(ops, 1*time.Millisecond); err != syscall.EAGAIN {
   209  		t.Fatal(err)
   210  	}
   211  }
   212  
   213  func TestSemWaitZeroNonBlocking(t *testing.T) {
   214  	semSetup(t)
   215  	defer semTeardown(t)
   216  
   217  	if err := ss.Setval(0, 1); err != nil {
   218  		t.Fatal(err)
   219  	}
   220  
   221  	ops := NewSemOps()
   222  	if err := ops.WaitZero(0, &SemOpFlags{DontWait: true}); err != nil {
   223  		t.Fatal(err)
   224  	}
   225  
   226  	done := make(chan struct{})
   227  	go func() {
   228  		defer close(done)
   229  		if err := ss.Run(ops, -1); err != syscall.EAGAIN {
   230  			t.Error("waitzero non-blocking should fail", err)
   231  		}
   232  	}()
   233  
   234  	select {
   235  	case <-done:
   236  	case <-time.After(time.Millisecond):
   237  		t.Error("timed out")
   238  	}
   239  }
   240  
   241  func TestSemSetAndGetVals(t *testing.T) {
   242  	semSetup(t)
   243  	defer semTeardown(t)
   244  
   245  	vals := []int{4, 3, 9, 6}
   246  
   247  	for i, val := range vals {
   248  		if err := ss.Setval(uint16(i), val); err != nil {
   249  			t.Fatal(err)
   250  		}
   251  	}
   252  
   253  	for i, val := range vals {
   254  		stored, err := ss.Getval(uint16(i))
   255  		if err != nil {
   256  			t.Fatal(err)
   257  		}
   258  
   259  		if val != stored {
   260  			t.Error("mismatched values:", val, stored)
   261  		}
   262  	}
   263  
   264  	// test failure on a negative number
   265  	if err := ss.Setval(0, -1); err != syscall.ERANGE {
   266  		t.Fatal(err)
   267  	}
   268  }
   269  
   270  func TestSemGetValNotAllowed(t *testing.T) {
   271  	s, err := GetSemSet(0xDA7ABA5E, 1, &SemSetFlags{
   272  		Create:    true,
   273  		Exclusive: true,
   274  		Perms:     0, // no read perms, even for owner
   275  	})
   276  	if err != nil {
   277  		t.Fatal(err)
   278  	}
   279  	defer func() {
   280  		if err := s.Remove(); err != nil {
   281  			t.Fatal(err)
   282  		}
   283  	}()
   284  
   285  	if _, err := s.Getval(0); err != syscall.EACCES {
   286  		t.Error(err)
   287  	}
   288  }
   289  
   290  func TestSemSetAndGetAll(t *testing.T) {
   291  	semSetup(t)
   292  	defer semTeardown(t)
   293  
   294  	target := []uint16{4, 5, 6, 7}
   295  
   296  	if err := ss.Setall(target[:3]); err == nil {
   297  		t.Error("Setall should have failed when given too few values")
   298  	}
   299  
   300  	if err := ss.Setall(target); err != nil {
   301  		t.Fatal(err)
   302  	}
   303  
   304  	got, err := ss.Getall()
   305  	if err != nil {
   306  		t.Fatal(err)
   307  	}
   308  
   309  	if len(got) != len(target) {
   310  		t.Error("didn't get back what we stored in semset")
   311  	} else {
   312  		for i := range target {
   313  			if got[i] != target[i] {
   314  				t.Error("didn't get back what we stored in semset")
   315  			}
   316  		}
   317  	}
   318  }
   319  
   320  func TestSemStat(t *testing.T) {
   321  	semSetup(t)
   322  	defer semTeardown(t)
   323  
   324  	// EIDRM with a bad semset id
   325  	if _, err := (&SemaphoreSet{5, 2}).Stat(); err != syscall.EIDRM {
   326  		t.Error("semctl(IPC_STAT) on a made up semset id should fail")
   327  	}
   328  
   329  	info, err := ss.Stat()
   330  	if err != nil {
   331  		t.Fatal(err)
   332  	}
   333  
   334  	if info.Perms.OwnerUID != os.Getuid() {
   335  		t.Error("wrong owner uid", info.Perms.OwnerUID)
   336  	}
   337  	if info.Perms.CreatorUID != os.Getuid() {
   338  		t.Error("wrong creator uid", info.Perms.CreatorUID)
   339  	}
   340  	if info.Perms.Mode&0777 != 0600 {
   341  		t.Error("wrong mode", info.Perms.Mode)
   342  	}
   343  	if info.Count != 4 {
   344  		t.Error("wrong count", info.Count)
   345  	}
   346  }
   347  
   348  func TestSemSetSet(t *testing.T) {
   349  	semSetup(t)
   350  	defer semTeardown(t)
   351  
   352  	info, err := ss.Stat()
   353  	if err != nil {
   354  		t.Fatal(err)
   355  	}
   356  
   357  	set := &SemSetInfo{
   358  		Perms: IpcPerms{
   359  			OwnerUID: info.Perms.OwnerUID,
   360  			OwnerGID: info.Perms.OwnerGID,
   361  			Mode:     0400,
   362  		},
   363  	}
   364  	if err := ss.Set(set); err != nil {
   365  		t.Fatal(err)
   366  	}
   367  
   368  	info, err = ss.Stat()
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  
   373  	if info.Perms.Mode&0777 != 0400 {
   374  		t.Error("set() didn't take")
   375  	}
   376  }
   377  
   378  func TestSemGetpid(t *testing.T) {
   379  	semSetup(t)
   380  	defer semTeardown(t)
   381  
   382  	ops := NewSemOps()
   383  	if err := ops.Increment(0, 1, nil); err != nil {
   384  		t.Fatal(err)
   385  	}
   386  	if err := ss.Run(ops, -1); err != nil {
   387  		t.Fatal(err)
   388  	}
   389  
   390  	pid, err := ss.Getpid(0)
   391  	if err != nil {
   392  		t.Fatal(err)
   393  	}
   394  
   395  	if pid != os.Getpid() {
   396  		t.Error("we should be the last pid to operate on sem 0")
   397  	}
   398  
   399  	if _, err := ss.Getpid(7); err != syscall.EINVAL {
   400  		t.Error("Getpid should fail with EINVAL for an out-of-bounds num", err)
   401  	}
   402  }
   403  
   404  func TestSemGetNCnt(t *testing.T) {
   405  	semSetup(t)
   406  	defer semTeardown(t)
   407  
   408  	cnt, err := ss.GetNCnt(0)
   409  	if err != nil {
   410  		t.Fatal(err)
   411  	}
   412  	if cnt != 0 {
   413  		t.Error("shouldn't be any decrement waiters yet")
   414  	}
   415  
   416  	wg := &sync.WaitGroup{}
   417  	wg.Add(3)
   418  	for i := 0; i < 3; i++ {
   419  		go func() {
   420  			ops := NewSemOps()
   421  			if err := ops.Decrement(0, 1, nil); err != nil {
   422  				t.Fatal(err)
   423  			}
   424  			wg.Done()
   425  			ss.Run(ops, -1)
   426  		}()
   427  	}
   428  
   429  	wg.Wait()
   430  	runtime.Gosched()
   431  
   432  	cnt, err = ss.GetNCnt(0)
   433  	if err != nil {
   434  		t.Fatal(err)
   435  	}
   436  	if cnt != 3 {
   437  		t.Error("should be 3 waiters, instead have", cnt)
   438  	}
   439  
   440  	cnt, err = ss.GetNCnt(14)
   441  	if err != syscall.EINVAL {
   442  		t.Error("GetNCnt with out-of-bounds num should fail")
   443  	}
   444  }
   445  
   446  func TestSemGetZCnt(t *testing.T) {
   447  	semSetup(t)
   448  	defer semTeardown(t)
   449  
   450  	if err := ss.Setval(0, 2); err != nil {
   451  		t.Fatal(err)
   452  	}
   453  
   454  	cnt, err := ss.GetZCnt(0)
   455  	if err != nil {
   456  		t.Fatal(err)
   457  	}
   458  	if cnt != 0 {
   459  		t.Error("no waiters, GetZCnt should be 0")
   460  	}
   461  
   462  	wg := &sync.WaitGroup{}
   463  	wg.Add(3)
   464  	for i := 0; i < 3; i++ {
   465  		go func() {
   466  			ops := NewSemOps()
   467  			if err := ops.WaitZero(0, nil); err != nil {
   468  				t.Fatal(err)
   469  			}
   470  
   471  			wg.Done()
   472  
   473  			ss.Run(ops, time.Millisecond)
   474  		}()
   475  	}
   476  
   477  	wg.Wait()
   478  	runtime.Gosched()
   479  
   480  	cnt, err = ss.GetZCnt(0)
   481  	if err != nil {
   482  		t.Fatal(err)
   483  	}
   484  	if cnt != 3 {
   485  		t.Error("should be 3 waiters, instead have", cnt)
   486  	}
   487  
   488  	_, err = ss.GetZCnt(11)
   489  	if err != syscall.EINVAL {
   490  		t.Error("GetZCnt should fail with an out-of-bounds num")
   491  	}
   492  }
   493  
   494  func TestSemBadSet(t *testing.T) {
   495  	if _, err := (&SemaphoreSet{5, 2}).Stat(); err != syscall.EIDRM {
   496  		t.Error("semctl(IPC_SET) on a made up semset id should fail")
   497  	}
   498  }
   499  
   500  var ss *SemaphoreSet
   501  
   502  func semSetup(t *testing.T) {
   503  	s, err := GetSemSet(0xDA7ABA5E, 4, &SemSetFlags{
   504  		Create:    true,
   505  		Exclusive: true,
   506  		Perms:     0600,
   507  	})
   508  	if err != nil {
   509  		t.Fatal(err)
   510  	}
   511  	ss = s
   512  }
   513  
   514  func semTeardown(t *testing.T) {
   515  	if err := ss.Remove(); err != nil {
   516  		t.Error(err)
   517  	}
   518  }