gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/waiter/waiter_test.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package waiter 16 17 import ( 18 "testing" 19 20 "gvisor.dev/gvisor/pkg/atomicbitops" 21 ) 22 23 func TestEmptyQueue(t *testing.T) { 24 var q Queue 25 26 // Notify the zero-value of a queue. 27 q.Notify(EventIn) 28 29 // Register then unregister a waiter, then notify the queue. 30 cnt := 0 31 e := NewFunctionEntry(EventIn, func(EventMask) { cnt++ }) 32 q.EventRegister(&e) 33 q.EventUnregister(&e) 34 q.Notify(EventIn) 35 if cnt != 0 { 36 t.Errorf("Callback was called when it shouldn't have been") 37 } 38 } 39 40 func TestMask(t *testing.T) { 41 // Register a waiter. 42 var q Queue 43 var cnt int 44 e := NewFunctionEntry(EventIn|EventErr, func(EventMask) { cnt++ }) 45 q.EventRegister(&e) 46 47 // Notify with an overlapping mask. 48 cnt = 0 49 q.Notify(EventIn | EventOut) 50 if cnt != 1 { 51 t.Errorf("Callback wasn't called when it should have been") 52 } 53 54 // Notify with a subset mask. 55 cnt = 0 56 q.Notify(EventIn) 57 if cnt != 1 { 58 t.Errorf("Callback wasn't called when it should have been") 59 } 60 61 // Notify with a superset mask. 62 cnt = 0 63 q.Notify(EventIn | EventErr | EventOut) 64 if cnt != 1 { 65 t.Errorf("Callback wasn't called when it should have been") 66 } 67 68 // Notify with the exact same mask. 69 cnt = 0 70 q.Notify(EventIn | EventErr) 71 if cnt != 1 { 72 t.Errorf("Callback wasn't called when it should have been") 73 } 74 75 // Notify with a disjoint mask. 76 cnt = 0 77 q.Notify(EventOut | EventHUp) 78 if cnt != 0 { 79 t.Errorf("Callback was called when it shouldn't have been") 80 } 81 } 82 83 func TestConcurrentRegistration(t *testing.T) { 84 var q Queue 85 var cnt int 86 const concurrency = 1000 87 88 ch1 := make(chan struct{}) 89 ch2 := make(chan struct{}) 90 ch3 := make(chan struct{}) 91 92 // Create goroutines that will all register/unregister concurrently. 93 for i := 0; i < concurrency; i++ { 94 go func() { 95 e := NewFunctionEntry(EventIn|EventErr, func(mask EventMask) { 96 cnt++ 97 if mask != EventIn { 98 t.Errorf("mask = %#x want %#x", mask, EventIn) 99 } 100 }) 101 102 // Wait for notification, then register. 103 <-ch1 104 q.EventRegister(&e) 105 106 // Tell main goroutine that we're done registering. 107 ch2 <- struct{}{} 108 109 // Wait for notification, then unregister. 110 <-ch3 111 q.EventUnregister(&e) 112 113 // Tell main goroutine that we're done unregistering. 114 ch2 <- struct{}{} 115 }() 116 } 117 118 // Let the goroutines register. 119 close(ch1) 120 for i := 0; i < concurrency; i++ { 121 <-ch2 122 } 123 124 // Issue a notification. 125 q.Notify(EventIn) 126 if cnt != concurrency { 127 t.Errorf("cnt = %d, want %d", cnt, concurrency) 128 } 129 130 // Let the goroutine unregister. 131 close(ch3) 132 for i := 0; i < concurrency; i++ { 133 <-ch2 134 } 135 136 // Issue a notification. 137 q.Notify(EventIn) 138 if cnt != concurrency { 139 t.Errorf("cnt = %d, want %d", cnt, concurrency) 140 } 141 } 142 143 func TestConcurrentNotification(t *testing.T) { 144 var q Queue 145 var cnt atomicbitops.Int32 146 const concurrency = 1000 147 const waiterCount = 1000 148 149 // Register waiters. 150 for i := 0; i < waiterCount; i++ { 151 e := NewFunctionEntry(EventIn|EventErr, func(mask EventMask) { 152 cnt.Add(1) 153 if mask != EventIn { 154 t.Errorf("mask = %#x want %#x", mask, EventIn) 155 } 156 }) 157 158 q.EventRegister(&e) 159 } 160 161 // Launch notifiers. 162 ch1 := make(chan struct{}) 163 ch2 := make(chan struct{}) 164 for i := 0; i < concurrency; i++ { 165 go func() { 166 <-ch1 167 q.Notify(EventIn) 168 ch2 <- struct{}{} 169 }() 170 } 171 172 // Let notifiers go. 173 close(ch1) 174 for i := 0; i < concurrency; i++ { 175 <-ch2 176 } 177 178 // Check the count. 179 if cnt.Load() != concurrency*waiterCount { 180 t.Errorf("cnt = %d, want %d", cnt.Load(), concurrency*waiterCount) 181 } 182 }