async def op(sem: RAM) -> t.Tuple[Pointer[Stack], WrittenPointer[Stack]]: stack_value = loader.make_trampoline_stack(Trampoline( loader.futex_helper_func, [ int(futex_pointer.near + ffi.offsetof('struct futex_node', 'futex')), futex_pointer.value.futex])) stack_buf = await sem.malloc(Stack, 4096) stack = await stack_buf.write_to_end(stack_value, alignment=16) return stack
def to_bytes(self) -> bytes: struct = ffi.new( 'struct robust_list_head*', { 'list': (ffi.cast('struct robust_list*', int(self.first.near)), ), 'futex_offset': ffi.offsetof('struct futex_node', 'futex'), 'list_op_pending': ffi.NULL, }) return bytes(ffi.buffer(struct))
async def clone( self, flags: CLONE, # these two pointers must be adjacent; the end of the first is the start of the # second. the first is the allocation for stack growth, the second is the data # we've written on the stack that will be popped off for arguments. child_stack: t.Tuple[Pointer[Stack], WrittenPointer[Stack]], ptid: t.Optional[Pointer], ctid: t.Optional[Pointer[FutexNode]], # this points to anything, it depends on the thread implementation newtls: t.Optional[Pointer] ) -> ThreadProcess: clone_parent = bool(flags & CLONE.PARENT) if clone_parent: if self.parent_task is None: raise Exception( "using CLONE.PARENT, but we don't know our parent task") # TODO also check that the parent_task hasn't shut down... not sure how to do that owning_task = self.parent_task else: owning_task = self with contextlib.ExitStack() as stack: stack_alloc, stack_data = child_stack if (int(stack_data.near) % 16) != 0: raise Exception( "child stack must have 16-byte alignment, so says Intel") stack_alloc_end = stack_alloc.near + stack_alloc.size() if stack_alloc_end != stack_data.near: raise Exception("the end of the stack allocation pointer", stack_alloc_end, "and the beginning of the stack data pointer", stack_data.near, "must be the same") stack.enter_context(stack_alloc.borrow(self)) stack.enter_context(stack_data.borrow(self)) ptid_n = self._borrow_optional(stack, ptid) ctid_n = self._borrow_optional(stack, ctid) newtls_n = self._borrow_optional(stack, newtls) process = await _clone( self.sysif, flags, stack_data.near, ptid_n, ctid_n + ffi.offsetof('struct futex_node', 'futex') if ctid_n else None, newtls_n) # TODO the safety of this depends on no-one borrowing/freeing the stack in borrow __aexit__ # should try to do this a bit more robustly... merged_stack = stack_alloc.merge(stack_data) return ThreadProcess(owning_task, process, merged_stack, stack_data.value, ctid, newtls)
async def launch_futex_monitor( loader: NativeLoader, monitor: ChildPidMonitor, futex_pointer: WrittenPointer[FutexNode]) -> AsyncChildPid: """Launch a process to wait on a futex; then we monitor the process to monitor the futex This process calls futex(futex_pointer, FUTEX_WAIT, futex_pointer.value) and then exits, so this process will exit if and when the futex has FUTEX_WAKE called on it. Sadly, this is the best we can do with integrating futexes into our event loop. There used to be a way to get a file descriptor to represent a futex, but it was removed because it was racy. Something better would be really great - especially because it would allow incorporating pprocesss locks and other shared memory concurrency mechanisms based on futexes, into a normal event loop. """ stack_value = loader.make_trampoline_stack( Trampoline(loader.futex_helper_func, [ int(futex_pointer.near + ffi.offsetof('struct futex_node', 'futex')), futex_pointer.value.futex ])) stack_buf = await monitor.cloning_task.malloc(Stack, 4096) stack = await stack_buf.write_to_end(stack_value, alignment=16) futex_pid = await monitor.clone(CLONE.VM | CLONE.FILES, stack) # wait for futex helper to SIGSTOP itself, # which indicates the trampoline is done and we can deallocate the stack. state = await futex_pid.waitpid(W.EXITED | W.STOPPED) if state.state(W.EXITED): raise Exception( "process internal futex-waiting task died unexpectedly", state) # resume the futex_process so it can start waiting on the futex await futex_pid.kill(SIG.CONT) # TODO uh we need to actually call something to free the stack return futex_pid
"GetdentsFileDescriptor", ] class DT(enum.IntEnum): BLK = lib.DT_BLK # This is a block device. CHR = lib.DT_CHR # This is a character device. DIR = lib.DT_DIR # This is a directory. FIFO = lib.DT_FIFO # This is a named pipe (FIFO). LNK = lib.DT_LNK # This is a symbolic link. REG = lib.DT_REG # This is a regular file. SOCK = lib.DT_SOCK # This is a UNIX domain socket. UNKNOWN = lib.DT_UNKNOWN # The file type is unknown. _d_name_offset = ffi.offsetof('struct linux_dirent64', 'd_name') @dataclass class Dirent: inode: int offset: int # the offset to seek to to see the next dirent type: DT name: str def __str__(self) -> str: return f"Dirent({self.type}, {self.name})" def to_bytes(self) -> bytes: def record(reclen: int) -> bytes: record = ffi.new(