github.com/dshulyak/uring@v0.0.0-20210209113719-1b2ec51f1542/ring_test.go (about)

     1  package uring
     2  
     3  import (
     4  	"io/ioutil"
     5  	"math/rand"
     6  	"os"
     7  	"syscall"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func TestWritev(t *testing.T) {
    15  	f, err := ioutil.TempFile("", "writev-tests-")
    16  	require.NoError(t, err)
    17  	defer f.Close()
    18  
    19  	ring, err := Setup(4, nil)
    20  	require.NoError(t, err)
    21  	defer ring.Close()
    22  
    23  	var offset uint64
    24  	bufs := [4][8]byte{}
    25  	vectors := [4][]syscall.Iovec{}
    26  
    27  	for round := 0; round < 10; round++ {
    28  		for i := 0; i < 4; i++ {
    29  			buf := bufs[i]
    30  			_, _ = rand.Read(buf[:])
    31  			bufs[i] = buf
    32  			vectors[i] = []syscall.Iovec{
    33  				{
    34  					Base: &buf[0],
    35  					Len:  uint64(len(buf)),
    36  				},
    37  			}
    38  			sqe := ring.GetSQEntry()
    39  			Writev(sqe, f.Fd(), vectors[i], offset, 0)
    40  			offset += uint64(len(buf))
    41  		}
    42  
    43  		_, err = ring.Submit(4)
    44  		require.NoError(t, err)
    45  
    46  		for i := 0; i < 4; i++ {
    47  			cqe, err := ring.GetCQEntry(0)
    48  			require.NoError(t, err)
    49  			require.True(t, cqe.Result() >= 0, "failed with %v", syscall.Errno(-cqe.Result()))
    50  		}
    51  
    52  		buf := [8]byte{}
    53  		for i := 0; i < 4; i++ {
    54  			n, err := f.Read(buf[:])
    55  			require.NoError(t, err)
    56  			require.Equal(t, len(buf), n)
    57  			require.Equal(t, bufs[i], buf)
    58  		}
    59  	}
    60  }
    61  
    62  func TestReadv(t *testing.T) {
    63  	f, err := ioutil.TempFile("", "readv-tests-")
    64  	require.NoError(t, err)
    65  	defer f.Close()
    66  
    67  	ring, err := Setup(4, nil)
    68  	require.NoError(t, err)
    69  	defer ring.Close()
    70  
    71  	var offset uint64
    72  	const num = 3
    73  	bufs := [num][8]byte{}
    74  	vectors := [num][]syscall.Iovec{}
    75  
    76  	for round := 0; round < 10; round++ {
    77  
    78  		wbuf := [num * 8]byte{}
    79  
    80  		_, _ = rand.Read(wbuf[:])
    81  		n, err := f.Write(wbuf[:])
    82  		require.NoError(t, err)
    83  		require.Equal(t, len(wbuf), n)
    84  
    85  		for i := 0; i < num; i++ {
    86  			sqe := ring.GetSQEntry()
    87  			vectors[i] = []syscall.Iovec{
    88  				{
    89  					Base: &bufs[i][0],
    90  					Len:  uint64(len(bufs[i])),
    91  				},
    92  			}
    93  			Readv(sqe, f.Fd(), vectors[i], offset, 0)
    94  			offset += uint64(len(bufs[i]))
    95  		}
    96  
    97  		_, err = ring.Submit(num)
    98  		require.NoError(t, err)
    99  
   100  		for i := 0; i < num; i++ {
   101  			cqe, err := ring.GetCQEntry(0)
   102  			require.NoError(t, err)
   103  			require.Equal(t, len(bufs[i]), int(cqe.Result()), "failed with %v", syscall.Errno(-cqe.Result()))
   104  			require.Equal(t, wbuf[i*8:(i+1)*8], bufs[i][:])
   105  		}
   106  	}
   107  }
   108  
   109  func TestCopy(t *testing.T) {
   110  	from, err := ioutil.TempFile("", "copy-from-")
   111  	require.NoError(t, err)
   112  	defer from.Close()
   113  
   114  	to, err := ioutil.TempFile("", "copy-to-")
   115  	require.NoError(t, err)
   116  	defer to.Close()
   117  
   118  	ring, err := Setup(4, nil)
   119  	require.NoError(t, err)
   120  	defer ring.Close()
   121  
   122  	buf := make([]byte, 4096)
   123  	_, _ = rand.Read(buf)
   124  	_, err = from.Write(buf)
   125  	require.NoError(t, err)
   126  	off, err := from.Seek(0, 0)
   127  	require.NoError(t, err)
   128  	require.Equal(t, int64(0), off)
   129  
   130  	reuse := [32]byte{}
   131  	rlth := uint64(len(reuse))
   132  	vector := []syscall.Iovec{
   133  		{
   134  			Base: &reuse[0],
   135  			Len:  rlth,
   136  		},
   137  	}
   138  	var (
   139  		offset uint64
   140  	)
   141  	for {
   142  		read := ring.GetSQEntry()
   143  		write := ring.GetSQEntry()
   144  
   145  		Readv(read, from.Fd(), vector, offset, 0)
   146  		read.SetFlags(IOSQE_IO_LINK)
   147  		Writev(write, to.Fd(), vector, offset, 0)
   148  
   149  		_, err := ring.Submit(2)
   150  		require.NoError(t, err)
   151  
   152  		rcqe, err := ring.GetCQEntry(0)
   153  		require.NoError(t, err)
   154  		require.True(t, rcqe.Result() >= 0, "read result %d ('%v')", rcqe.Result(), syscall.Errno(-rcqe.Result()))
   155  
   156  		ret := rcqe.Result()
   157  		if ret == 0 {
   158  			break
   159  		}
   160  
   161  		wcqe, err := ring.GetCQEntry(0)
   162  		require.NoError(t, err)
   163  		require.Equal(t, ret, wcqe.Result(), "write result %d ('%v')", wcqe.Result(), syscall.Errno(-wcqe.Result()))
   164  
   165  		offset += rlth
   166  	}
   167  
   168  	fromData, err := ioutil.ReadAll(from)
   169  	toData, err := ioutil.ReadAll(to)
   170  	require.NoError(t, err, "failed to read 'from'")
   171  	require.NoError(t, err, "failed to read 'to'")
   172  	require.Equal(t, len(fromData), len(toData))
   173  	require.Equal(t, fromData, toData)
   174  }
   175  
   176  func TestReuseSQEntries(t *testing.T) {
   177  	ring, err := Setup(2, nil)
   178  	require.NoError(t, err)
   179  
   180  	for r := 0; r < 10; r++ {
   181  		for i := 1; i <= 2; i++ {
   182  			sqe := ring.GetSQEntry()
   183  			sqe.Reset()
   184  			require.Equal(t, uint64(0), sqe.userData)
   185  			Nop(sqe)
   186  			sqe.SetUserData(uint64(i))
   187  		}
   188  		n, err := ring.Submit(2)
   189  		require.NoError(t, err)
   190  		require.Equal(t, uint32(2), n)
   191  
   192  		for i := 1; i <= 2; i++ {
   193  			cqe, err := ring.GetCQEntry(0)
   194  			require.NoError(t, err)
   195  			require.Equal(t, uint64(i), cqe.UserData())
   196  		}
   197  	}
   198  
   199  }
   200  
   201  func TestNoEnter(t *testing.T) {
   202  	ring, err := Setup(4, nil)
   203  	require.NoError(t, err)
   204  	defer ring.Close()
   205  
   206  	sqe := ring.GetSQEntry()
   207  	Nop(sqe)
   208  	_, err = ring.Submit(0)
   209  	require.NoError(t, err)
   210  
   211  	start := time.Now()
   212  	for time.Since(start) < time.Second {
   213  		_, err := ring.GetCQEntry(0)
   214  		if err == nil {
   215  			return
   216  		}
   217  	}
   218  	require.FailNow(t, "nop operation wasn't completed")
   219  }
   220  
   221  func TestResubmitBeforeCompletion(t *testing.T) {
   222  	n := 2048
   223  	ring, err := Setup(uint(n), nil)
   224  	require.NoError(t, err)
   225  	defer ring.Close()
   226  
   227  	for round := 0; round < 2; round++ {
   228  		// sq entry can be reused after call to Submit returned
   229  		for i := uint64(1); i <= uint64(n); i++ {
   230  			sqe := ring.GetSQEntry()
   231  			Nop(sqe)
   232  			sqe.SetUserData(i)
   233  		}
   234  
   235  		_, err = ring.Submit(0)
   236  		require.NoError(t, err)
   237  	}
   238  	for round := 0; round < 2; round++ {
   239  		for i := uint64(1); i <= uint64(n); i++ {
   240  			for {
   241  				cqe, err := ring.GetCQEntry(0)
   242  				if err != nil {
   243  					continue
   244  				}
   245  				require.Equal(t, i, cqe.UserData())
   246  				break
   247  			}
   248  		}
   249  	}
   250  }
   251  
   252  func TestReadWriteFixed(t *testing.T) {
   253  	ring, err := Setup(32, nil)
   254  	require.NoError(t, err)
   255  	defer ring.Close()
   256  
   257  	f, err := ioutil.TempFile("", "test")
   258  	require.NoError(t, err)
   259  	defer os.Remove(f.Name())
   260  
   261  	data := []byte("ping")
   262  	resp := make([]byte, len(data))
   263  	iovec := []syscall.Iovec{
   264  		{
   265  			Base: &data[0],
   266  			Len:  uint64(len(data)),
   267  		},
   268  		{
   269  			Base: &resp[0],
   270  			Len:  uint64(len(data)),
   271  		},
   272  	}
   273  
   274  	require.NoError(t, ring.RegisterBuffers(iovec))
   275  
   276  	sqe := ring.GetSQEntry()
   277  	WriteFixed(sqe, f.Fd(), iovec[0].Base, iovec[0].Len, 0, 0, 0)
   278  	_, err = ring.Submit(1)
   279  	require.NoError(t, err)
   280  
   281  	cqe, err := ring.GetCQEntry(1)
   282  	require.NoError(t, err)
   283  	require.Equal(t, int32(len(data)), cqe.Result(), syscall.Errno(-cqe.Result()))
   284  
   285  	out := make([]byte, len(data))
   286  	_, err = f.ReadAt(out, 0)
   287  	require.NoError(t, err)
   288  	require.Equal(t, data, out)
   289  
   290  	in := []byte("pong")
   291  	_, err = f.WriteAt(in, 0)
   292  	require.NoError(t, err)
   293  
   294  	sqe = ring.GetSQEntry()
   295  	ReadFixed(sqe, f.Fd(), iovec[1].Base, iovec[1].Len, 0, 0, 1)
   296  	_, err = ring.Submit(1)
   297  	require.NoError(t, err)
   298  
   299  	cqe, err = ring.GetCQEntry(1)
   300  	require.NoError(t, err)
   301  	require.Equal(t, int32(len(data)), cqe.Result(), syscall.Errno(-cqe.Result()))
   302  
   303  	require.Equal(t, in, resp)
   304  }
   305  
   306  func TestIOPoll(t *testing.T) {
   307  	ring, err := Setup(4, &IOUringParams{Flags: IORING_SETUP_IOPOLL})
   308  	require.NoError(t, err)
   309  	defer ring.Close()
   310  
   311  	// returns immediatly
   312  	_, err = ring.GetCQEntry(0)
   313  	require.Error(t, syscall.EAGAIN, err)
   314  
   315  	// returns once consumed scheduler time slice
   316  	_, err = ring.GetCQEntry(1)
   317  	require.Error(t, syscall.EAGAIN, err)
   318  
   319  	// TODO IOPOLL currently not supported on my devices
   320  }