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 }