gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/syncevent/broadcaster_test.go (about)

     1  // Copyright 2020 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 syncevent
    16  
    17  import (
    18  	"fmt"
    19  	"math/rand"
    20  	"testing"
    21  
    22  	"gvisor.dev/gvisor/pkg/sync"
    23  	"gvisor.dev/gvisor/pkg/waiter"
    24  )
    25  
    26  func TestBroadcasterFilter(t *testing.T) {
    27  	const numReceivers = 2 * MaxEvents
    28  
    29  	var br Broadcaster
    30  	ws := make([]Waiter, numReceivers)
    31  	for i := range ws {
    32  		ws[i].Init()
    33  		br.SubscribeEvents(ws[i].Receiver(), 1<<(i%MaxEvents))
    34  	}
    35  	for ev := 0; ev < MaxEvents; ev++ {
    36  		br.Broadcast(1 << ev)
    37  		for i := range ws {
    38  			want := NoEvents
    39  			if i%MaxEvents == ev {
    40  				want = 1 << ev
    41  			}
    42  			if got := ws[i].Receiver().PendingAndAckAll(); got != want {
    43  				t.Errorf("after Broadcast of event %d: waiter %d has pending event set %#x, wanted %#x", ev, i, got, want)
    44  			}
    45  		}
    46  	}
    47  }
    48  
    49  // TestBroadcasterManySubscriptions tests that subscriptions are not lost by
    50  // table expansion/compaction.
    51  func TestBroadcasterManySubscriptions(t *testing.T) {
    52  	const numReceivers = 5000 // arbitrary
    53  
    54  	var br Broadcaster
    55  	ws := make([]Waiter, numReceivers)
    56  	for i := range ws {
    57  		ws[i].Init()
    58  	}
    59  
    60  	ids := make([]SubscriptionID, numReceivers)
    61  	for i := 0; i < numReceivers; i++ {
    62  		// Subscribe receiver i.
    63  		ids[i] = br.SubscribeEvents(ws[i].Receiver(), 1)
    64  		// Check that receivers [0, i] are subscribed.
    65  		br.Broadcast(1)
    66  		for j := 0; j <= i; j++ {
    67  			if ws[j].Pending() != 1 {
    68  				t.Errorf("receiver %d did not receive an event after subscription of receiver %d", j, i)
    69  			}
    70  			ws[j].Ack(1)
    71  		}
    72  	}
    73  
    74  	// Generate a random order for unsubscriptions.
    75  	unsub := rand.Perm(numReceivers)
    76  	for i := 0; i < numReceivers; i++ {
    77  		// Unsubscribe receiver unsub[i].
    78  		br.UnsubscribeEvents(ids[unsub[i]])
    79  		// Check that receivers [unsub[0], unsub[i]] are not subscribed, and that
    80  		// receivers (unsub[i], unsub[numReceivers]) are still subscribed.
    81  		br.Broadcast(1)
    82  		for j := 0; j <= i; j++ {
    83  			if ws[unsub[j]].Pending() != 0 {
    84  				t.Errorf("unsub iteration %d: receiver %d received an event after unsubscription of receiver %d", i, unsub[j], unsub[i])
    85  			}
    86  		}
    87  		for j := i + 1; j < numReceivers; j++ {
    88  			if ws[unsub[j]].Pending() != 1 {
    89  				t.Errorf("unsub iteration %d: receiver %d did not receive an event after unsubscription of receiver %d", i, unsub[j], unsub[i])
    90  			}
    91  			ws[unsub[j]].Ack(1)
    92  		}
    93  	}
    94  }
    95  
    96  var (
    97  	receiverCountsNonZero       = []int{1, 4, 16, 64}
    98  	receiverCountsIncludingZero = append([]int{0}, receiverCountsNonZero...)
    99  )
   100  
   101  // BenchmarkBroadcasterX, BenchmarkMapX, and BenchmarkQueueX benchmark usage
   102  // pattern X (described in terms of Broadcaster) with Broadcaster, a
   103  // Mutex-protected map[*Receiver]Set, and waiter.Queue respectively.
   104  
   105  // BenchmarkXxxSubscribeUnsubscribe measures the cost of a Subscribe/Unsubscribe
   106  // cycle.
   107  
   108  func BenchmarkBroadcasterSubscribeUnsubscribe(b *testing.B) {
   109  	var br Broadcaster
   110  	var w Waiter
   111  	w.Init()
   112  
   113  	b.ResetTimer()
   114  	for i := 0; i < b.N; i++ {
   115  		id := br.SubscribeEvents(w.Receiver(), 1)
   116  		br.UnsubscribeEvents(id)
   117  	}
   118  }
   119  
   120  func BenchmarkMapSubscribeUnsubscribe(b *testing.B) {
   121  	var mu sync.Mutex
   122  	m := make(map[*Receiver]Set)
   123  	var w Waiter
   124  	w.Init()
   125  
   126  	b.ResetTimer()
   127  	for i := 0; i < b.N; i++ {
   128  		mu.Lock()
   129  		m[w.Receiver()] = Set(1)
   130  		mu.Unlock()
   131  		mu.Lock()
   132  		delete(m, w.Receiver())
   133  		mu.Unlock()
   134  	}
   135  }
   136  
   137  func BenchmarkQueueSubscribeUnsubscribe(b *testing.B) {
   138  	var q waiter.Queue
   139  	e, _ := waiter.NewChannelEntry(1)
   140  
   141  	b.ResetTimer()
   142  	for i := 0; i < b.N; i++ {
   143  		q.EventRegister(&e)
   144  		q.EventUnregister(&e)
   145  	}
   146  }
   147  
   148  // BenchmarkXxxSubscribeUnsubscribeBatch is similar to
   149  // BenchmarkXxxSubscribeUnsubscribe, but subscribes and unsubscribes a large
   150  // number of Receivers at a time in order to measure the amortized overhead of
   151  // table expansion/compaction. (Since waiter.Queue is implemented using a
   152  // linked list, BenchmarkQueueSubscribeUnsubscribe and
   153  // BenchmarkQueueSubscribeUnsubscribeBatch should produce nearly the same
   154  // result.)
   155  
   156  const numBatchReceivers = 1000
   157  
   158  func BenchmarkBroadcasterSubscribeUnsubscribeBatch(b *testing.B) {
   159  	var br Broadcaster
   160  	ws := make([]Waiter, numBatchReceivers)
   161  	for i := range ws {
   162  		ws[i].Init()
   163  	}
   164  	ids := make([]SubscriptionID, numBatchReceivers)
   165  
   166  	// Generate a random order for unsubscriptions.
   167  	unsub := rand.Perm(numBatchReceivers)
   168  
   169  	b.ResetTimer()
   170  	for i := 0; i < b.N/numBatchReceivers; i++ {
   171  		for j := 0; j < numBatchReceivers; j++ {
   172  			ids[j] = br.SubscribeEvents(ws[j].Receiver(), 1)
   173  		}
   174  		for j := 0; j < numBatchReceivers; j++ {
   175  			br.UnsubscribeEvents(ids[unsub[j]])
   176  		}
   177  	}
   178  }
   179  
   180  func BenchmarkMapSubscribeUnsubscribeBatch(b *testing.B) {
   181  	var mu sync.Mutex
   182  	m := make(map[*Receiver]Set)
   183  	ws := make([]Waiter, numBatchReceivers)
   184  	for i := range ws {
   185  		ws[i].Init()
   186  	}
   187  
   188  	// Generate a random order for unsubscriptions.
   189  	unsub := rand.Perm(numBatchReceivers)
   190  
   191  	b.ResetTimer()
   192  	for i := 0; i < b.N/numBatchReceivers; i++ {
   193  		for j := 0; j < numBatchReceivers; j++ {
   194  			mu.Lock()
   195  			m[ws[j].Receiver()] = Set(1)
   196  			mu.Unlock()
   197  		}
   198  		for j := 0; j < numBatchReceivers; j++ {
   199  			mu.Lock()
   200  			delete(m, ws[unsub[j]].Receiver())
   201  			mu.Unlock()
   202  		}
   203  	}
   204  }
   205  
   206  func BenchmarkQueueSubscribeUnsubscribeBatch(b *testing.B) {
   207  	var q waiter.Queue
   208  	es := make([]waiter.Entry, numBatchReceivers)
   209  	for i := range es {
   210  		es[i], _ = waiter.NewChannelEntry(1)
   211  	}
   212  
   213  	// Generate a random order for unsubscriptions.
   214  	unsub := rand.Perm(numBatchReceivers)
   215  
   216  	b.ResetTimer()
   217  	for i := 0; i < b.N/numBatchReceivers; i++ {
   218  		for j := 0; j < numBatchReceivers; j++ {
   219  			q.EventRegister(&es[j])
   220  		}
   221  		for j := 0; j < numBatchReceivers; j++ {
   222  			q.EventUnregister(&es[unsub[j]])
   223  		}
   224  	}
   225  }
   226  
   227  // BenchmarkXxxBroadcastRedundant measures how long it takes to Broadcast
   228  // already-pending events to multiple Receivers.
   229  
   230  func BenchmarkBroadcasterBroadcastRedundant(b *testing.B) {
   231  	for _, n := range receiverCountsIncludingZero {
   232  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   233  			var br Broadcaster
   234  			ws := make([]Waiter, n)
   235  			for i := range ws {
   236  				ws[i].Init()
   237  				br.SubscribeEvents(ws[i].Receiver(), 1)
   238  			}
   239  			br.Broadcast(1)
   240  
   241  			b.ResetTimer()
   242  			for i := 0; i < b.N; i++ {
   243  				br.Broadcast(1)
   244  			}
   245  		})
   246  	}
   247  }
   248  
   249  func BenchmarkMapBroadcastRedundant(b *testing.B) {
   250  	for _, n := range receiverCountsIncludingZero {
   251  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   252  			var mu sync.Mutex
   253  			m := make(map[*Receiver]Set)
   254  			ws := make([]Waiter, n)
   255  			for i := range ws {
   256  				ws[i].Init()
   257  				m[ws[i].Receiver()] = Set(1)
   258  			}
   259  			mu.Lock()
   260  			for r := range m {
   261  				r.Notify(1)
   262  			}
   263  			mu.Unlock()
   264  
   265  			b.ResetTimer()
   266  			for i := 0; i < b.N; i++ {
   267  				mu.Lock()
   268  				for r := range m {
   269  					r.Notify(1)
   270  				}
   271  				mu.Unlock()
   272  			}
   273  		})
   274  	}
   275  }
   276  
   277  func BenchmarkQueueBroadcastRedundant(b *testing.B) {
   278  	for _, n := range receiverCountsIncludingZero {
   279  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   280  			var q waiter.Queue
   281  			for i := 0; i < n; i++ {
   282  				e, _ := waiter.NewChannelEntry(1)
   283  				q.EventRegister(&e)
   284  			}
   285  			q.Notify(1)
   286  
   287  			b.ResetTimer()
   288  			for i := 0; i < b.N; i++ {
   289  				q.Notify(1)
   290  			}
   291  		})
   292  	}
   293  }
   294  
   295  // BenchmarkXxxBroadcastAck measures how long it takes to Broadcast events to
   296  // multiple Receivers, check that all Receivers have received the event, and
   297  // clear the event from all Receivers.
   298  
   299  func BenchmarkBroadcasterBroadcastAck(b *testing.B) {
   300  	for _, n := range receiverCountsNonZero {
   301  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   302  			var br Broadcaster
   303  			ws := make([]Waiter, n)
   304  			for i := range ws {
   305  				ws[i].Init()
   306  				br.SubscribeEvents(ws[i].Receiver(), 1)
   307  			}
   308  
   309  			b.ResetTimer()
   310  			for i := 0; i < b.N; i++ {
   311  				br.Broadcast(1)
   312  				for j := range ws {
   313  					if got, want := ws[j].Pending(), Set(1); got != want {
   314  						b.Fatalf("Receiver.Pending(): got %#x, wanted %#x", got, want)
   315  					}
   316  					ws[j].Ack(1)
   317  				}
   318  			}
   319  		})
   320  	}
   321  }
   322  
   323  func BenchmarkMapBroadcastAck(b *testing.B) {
   324  	for _, n := range receiverCountsNonZero {
   325  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   326  			var mu sync.Mutex
   327  			m := make(map[*Receiver]Set)
   328  			ws := make([]Waiter, n)
   329  			for i := range ws {
   330  				ws[i].Init()
   331  				m[ws[i].Receiver()] = Set(1)
   332  			}
   333  
   334  			b.ResetTimer()
   335  			for i := 0; i < b.N; i++ {
   336  				mu.Lock()
   337  				for r := range m {
   338  					r.Notify(1)
   339  				}
   340  				mu.Unlock()
   341  				for j := range ws {
   342  					if got, want := ws[j].Pending(), Set(1); got != want {
   343  						b.Fatalf("Receiver.Pending(): got %#x, wanted %#x", got, want)
   344  					}
   345  					ws[j].Ack(1)
   346  				}
   347  			}
   348  		})
   349  	}
   350  }
   351  
   352  func BenchmarkQueueBroadcastAck(b *testing.B) {
   353  	for _, n := range receiverCountsNonZero {
   354  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   355  			var q waiter.Queue
   356  			chs := make([]chan struct{}, n)
   357  			for i := range chs {
   358  				e, ch := waiter.NewChannelEntry(1)
   359  				q.EventRegister(&e)
   360  				chs[i] = ch
   361  			}
   362  
   363  			b.ResetTimer()
   364  			for i := 0; i < b.N; i++ {
   365  				q.Notify(1)
   366  				for _, ch := range chs {
   367  					select {
   368  					case <-ch:
   369  					default:
   370  						b.Fatalf("channel did not receive event")
   371  					}
   372  				}
   373  			}
   374  		})
   375  	}
   376  }