(t *testing.T)
| 15 | ) |
| 16 | |
| 17 | func TestUpgradeLock(t *testing.T) { |
| 18 | if testing.Short() { |
| 19 | t.Skipf("skipping %s in short mode", t.Name()) |
| 20 | } |
| 21 | for _, test := range []string{"downgrade", "unlock"} { |
| 22 | t.Run(test, func(t *testing.T) { |
| 23 | const threadCnt = 10000 |
| 24 | var ( |
| 25 | n = &nlc{} |
| 26 | wg = &sync.WaitGroup{} |
| 27 | sema = cos.NewDynSemaphore(threadCnt) |
| 28 | counter = atomic.NewInt32(0) |
| 29 | uname = cos.RandString(10) |
| 30 | ) |
| 31 | n.init() |
| 32 | |
| 33 | // Additional stray reader which forces the other to block on `UpgradeLock`. |
| 34 | n.Lock(uname, false) |
| 35 | |
| 36 | sema.Acquire(threadCnt) |
| 37 | wg.Add(threadCnt) |
| 38 | for i := 0; i < threadCnt; i++ { |
| 39 | go func() { |
| 40 | defer wg.Done() |
| 41 | |
| 42 | n.Lock(uname, false) |
| 43 | |
| 44 | sema.Acquire() |
| 45 | if finished := n.UpgradeLock(uname); finished { |
| 46 | tassert.Fatalf(t, counter.Load() > 0, "counter should be already updated") |
| 47 | n.Unlock(uname, false) |
| 48 | return |
| 49 | } |
| 50 | |
| 51 | // Imitate doing job. |
| 52 | counter.Inc() |
| 53 | time.Sleep(time.Second) |
| 54 | |
| 55 | switch test { |
| 56 | case "downgrade": |
| 57 | n.DowngradeLock(uname) |
| 58 | n.Unlock(uname, false) |
| 59 | case "unlock": |
| 60 | n.Unlock(uname, true) |
| 61 | default: |
| 62 | panic(test) |
| 63 | } |
| 64 | }() |
| 65 | } |
| 66 | |
| 67 | // Make sure all other threads are past `n.Lock` and blocked on `sema.Acquire`. |
| 68 | time.Sleep(500 * time.Millisecond) |
| 69 | |
| 70 | sema.Release(threadCnt) |
| 71 | // Wait a while to make sure that all goroutines started and blocked on `UpgradeLock`. |
| 72 | time.Sleep(500 * time.Millisecond) |
| 73 | |
| 74 | // Should wake up one of the waiter which should start doing job. |
nothing calls this directly
no test coverage detected