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

     1  // +build linux
     2  
     3  package iouring
     4  
     5  import (
     6  	"errors"
     7  	"os"
     8  	"sync"
     9  	"unsafe"
    10  
    11  	iouring_syscall "github.com/iceber/iouring-go/syscall"
    12  )
    13  
    14  func (iour *IOURing) GetFixedFileIndex(file *os.File) (int, bool) {
    15  	return iour.fileRegister.GetFileIndex(int32(file.Fd()))
    16  }
    17  
    18  func (iour *IOURing) RegisterFile(file *os.File) error {
    19  	return iour.fileRegister.RegisterFile(int32(file.Fd()))
    20  }
    21  
    22  func (iour *IOURing) RegisterFiles(files []*os.File) error {
    23  	fds := make([]int32, 0, len(files))
    24  	for _, file := range files {
    25  		fds = append(fds, int32(file.Fd()))
    26  	}
    27  
    28  	return iour.fileRegister.RegisterFiles(fds)
    29  }
    30  
    31  func (iour *IOURing) UnregisterFile(file *os.File) error {
    32  	return iour.fileRegister.UnregisterFile(int32(file.Fd()))
    33  }
    34  
    35  func (iour *IOURing) UnregisterFiles(files []*os.File) error {
    36  	fds := make([]int32, 0, len(files))
    37  	for _, file := range files {
    38  		fds = append(fds, int32(file.Fd()))
    39  	}
    40  
    41  	return iour.fileRegister.UnregisterFiles(fds)
    42  }
    43  
    44  func (iour *IOURing) FileRegister() FileRegister {
    45  	return iour.fileRegister
    46  }
    47  
    48  type FileRegister interface {
    49  	GetFileIndex(fd int32) (int, bool)
    50  	RegisterFile(fd int32) error
    51  	RegisterFiles(fds []int32) error
    52  	UnregisterFile(fd int32) error
    53  	UnregisterFiles(fds []int32) error
    54  }
    55  
    56  type fileRegister struct {
    57  	lock      sync.Mutex
    58  	iouringFd int
    59  
    60  	fds          []int32
    61  	sparseIndexs map[int]int
    62  
    63  	registered bool
    64  	indexs     sync.Map
    65  }
    66  
    67  func (register *fileRegister) GetFileIndex(fd int32) (int, bool) {
    68  	if fd < 0 {
    69  		return -1, false
    70  	}
    71  
    72  	i, ok := register.indexs.Load(fd)
    73  	if !ok {
    74  		return -1, false
    75  	}
    76  	return i.(int), true
    77  }
    78  
    79  func (register *fileRegister) register() error {
    80  	if err := iouring_syscall.IOURingRegister(
    81  		register.iouringFd,
    82  		iouring_syscall.IORING_REGISTER_FILES,
    83  		unsafe.Pointer(&register.fds[0]),
    84  		uint32(len(register.fds)),
    85  	); err != nil {
    86  		return err
    87  	}
    88  
    89  	for i, fd := range register.fds {
    90  		register.indexs.Store(fd, i)
    91  	}
    92  	register.registered = true
    93  	return nil
    94  }
    95  
    96  func (register *fileRegister) unregister() error {
    97  	return iouring_syscall.IOURingRegister(register.iouringFd, iouring_syscall.IORING_UNREGISTER_FILES, nil, 0)
    98  }
    99  
   100  func (register *fileRegister) RegisterFiles(fds []int32) error {
   101  	if len(fds) == 0 {
   102  		return errors.New("file set is empty")
   103  	}
   104  
   105  	vfds := make([]int32, 0, len(fds))
   106  	for _, fd := range fds {
   107  		if fd < 0 {
   108  			continue
   109  		}
   110  
   111  		if _, ok := register.indexs.Load(fd); ok {
   112  			continue
   113  		}
   114  		vfds = append(vfds, fd)
   115  	}
   116  
   117  	if len(vfds) == 0 {
   118  		return nil
   119  	}
   120  	fds = vfds
   121  
   122  	register.lock.Lock()
   123  	defer register.lock.Unlock()
   124  
   125  	if !register.registered {
   126  		register.fds = fds
   127  		return register.register()
   128  	}
   129  
   130  	var fdi int
   131  	indexs := make(map[int32]int, len(fds))
   132  	updatedSpares := make(map[int]int, len(fds))
   133  	for i, spares := range register.sparseIndexs {
   134  		updatedSpares[i] = spares
   135  		for si := 0; si < spares; si++ {
   136  			for ; fdi < len(fds); fdi++ {
   137  				register.fds[i+si] = fds[fdi]
   138  				indexs[fds[fdi]] = i + si
   139  
   140  				fdi++
   141  				updatedSpares[i]--
   142  				break
   143  			}
   144  
   145  			if fdi >= len(fds) {
   146  				goto update
   147  			}
   148  		}
   149  	}
   150  	register.fds = append(register.fds, fds[fdi:]...)
   151  
   152  update:
   153  	if err := register.fresh(0, len(register.fds)); err != nil {
   154  		return err
   155  	}
   156  
   157  	for i, spares := range updatedSpares {
   158  		if spares > 0 {
   159  			register.sparseIndexs[i] = spares
   160  			continue
   161  		}
   162  		delete(register.sparseIndexs, i)
   163  	}
   164  	for i, fd := range register.fds {
   165  		register.indexs.Store(fd, i)
   166  	}
   167  
   168  	return nil
   169  }
   170  
   171  func (register *fileRegister) RegisterFile(fd int32) error {
   172  	if fd < 0 {
   173  		return nil
   174  	}
   175  
   176  	if _, ok := register.GetFileIndex(fd); ok {
   177  		return nil
   178  	}
   179  
   180  	register.lock.Lock()
   181  	defer register.lock.Unlock()
   182  
   183  	if !register.registered {
   184  		register.fds = []int32{fd}
   185  		return register.register()
   186  	}
   187  
   188  	var fdi int
   189  	var spares int
   190  	for fdi, spares = range register.sparseIndexs {
   191  		break
   192  	}
   193  	register.fds[fdi] = fd
   194  
   195  	if err := register.fresh(fdi, 1); err != nil {
   196  		return err
   197  	}
   198  
   199  	if spares == 1 {
   200  		delete(register.sparseIndexs, fdi)
   201  	} else {
   202  		register.sparseIndexs[fdi]--
   203  	}
   204  
   205  	register.indexs.Store(fd, fdi)
   206  	return nil
   207  }
   208  
   209  func (register *fileRegister) UnregisterFile(fd int32) error {
   210  	if fd < 0 {
   211  		return nil
   212  	}
   213  
   214  	register.lock.Lock()
   215  	defer register.lock.Unlock()
   216  
   217  	fdi, ok := register.deleteFile(fd)
   218  	if !ok {
   219  		return nil
   220  	}
   221  
   222  	return register.fresh(fdi, 1)
   223  }
   224  
   225  func (register *fileRegister) UnregisterFiles(fds []int32) error {
   226  	register.lock.Lock()
   227  	defer register.lock.Unlock()
   228  
   229  	var unregistered bool
   230  	for _, fd := range fds {
   231  		if fd < 0 {
   232  			continue
   233  		}
   234  
   235  		_, ok := register.deleteFile(fd)
   236  		if !ok {
   237  			continue
   238  		}
   239  		unregistered = true
   240  	}
   241  	if unregistered {
   242  		return nil
   243  	}
   244  
   245  	return register.fresh(0, len(register.fds))
   246  }
   247  
   248  func (register *fileRegister) deleteFile(fd int32) (fdi int, ok bool) {
   249  	var v interface{}
   250  	/*
   251  		go version >= 1.15
   252  
   253  		v, ok = register.index.LoadAndDelete(fd)
   254  	*/
   255  	v, ok = register.indexs.Load(fd)
   256  	if !ok {
   257  		return
   258  	}
   259  	register.indexs.Delete(fd)
   260  
   261  	fdi = v.(int)
   262  	register.fds[fdi] = -1
   263  
   264  	var updated bool
   265  	for i, sparse := range register.sparseIndexs {
   266  		if i+sparse == fdi {
   267  			register.sparseIndexs[i]++
   268  			updated = true
   269  			break
   270  		}
   271  	}
   272  
   273  	if !updated {
   274  		register.sparseIndexs[fdi] = 1
   275  	}
   276  	return
   277  }
   278  
   279  func (register *fileRegister) fresh(offset int, length int) error {
   280  	update := iouring_syscall.IOURingFilesUpdate{
   281  		Offset: uint32(offset),
   282  		Fds:    &register.fds[offset],
   283  	}
   284  	return iouring_syscall.IOURingRegister(
   285  		register.iouringFd,
   286  		iouring_syscall.IORING_REGISTER_FILES_UPDATE,
   287  		unsafe.Pointer(&update),
   288  		uint32(len(register.fds)),
   289  	)
   290  }