With our asynchronous socket(), we have the very first operation working with file descriptors on board. This also immediately brings us to yet another design challenge for an abstraction in our system:
class Resource:
def __init__(self, handle: int, direct: bool):
self._handle = handle
self._direct = direct
async def __aenter__(self) -> typing.Self:
return self
async def __aexit__(self, *exc: typing.Any):
await self.close()
async def close(self):
handle, self._handle = self._handle, -1
await cancel_all_fd_ops(handle, self.direct)
await self.close(handle, self.direct)
You'll notice that handle sure enough is our file descriptor and the direct flag to distinguish regular file descriptors and io_uring direct descriptors.
Direct descriptors?
io_uring has a cool feature where you preallocate a sparse table of descriptors and then you can allocate direct descriptors to it which act synonymous to regular file descriptors. The difference is that manipulating them is more efficient because they avoid the fget/fput overhead associated with refcounting in the kernel.
We want to be able to use both in boros so some distinction is necessary.
Closing resources
When closing a resource, it is very important that we first cancel all ongoing operations and invalidate the object. This will also prevent consumers from scheduling more operations on the same file after cancellation.
This is very important to prevent race conditions where a descriptor gets freed, reassigned to another resource, and then previously scheduled operations manipulate the new resource.
And mimicking existing APIs commonly found in Python, we offer both an explicit close method and context manager ergonomics.
Naming
Should we actually name the close method close? Since this will be the first sample of public API in boros, we want to continue with a uniform style in the future.
aclose is also a somewhat popular alternative.
Optimization
We can link the cancellation and the close operation together into a single chained operation.
With our asynchronous
socket(), we have the very first operation working with file descriptors on board. This also immediately brings us to yet another design challenge for an abstraction in our system:You'll notice that
handlesure enough is our file descriptor and thedirectflag to distinguish regular file descriptors and io_uring direct descriptors.Direct descriptors?
io_uring has a cool feature where you preallocate a sparse table of descriptors and then you can allocate direct descriptors to it which act synonymous to regular file descriptors. The difference is that manipulating them is more efficient because they avoid the
fget/fputoverhead associated with refcounting in the kernel.We want to be able to use both in boros so some distinction is necessary.
Closing resources
When closing a resource, it is very important that we first cancel all ongoing operations and invalidate the object. This will also prevent consumers from scheduling more operations on the same file after cancellation.
This is very important to prevent race conditions where a descriptor gets freed, reassigned to another resource, and then previously scheduled operations manipulate the new resource.
And mimicking existing APIs commonly found in Python, we offer both an explicit close method and context manager ergonomics.
Naming
Should we actually name the
closemethodclose? Since this will be the first sample of public API in boros, we want to continue with a uniform style in the future.acloseis also a somewhat popular alternative.Optimization
We can link the cancellation and the close operation together into a single chained operation.