< prev index next >

src/java.base/linux/native/libnet/linux_close.c

Print this page
rev 13764 : 8150460: (linux|bsd|aix)_close.c: file descriptor table may become large or may not work at all
Reviewed-by:

@@ -31,10 +31,11 @@
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sys/uio.h>
 #include <unistd.h>
+#include <limits.h>
 #include <errno.h>
 #include <sys/poll.h>
 
 /*
  * Stack allocated by thread when doing blocking operation

@@ -57,14 +58,24 @@
  * Signal to unblock thread
  */
 static int sigWakeup = (__SIGRTMAX - 2);
 
 /*
- * The fd table and the number of file descriptors
- */
-static fdEntry_t *fdTable;
-static int fdCount;
+ * The fd table holds one entry per file descriptor.
+ * Note: the number of possible file descriptors can get quite large;
+ * RLIMIT_NO_FILE can be large or even infinite.
+ * The fd table is organized as sparse two dimensional array, with a
+ * root array holding pointers to entry arrays, which in turn hold
+ * the entries. entry arrays are allocated on demand, save for the
+ * very first one, which is pre-allocated.
+ */
+fdEntry_t** fdTable;
+pthread_mutex_t fdTableLock = PTHREAD_MUTEX_INITIALIZER;
+/* Number of bits to apply to the file descriptor to get the index into
+ * the root array resp. entry array */
+unsigned fdTableRootArrayBits;
+unsigned fdTableEntryArrayBits;
 
 /*
  * Null signal handler
  */
 static void sig_wakeup(int sig) {

@@ -74,25 +85,49 @@
  * Initialization routine (executed when library is loaded)
  * Allocate fd tables and sets up signal handler.
  */
 static void __attribute((constructor)) init() {
     struct rlimit nbr_files;
+    unsigned max = 0;
+    unsigned maxbits;
     sigset_t sigset;
     struct sigaction sa;
 
-    /*
-     * Allocate table based on the maximum number of
-     * file descriptors.
-     */
+    /* Determine the maximum number of possible file descriptors. */
     getrlimit(RLIMIT_NOFILE, &nbr_files);
-    fdCount = nbr_files.rlim_max;
-    fdTable = (fdEntry_t *)calloc(fdCount, sizeof(fdEntry_t));
+    if (nbr_files.rlim_max != RLIM_INFINITY) {
+        max = nbr_files.rlim_max;
+    } else {
+        max = INT_MAX;
+    }
+
+    /* How many bits do we need to cover all possible values for
+     * file descriptors? */
+    for (maxbits = 1;
+        (unsigned) 1 << maxbits < max && maxbits < 32;
+        maxbits ++);
+
+    /* Calculate size of entry arrays: for small possible file descriptor ranges,
+     * fall back to a linear array. For larger ranges, make the entry tables 64K.
+     * This means for the largest possible range of INT_MAX (32bit ints), the root
+     * table will be 32K entries (32K * 64K = INT_MAX) */
+    fdTableEntryArrayBits = maxbits > 16 ? 16 : maxbits;
+    fdTableRootArrayBits = maxbits - fdTableEntryArrayBits;
+
+    /* Allocate root array */
+    fdTable = (fdEntry_t**)calloc(1 << fdTableRootArrayBits, sizeof(fdEntry_t*));
     if (fdTable == NULL) {
         fprintf(stderr, "library initialization failed - "
                 "unable to allocate file descriptor table - out of memory");
         abort();
     }
+    fdTable[0] = (fdEntry_t*)calloc(1 << fdTableEntryArrayBits, sizeof(fdEntry_t));
+    if (fdTable[0] == NULL) {
+        fprintf(stderr, "library initialization failed - "
+                "unable to allocate file descriptor table - out of memory");
+        abort();
+    }
 
     /*
      * Setup the signal handler
      */
     sa.sa_handler = sig_wakeup;

@@ -109,14 +144,48 @@
  * Return the fd table for this fd or NULL is fd out
  * of range.
  */
 static inline fdEntry_t *getFdEntry(int fd)
 {
-    if (fd < 0 || fd >= fdCount) {
+    int rootArrayIndex;
+    int entryArrayIndex;
+    unsigned rootArrayMask;
+    unsigned entryArrayMask;
+    fdEntry_t* entryTable = NULL;
+
+    if (fd < 0) {
         return NULL;
     }
-    return &fdTable[fd];
+
+    entryArrayMask = (1 << fdTableEntryArrayBits) - 1;
+    rootArrayMask =
+      ((1 << (fdTableRootArrayBits + fdTableEntryArrayBits)) - 1) & ~entryArrayMask;
+
+    entryArrayIndex = fd & entryArrayMask;
+    rootArrayIndex = (fd & rootArrayMask) >> fdTableEntryArrayBits;
+
+    if (rootArrayIndex == 0) {
+      /* fast path: first entry array gets preallocated. */
+      entryTable = fdTable[0];
+    } else {
+      /* Slow path: check if entry array exists, create it if needed */
+      pthread_mutex_lock(&fdTableLock);
+      if (fdTable[rootArrayIndex] == NULL) {
+        entryTable = (fdEntry_t*)calloc(1 << fdTableEntryArrayBits, sizeof(fdEntry_t));
+        if (entryTable == NULL) {
+          fprintf(stderr, "Unable to allocate file descriptor table - out of memory");
+          pthread_mutex_unlock(&fdTableLock);
+          abort();
+        }
+        fdTable[rootArrayIndex] = entryTable;
+      } else {
+        entryTable = fdTable[rootArrayIndex];
+      }
+      pthread_mutex_unlock(&fdTableLock);
+    }
+
+    return entryTable + entryArrayIndex;
 }
 
 /*
  * Start a blocking operation :-
  *    Insert thread onto thread list for the fd.
< prev index next >