gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/tcpip/link/sharedmem/pipe/pipe_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 pipe
    16  
    17  import (
    18  	"math/rand"
    19  	"reflect"
    20  	"runtime"
    21  	"testing"
    22  
    23  	"gvisor.dev/gvisor/pkg/sync"
    24  )
    25  
    26  func TestSimpleReadWrite(t *testing.T) {
    27  	// Check that a simple write can be properly read from the rx side.
    28  	tr := rand.New(rand.NewSource(99))
    29  	rr := rand.New(rand.NewSource(99))
    30  
    31  	b := make([]byte, 100)
    32  	var tx Tx
    33  	tx.Init(b)
    34  
    35  	wb := tx.Push(10)
    36  	if wb == nil {
    37  		t.Fatalf("Push failed on empty pipe")
    38  	}
    39  	for i := range wb {
    40  		wb[i] = byte(tr.Intn(256))
    41  	}
    42  	tx.Flush()
    43  
    44  	var rx Rx
    45  	rx.Init(b)
    46  	rb := rx.Pull()
    47  	if len(rb) != 10 {
    48  		t.Fatalf("Bad buffer size returned: got %v, want %v", len(rb), 10)
    49  	}
    50  
    51  	for i := range rb {
    52  		if v := byte(rr.Intn(256)); v != rb[i] {
    53  			t.Fatalf("Bad read buffer at index %v: got %v, want %v", i, rb[i], v)
    54  		}
    55  	}
    56  	rx.Flush()
    57  }
    58  
    59  func TestEmptyRead(t *testing.T) {
    60  	// Check that pulling from an empty pipe fails.
    61  	b := make([]byte, 100)
    62  	var tx Tx
    63  	tx.Init(b)
    64  
    65  	var rx Rx
    66  	rx.Init(b)
    67  	if rb := rx.Pull(); rb != nil {
    68  		t.Fatalf("Pull succeeded on empty pipe")
    69  	}
    70  }
    71  
    72  func TestTooLargeWrite(t *testing.T) {
    73  	// Check that writes that are too large are properly rejected.
    74  	b := make([]byte, 96)
    75  	var tx Tx
    76  	tx.Init(b)
    77  
    78  	if wb := tx.Push(96); wb != nil {
    79  		t.Fatalf("Write of 96 bytes succeeded on 96-byte pipe")
    80  	}
    81  
    82  	if wb := tx.Push(88); wb != nil {
    83  		t.Fatalf("Write of 88 bytes succeeded on 96-byte pipe")
    84  	}
    85  
    86  	if wb := tx.Push(80); wb == nil {
    87  		t.Fatalf("Write of 80 bytes failed on 96-byte pipe")
    88  	}
    89  }
    90  
    91  func TestFullWrite(t *testing.T) {
    92  	// Check that writes fail when the pipe is full.
    93  	b := make([]byte, 100)
    94  	var tx Tx
    95  	tx.Init(b)
    96  
    97  	if wb := tx.Push(80); wb == nil {
    98  		t.Fatalf("Write of 80 bytes failed on 96-byte pipe")
    99  	}
   100  
   101  	if wb := tx.Push(1); wb != nil {
   102  		t.Fatalf("Write succeeded on full pipe")
   103  	}
   104  }
   105  
   106  func TestFullAndFlushedWrite(t *testing.T) {
   107  	// Check that writes fail when the pipe is full and has already been
   108  	// flushed.
   109  	b := make([]byte, 100)
   110  	var tx Tx
   111  	tx.Init(b)
   112  
   113  	if wb := tx.Push(80); wb == nil {
   114  		t.Fatalf("Write of 80 bytes failed on 96-byte pipe")
   115  	}
   116  
   117  	tx.Flush()
   118  
   119  	if wb := tx.Push(1); wb != nil {
   120  		t.Fatalf("Write succeeded on full pipe")
   121  	}
   122  }
   123  
   124  func TestTxFlushTwice(t *testing.T) {
   125  	// Checks that a second consecutive tx flush is a no-op.
   126  	b := make([]byte, 100)
   127  	var tx Tx
   128  	tx.Init(b)
   129  
   130  	if wb := tx.Push(50); wb == nil {
   131  		t.Fatalf("Push failed on empty pipe")
   132  	}
   133  	tx.Flush()
   134  
   135  	// Make copy of original tx queue, flush it, then check that it didn't
   136  	// change.
   137  	orig := tx
   138  	tx.Flush()
   139  
   140  	if !reflect.DeepEqual(orig, tx) {
   141  		t.Fatalf("Flush mutated tx pipe: got %v, want %v", tx, orig)
   142  	}
   143  }
   144  
   145  func TestRxFlushTwice(t *testing.T) {
   146  	// Checks that a second consecutive rx flush is a no-op.
   147  	b := make([]byte, 100)
   148  	var tx Tx
   149  	tx.Init(b)
   150  
   151  	if wb := tx.Push(50); wb == nil {
   152  		t.Fatalf("Push failed on empty pipe")
   153  	}
   154  	tx.Flush()
   155  
   156  	var rx Rx
   157  	rx.Init(b)
   158  	if rb := rx.Pull(); rb == nil {
   159  		t.Fatalf("Pull failed on non-empty pipe")
   160  	}
   161  	rx.Flush()
   162  
   163  	// Make copy of original rx queue, flush it, then check that it didn't
   164  	// change.
   165  	orig := rx
   166  	rx.Flush()
   167  
   168  	if !reflect.DeepEqual(orig, rx) {
   169  		t.Fatalf("Flush mutated rx pipe: got %v, want %v", rx, orig)
   170  	}
   171  }
   172  
   173  func TestWrapInMiddleOfTransaction(t *testing.T) {
   174  	// Check that writes are not flushed when we need to wrap the buffer
   175  	// around.
   176  	b := make([]byte, 100)
   177  	var tx Tx
   178  	tx.Init(b)
   179  
   180  	if wb := tx.Push(50); wb == nil {
   181  		t.Fatalf("Push failed on empty pipe")
   182  	}
   183  	tx.Flush()
   184  
   185  	var rx Rx
   186  	rx.Init(b)
   187  	if rb := rx.Pull(); rb == nil {
   188  		t.Fatalf("Pull failed on non-empty pipe")
   189  	}
   190  	rx.Flush()
   191  
   192  	// At this point the ring buffer is empty, but the write is at offset
   193  	// 64 (50 + sizeOfSlotHeader + padding-for-8-byte-alignment).
   194  	if wb := tx.Push(10); wb == nil {
   195  		t.Fatalf("Push failed on empty pipe")
   196  	}
   197  
   198  	if wb := tx.Push(50); wb == nil {
   199  		t.Fatalf("Push failed on non-full pipe")
   200  	}
   201  
   202  	// We haven't flushed yet, so pull must return nil.
   203  	if rb := rx.Pull(); rb != nil {
   204  		t.Fatalf("Pull succeeded on non-flushed pipe")
   205  	}
   206  
   207  	tx.Flush()
   208  
   209  	// The two buffers must be available now.
   210  	if rb := rx.Pull(); rb == nil {
   211  		t.Fatalf("Pull failed on non-empty pipe")
   212  	}
   213  
   214  	if rb := rx.Pull(); rb == nil {
   215  		t.Fatalf("Pull failed on non-empty pipe")
   216  	}
   217  }
   218  
   219  func TestWriteAbort(t *testing.T) {
   220  	// Check that a read fails on a pipe that has had data pushed to it but
   221  	// has aborted the push.
   222  	b := make([]byte, 100)
   223  	var tx Tx
   224  	tx.Init(b)
   225  
   226  	if wb := tx.Push(10); wb == nil {
   227  		t.Fatalf("Write failed on empty pipe")
   228  	}
   229  
   230  	var rx Rx
   231  	rx.Init(b)
   232  	if rb := rx.Pull(); rb != nil {
   233  		t.Fatalf("Pull succeeded on empty pipe")
   234  	}
   235  
   236  	tx.Abort()
   237  	if rb := rx.Pull(); rb != nil {
   238  		t.Fatalf("Pull succeeded on empty pipe")
   239  	}
   240  }
   241  
   242  func TestWrappedWriteAbort(t *testing.T) {
   243  	// Check that writes are properly aborted even if the writes wrap
   244  	// around.
   245  	b := make([]byte, 100)
   246  	var tx Tx
   247  	tx.Init(b)
   248  
   249  	if wb := tx.Push(50); wb == nil {
   250  		t.Fatalf("Push failed on empty pipe")
   251  	}
   252  	tx.Flush()
   253  
   254  	var rx Rx
   255  	rx.Init(b)
   256  	if rb := rx.Pull(); rb == nil {
   257  		t.Fatalf("Pull failed on non-empty pipe")
   258  	}
   259  	rx.Flush()
   260  
   261  	// At this point the ring buffer is empty, but the write is at offset
   262  	// 64 (50 + sizeOfSlotHeader + padding-for-8-byte-alignment).
   263  	if wb := tx.Push(10); wb == nil {
   264  		t.Fatalf("Push failed on empty pipe")
   265  	}
   266  
   267  	if wb := tx.Push(50); wb == nil {
   268  		t.Fatalf("Push failed on non-full pipe")
   269  	}
   270  
   271  	// We haven't flushed yet, so pull must return nil.
   272  	if rb := rx.Pull(); rb != nil {
   273  		t.Fatalf("Pull succeeded on non-flushed pipe")
   274  	}
   275  
   276  	tx.Abort()
   277  
   278  	// The pushes were aborted, so no data should be readable.
   279  	if rb := rx.Pull(); rb != nil {
   280  		t.Fatalf("Pull succeeded on non-flushed pipe")
   281  	}
   282  
   283  	// Try the same transactions again, but flush this time.
   284  	if wb := tx.Push(10); wb == nil {
   285  		t.Fatalf("Push failed on empty pipe")
   286  	}
   287  
   288  	if wb := tx.Push(50); wb == nil {
   289  		t.Fatalf("Push failed on non-full pipe")
   290  	}
   291  
   292  	tx.Flush()
   293  
   294  	// The two buffers must be available now.
   295  	if rb := rx.Pull(); rb == nil {
   296  		t.Fatalf("Pull failed on non-empty pipe")
   297  	}
   298  
   299  	if rb := rx.Pull(); rb == nil {
   300  		t.Fatalf("Pull failed on non-empty pipe")
   301  	}
   302  }
   303  
   304  func TestEmptyReadOnNonFlushedWrite(t *testing.T) {
   305  	// Check that a read fails on a pipe that has had data pushed to it
   306  	// but not yet flushed.
   307  	b := make([]byte, 100)
   308  	var tx Tx
   309  	tx.Init(b)
   310  
   311  	if wb := tx.Push(10); wb == nil {
   312  		t.Fatalf("Write failed on empty pipe")
   313  	}
   314  
   315  	var rx Rx
   316  	rx.Init(b)
   317  	if rb := rx.Pull(); rb != nil {
   318  		t.Fatalf("Pull succeeded on empty pipe")
   319  	}
   320  
   321  	tx.Flush()
   322  	if rb := rx.Pull(); rb == nil {
   323  		t.Fatalf("Pull on failed on non-empty pipe")
   324  	}
   325  }
   326  
   327  func TestPullAfterPullingEntirePipe(t *testing.T) {
   328  	// Check that Pull fails when the pipe is full, but all of it has
   329  	// already been pulled but not yet flushed.
   330  	b := make([]byte, 100)
   331  	var tx Tx
   332  	tx.Init(b)
   333  
   334  	if wb := tx.Push(50); wb == nil {
   335  		t.Fatalf("Push failed on empty pipe")
   336  	}
   337  	tx.Flush()
   338  
   339  	var rx Rx
   340  	rx.Init(b)
   341  	if rb := rx.Pull(); rb == nil {
   342  		t.Fatalf("Pull failed on non-empty pipe")
   343  	}
   344  	rx.Flush()
   345  
   346  	// At this point the ring buffer is empty, but the write is at offset
   347  	// 64 (50 + sizeOfSlotHeader + padding-for-8-byte-alignment). Write 3
   348  	// buffers that will fill the pipe.
   349  	if wb := tx.Push(10); wb == nil {
   350  		t.Fatalf("Push failed on empty pipe")
   351  	}
   352  
   353  	if wb := tx.Push(20); wb == nil {
   354  		t.Fatalf("Push failed on non-full pipe")
   355  	}
   356  
   357  	if wb := tx.Push(24); wb == nil {
   358  		t.Fatalf("Push failed on non-full pipe")
   359  	}
   360  
   361  	tx.Flush()
   362  
   363  	// The three buffers must be available now.
   364  	if rb := rx.Pull(); rb == nil {
   365  		t.Fatalf("Pull failed on non-empty pipe")
   366  	}
   367  
   368  	if rb := rx.Pull(); rb == nil {
   369  		t.Fatalf("Pull failed on non-empty pipe")
   370  	}
   371  
   372  	if rb := rx.Pull(); rb == nil {
   373  		t.Fatalf("Pull failed on non-empty pipe")
   374  	}
   375  
   376  	// Fourth pull must fail.
   377  	if rb := rx.Pull(); rb != nil {
   378  		t.Fatalf("Pull succeeded on empty pipe")
   379  	}
   380  }
   381  
   382  func TestNoRoomToWrapOnPush(t *testing.T) {
   383  	// Check that Push fails when it tries to allocate room to add a wrap
   384  	// message.
   385  	b := make([]byte, 100)
   386  	var tx Tx
   387  	tx.Init(b)
   388  
   389  	if wb := tx.Push(50); wb == nil {
   390  		t.Fatalf("Push failed on empty pipe")
   391  	}
   392  	tx.Flush()
   393  
   394  	var rx Rx
   395  	rx.Init(b)
   396  	if rb := rx.Pull(); rb == nil {
   397  		t.Fatalf("Pull failed on non-empty pipe")
   398  	}
   399  	rx.Flush()
   400  
   401  	// At this point the ring buffer is empty, but the write is at offset
   402  	// 64 (50 + sizeOfSlotHeader + padding-for-8-byte-alignment). Write 20,
   403  	// which won't fit (64+20+8+padding = 96, which wouldn't leave room for
   404  	// the padding), so it wraps around.
   405  	if wb := tx.Push(20); wb == nil {
   406  		t.Fatalf("Push failed on empty pipe")
   407  	}
   408  
   409  	tx.Flush()
   410  
   411  	// Buffer offset is at 28. Try to write 70, which would require a wrap
   412  	// slot which cannot be created now.
   413  	if wb := tx.Push(70); wb != nil {
   414  		t.Fatalf("Push succeeded on pipe with no room for wrap message")
   415  	}
   416  }
   417  
   418  func TestRxImplicitFlushOfWrapMessage(t *testing.T) {
   419  	// Check if the first read is that of a wrapping message, that it gets
   420  	// immediately flushed.
   421  	b := make([]byte, 100)
   422  	var tx Tx
   423  	tx.Init(b)
   424  
   425  	if wb := tx.Push(50); wb == nil {
   426  		t.Fatalf("Push failed on empty pipe")
   427  	}
   428  	tx.Flush()
   429  
   430  	// This will cause a wrapping message to written.
   431  	if wb := tx.Push(60); wb != nil {
   432  		t.Fatalf("Push succeeded when there is no room in pipe")
   433  	}
   434  
   435  	var rx Rx
   436  	rx.Init(b)
   437  
   438  	// Read the first message.
   439  	if rb := rx.Pull(); rb == nil {
   440  		t.Fatalf("Pull failed on non-empty pipe")
   441  	}
   442  	rx.Flush()
   443  
   444  	// This should fail because of the wrapping message is taking up space.
   445  	if wb := tx.Push(60); wb != nil {
   446  		t.Fatalf("Push succeeded when there is no room in pipe")
   447  	}
   448  
   449  	// Try to read the next one. This should consume the wrapping message.
   450  	rx.Pull()
   451  
   452  	// This must now succeed.
   453  	if wb := tx.Push(60); wb == nil {
   454  		t.Fatalf("Push failed on empty pipe")
   455  	}
   456  }
   457  
   458  func TestConcurrentReaderWriter(t *testing.T) {
   459  	// Push a million buffers of random sizes and random contents. Check
   460  	// that buffers read match what was written.
   461  	tr := rand.New(rand.NewSource(99))
   462  	rr := rand.New(rand.NewSource(99))
   463  
   464  	b := make([]byte, 4096)
   465  	var tx Tx
   466  	tx.Init(b)
   467  
   468  	var rx Rx
   469  	rx.Init(b)
   470  
   471  	const count = 1000000
   472  	var wg sync.WaitGroup
   473  	defer wg.Wait()
   474  	wg.Add(1)
   475  	go func() {
   476  		defer wg.Done()
   477  		runtime.Gosched()
   478  		for i := 0; i < count; i++ {
   479  			n := 1 + tr.Intn(80)
   480  			wb := tx.Push(uint64(n))
   481  			for wb == nil {
   482  				wb = tx.Push(uint64(n))
   483  			}
   484  
   485  			for j := range wb {
   486  				wb[j] = byte(tr.Intn(256))
   487  			}
   488  
   489  			tx.Flush()
   490  		}
   491  	}()
   492  
   493  	for i := 0; i < count; i++ {
   494  		n := 1 + rr.Intn(80)
   495  		rb := rx.Pull()
   496  		for rb == nil {
   497  			rb = rx.Pull()
   498  		}
   499  
   500  		if n != len(rb) {
   501  			t.Fatalf("Bad %v-th buffer length: got %v, want %v", i, len(rb), n)
   502  		}
   503  
   504  		for j := range rb {
   505  			if v := byte(rr.Intn(256)); v != rb[j] {
   506  				t.Fatalf("Bad %v-th read buffer at index %v: got %v, want %v", i, j, rb[j], v)
   507  			}
   508  		}
   509  
   510  		rx.Flush()
   511  	}
   512  }