github.com/iceber/iouring-go@v0.0.0-20230403020409-002cfd2e2a90/link_request.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  package iouring
     5  
     6  import (
     7  	"errors"
     8  	"time"
     9  	"unsafe"
    10  
    11  	"golang.org/x/sys/unix"
    12  
    13  	iouring_syscall "github.com/iceber/iouring-go/syscall"
    14  )
    15  
    16  func (iour *IOURing) SubmitLinkRequests(requests []PrepRequest, ch chan<- Result) (RequestSet, error) {
    17  	return iour.submitLinkRequest(requests, ch, false)
    18  }
    19  
    20  func (iour *IOURing) SubmitHardLinkRequests(requests []PrepRequest, ch chan<- Result) (RequestSet, error) {
    21  	return iour.submitLinkRequest(requests, ch, true)
    22  }
    23  
    24  func (iour *IOURing) submitLinkRequest(requests []PrepRequest, ch chan<- Result, hard bool) (RequestSet, error) {
    25  	// TODO(iceber): no length limit
    26  	if len(requests) > int(*iour.sq.entries) {
    27  		return nil, errors.New("too many requests")
    28  	}
    29  
    30  	flags := iouring_syscall.IOSQE_FLAGS_IO_LINK
    31  	if hard {
    32  		flags = iouring_syscall.IOSQE_FLAGS_IO_HARDLINK
    33  	}
    34  
    35  	iour.submitLock.Lock()
    36  	defer iour.submitLock.Unlock()
    37  
    38  	if iour.IsClosed() {
    39  		return nil, ErrIOURingClosed
    40  	}
    41  
    42  	var sqeN uint32
    43  	userDatas := make([]*UserData, 0, len(requests))
    44  	for i := range requests {
    45  		sqe := iour.getSQEntry()
    46  		sqeN++
    47  
    48  		userData, err := iour.doRequest(sqe, requests[i], ch)
    49  		if err != nil {
    50  			iour.sq.fallback(sqeN)
    51  			return nil, err
    52  		}
    53  		userDatas = append(userDatas, userData)
    54  
    55  		sqe.CleanFlags(iouring_syscall.IOSQE_FLAGS_IO_HARDLINK | iouring_syscall.IOSQE_FLAGS_IO_LINK)
    56  		if i < len(requests)-1 {
    57  			sqe.SetFlags(flags)
    58  		}
    59  	}
    60  
    61  	// must be located before the lock operation to
    62  	// avoid the compiler's adjustment of the code order.
    63  	// issue: https://github.com/Iceber/iouring-go/issues/8
    64  	rset := newRequestSet(userDatas)
    65  
    66  	iour.userDataLock.Lock()
    67  	for _, data := range userDatas {
    68  		iour.userDatas[data.id] = data
    69  	}
    70  	iour.userDataLock.Unlock()
    71  
    72  	if _, err := iour.submit(); err != nil {
    73  		iour.userDataLock.Lock()
    74  		for _, data := range userDatas {
    75  			delete(iour.userDatas, data.id)
    76  		}
    77  		iour.userDataLock.Unlock()
    78  
    79  		return nil, err
    80  	}
    81  
    82  	return rset, nil
    83  }
    84  
    85  func linkTimeout(t time.Duration) PrepRequest {
    86  	timespec := unix.NsecToTimespec(t.Nanoseconds())
    87  
    88  	return func(sqe iouring_syscall.SubmissionQueueEntry, userData *UserData) {
    89  		userData.hold(&timespec)
    90  		userData.request.resolver = timeoutResolver
    91  
    92  		sqe.PrepOperation(iouring_syscall.IORING_OP_LINK_TIMEOUT, -1, uint64(uintptr(unsafe.Pointer(&timespec))), 1, 0)
    93  	}
    94  }