Ejemplo n.º 1
0
 async def epoll_wait(self, events: Pointer[EpollEventList], timeout: int) -> t.Tuple[Pointer[EpollEventList], Pointer]:
     self._validate()
     with events.borrow(self.task) as events_n:
         num = await rsyscall.near.epoll_wait(
             self.task.sysif, self.near, events_n, events.size()//EpollEvent.sizeof(), timeout)
         valid_size = num * EpollEvent.sizeof()
         return events.split(valid_size)
Ejemplo n.º 2
0
    async def _run(self) -> None:
        input_buf: Pointer = await self.epfd.task.malloc(
            EpollEventList, 32 * EpollEvent.sizeof())
        number_to_cb: t.Dict[int, Continuation[EPOLL]] = {}
        registered_activity_fd: t.Optional[FileDescriptor] = None
        while True:
            if self.wait_readable:
                await self.wait_readable()
            activity_fd = self.epfd.task.sysif.get_activity_fd()
            if activity_fd and (registered_activity_fd is not activity_fd):
                # the activity fd changed, we need to register the new one
                if registered_activity_fd:
                    # delete the old registered activity fd
                    await self.epfd.epoll_ctl(EPOLL_CTL.DEL,
                                              registered_activity_fd)
                activity_fd_number = self.allocate_number(int(activity_fd))

                # start up a coroutine to consume events from the activity_fd
                async def devnull(activity_fd_number=activity_fd_number):
                    while True:
                        await self.queue.request(activity_fd_number)

                reset(devnull())
                await self.epfd.epoll_ctl(
                    EPOLL_CTL.ADD,
                    activity_fd,
                    await self.epfd.task.ptr(
                        EpollEvent(
                            activity_fd_number,
                            # not edge triggered; we don't want to block if there's
                            # anything that can be read.
                            EPOLL.IN | EPOLL.RDHUP | EPOLL.PRI | EPOLL.ERR
                            | EPOLL.HUP)))
                registered_activity_fd = activity_fd
            try:
                valid_events_buf, rest = await self.epfd.epoll_wait(
                    input_buf, self.timeout)
                received_events = await valid_events_buf.read()
            except SyscallHangup:
                # retry the epoll_wait to support rsyscall.tasks.persistent, as documented there;
                # for non-persistent tasks this will just fail with a SyscallSendError next time around.
                continue
            except Exception as wait_error:
                final_exn = wait_error
                break
            input_buf = valid_events_buf + rest
            for num, cb in self.queue.fetch_any():
                number_to_cb[num] = cb
            for event in received_events:
                number_to_cb[event.data].send(event.events)
                del number_to_cb[event.data]
            for num, cb in self.queue.fetch_any():
                number_to_cb[num] = cb
            for number in list(self.pending_remove):
                number_to_cb[number].throw(RemovedFromEpollError())
                del number_to_cb[number]
                self.pending_remove.remove(number)
        self.queue.close(final_exn)
Ejemplo n.º 3
0
    async def do_wait(self) -> None:
        """Perform an interruptible epoll_wait, calling callbacks with any events.

        We take care to store the running state of this method on the object, so that if
        we're cancelled, someone else can continue the call without dropping any events.
        It would be nice if we didn't have to do that manually; see "shared coroutine"
        notion in the docstring of OneAtATime for what we'd prefer.

        """
        async with self.running_wait.needs_run() as needs_run:
            if needs_run:
                for number in self.pending_remove:
                    del self.number_to_cb[number]
                self.pending_remove = set()
                maxevents = 32
                if self.input_buf is None:
                    self.input_buf = await self.ram.malloc(EpollEventList, maxevents * EpollEvent.sizeof())
                if self.syscall_response is None:
                    if self.wait_readable:
                        await self.wait_readable()
                    self.syscall_response = await self.epfd.task.sysif.submit_syscall(
                        SYS.epoll_wait, self.epfd.near, self.input_buf.near, maxevents, self.timeout)
                if self.valid_events_buf is None:
                    count = await self.syscall_response.receive()
                    self.valid_events_buf, _ = self.input_buf.split(count * EpollEvent.sizeof())
                received_events = await self.valid_events_buf.read()
                self.input_buf = None
                self.valid_events_buf = None
                self.syscall_response = None
                for event in received_events:
                    if event.data not in self.pending_remove:
                        self.number_to_cb[event.data](event.events)
Ejemplo n.º 4
0
 async def epoll_ctl(self, op: EPOLL_CTL, fd: FileDescriptor, event: t.Optional[Pointer[EpollEvent]]=None) -> None:
     self._validate()
     with fd.borrow(self.task) as fd_n:
         if event is not None:
             if event.size() < EpollEvent.sizeof():
                 raise Exception("pointer is too small", event.size(), "to be an EpollEvent", EpollEvent.sizeof())
             with event.borrow(self.task) as event_n:
                 return (await rsyscall.near.epoll_ctl(self.task.sysif, self.near, op, fd_n, event_n))
         else:
             return (await rsyscall.near.epoll_ctl(self.task.sysif, self.near, op, fd_n))
Ejemplo n.º 5
0
    async def register(self, fd: FileDescriptor, events: EPOLL,
                       cb: t.Callable[[EPOLL], None]) -> EpolledFileDescriptor:
        """Register a file descriptor on this epollfd, for the given events, calling the passed callback.

        The return value can be used to wait for callback calls, modify the events
        registered for this file descriptor, and delete the file descriptor from this
        epollfd.

        """
        number = self.epoll_waiter.add_and_allocate_number(cb)
        await self.epfd.epoll_ctl(EPOLL_CTL.ADD, fd, await self.ram.ptr(EpollEvent(number, events)))
        return EpolledFileDescriptor(self, fd, number)
Ejemplo n.º 6
0
    async def register(self, fd: FileDescriptor,
                       events: EPOLL) -> EpolledFileDescriptor:
        """Register a file descriptor on this epollfd, for the given events, calling the passed callback.

        The return value can be used to wait for callback calls, modify the events
        registered for this file descriptor, and delete the file descriptor from this
        epollfd.

        """
        number = self.epoll_waiter.allocate_number(int(fd.near))
        efd = EpolledFileDescriptor(self, fd, number)
        await self.epfd.epoll_ctl(
            EPOLL_CTL.ADD, fd, await
            self.epfd.task.ptr(EpollEvent(number, events)))
        return efd
Ejemplo n.º 7
0
 async def modify(self, events: EPOLL) -> None:
     "Change the EPOLL flags that this fd is registered with."
     await self.epoller.epfd.epoll_ctl(
         EPOLL_CTL.MOD, self.fd, await
         self.epoller.ram.ptr(EpollEvent(self.number, events)))