import fcntl import os import resource import select import sys try: MAXEVENTS = int(sys.argv[1]) except (IndexError, ValueError): MAXEVENTS = -1 def set_nonblocking(fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK|flags) # increase RLIMIT_NOFILE try: soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE) resource.setrlimit(resource.RLIMIT_NOFILE, (hard, hard)) NUM_WRITERS = hard except OSError: NUM_WRITERS = soft # guard for already allocated FDs (stdin, stdout...) NUM_WRITERS -= 16 print("Working with {} FDs, {} maxevents".format(NUM_WRITERS, MAXEVENTS)) r, w = os.pipe() all_writers = set() seen_writers = set() with select.epoll() as ep: # register read-end set_nonblocking(r) ep.register(r, select.EPOLLIN) # register many write-ends for i in range(NUM_WRITERS): fd = os.dup(w) set_nonblocking(w) ep.register(fd, select.EPOLLOUT) all_writers.add(fd) fill = False # loop until all the writer FDs have been reported ready while all_writers - seen_writers: print("Number of missing FDs:{}".format(len(all_writers - seen_writers))) if fill: # fill the pipe every other loop, so that the next poll() call sees # all the writers non-ready/ready alternatively (necessary to reset # edge-levels) try: while True: os.write(w, b'X' * 8096) except OSError: pass fill = False else: # drain the pipe try: while True: os.read(r, 8096) except OSError: pass fill = True # find all ready writers - if `maxevent` is less than the number of # ready writers, some events won't be reported ready_writers = set(fd for fd, evt in ep.poll(-1, MAXEVENTS) if fd != r) # remember writers seen seen_writers |= ready_writers print("Number of ready FDs: {}".format(len(ready_writers)))