X-Git-Url: https://ruderich.org/simon/gitweb/?p=coloredstderr%2Fcoloredstderr.git;a=blobdiff_plain;f=src%2Ftrackfds.h;h=00b3b6e3b55da47e7c66453e609e6b7754a6071d;hp=5896cbb4ee3eb223db010131253bb609d45de774;hb=f30dbbd26d18e3014762ccc37b8e5ab65b596b35;hpb=feff94de6be535f17f06e3c135366b33ab9c0ca3 diff --git a/src/trackfds.h b/src/trackfds.h index 5896cbb..00b3b6e 100644 --- a/src/trackfds.h +++ b/src/trackfds.h @@ -20,26 +20,55 @@ #ifndef TRACKFDS_H #define TRACKFDS_H 1 -/* List of tracked file descriptors. */ -static int *tracked_fds; +/* Array of tracked file descriptors. Used for fast lookups for the normally + * used file descriptors (0 <= fd < TRACKFDS_STATIC_COUNT). */ +static int tracked_fds[TRACKFDS_STATIC_COUNT]; + +/* List of tracked file descriptors >= TRACKFDS_STATIC_COUNT. */ +static int *tracked_fds_list; /* Current number of items in the list. */ -static size_t tracked_fds_count; +static size_t tracked_fds_list_count; /* Allocated items, used to reduce realloc()s. */ -static size_t tracked_fds_space; +static size_t tracked_fds_list_space; #ifdef DEBUG static void tracked_fds_debug(void) { - debug(" tracked_fds: %d/%d\t\t[%d]\n", tracked_fds_count, - tracked_fds_space, - getpid()); size_t i; - for (i = 0; i < tracked_fds_count; i++) { - debug(" tracked_fds[%d]: %d\n", i, tracked_fds[i]); + + for (i = 0; i < TRACKFDS_STATIC_COUNT; i++) { + if (tracked_fds[i]) { + debug(" tracked_fds[%d]: %d\n", i, tracked_fds[i]); + } + } + debug(" tracked_fds_list: %d/%d\t[%d]\n", tracked_fds_list_count, + tracked_fds_list_space, + getpid()); + for (i = 0; i < tracked_fds_list_count; i++) { + debug(" tracked_fds_list[%d]: %d\n", i, tracked_fds_list[i]); } } #endif +static int init_tracked_fds_list(size_t count) { + assert(count > 0); + + /* Reduce reallocs. */ + count += TRACKFDS_REALLOC_STEP; + + tracked_fds_list = malloc(count * sizeof(*tracked_fds_list)); + if (!tracked_fds_list) { +#ifdef WARNING + warning("malloc(tracked_fds_list, %d) failed [%d]\n", + count * sizeof(*tracked_fds_list), getpid()); +#endif + return 0; + } + + tracked_fds_list_space = count; + return 1; +} + /* Load tracked file descriptors from the environment. The environment is used * to pass the information to child processes. * @@ -49,10 +78,12 @@ static void init_from_environment(void) { #ifdef DEBUG debug("init_from_environment()\t\t[%d]\n", getpid()); #endif - const char *env; + char const *env; + + int saved_errno = errno; initialized = 1; - tracked_fds_count = 0; + tracked_fds_list_count = 0; /* If ENV_NAME_FORCE_WRITE is set and not empty, allow writes to a non-tty * device. Use with care! Mainly used for the test suite. */ @@ -63,8 +94,12 @@ static void init_from_environment(void) { env = getenv(ENV_NAME_FDS); if (!env) { + errno = saved_errno; return; } +#ifdef DEBUG + debug(" getenv(\"%s\"): \"%s\"\n", ENV_NAME_FDS, env); +#endif /* Environment is read-only. */ char env_copy[strlen(env) + 1]; strcpy(env_copy, env); @@ -77,17 +112,11 @@ static void init_from_environment(void) { count++; } } - tracked_fds_space = count + TRACKFDS_REALLOC_STEP; - - tracked_fds = malloc(tracked_fds_space * sizeof(*tracked_fds)); - if (!tracked_fds) { - return; - } size_t i = 0; /* Parse file descriptor numbers from environment string and store them as - * integers in tracked_fds. */ + * integers in tracked_fds and tracked_fds_list. */ char *last; for (x = env_copy, last = env_copy; *x; x++) { if (*x != ',') { @@ -104,39 +133,83 @@ static void init_from_environment(void) { } *x = 0; - tracked_fds[i++] = atoi(last); + int fd = atoi(last); + if (fd < TRACKFDS_STATIC_COUNT) { + tracked_fds[fd] = 1; + } else { + if (!tracked_fds_list) { + /* Pessimistic count estimate, but allocating a few more + * elements doesn't hurt. */ + if (!init_tracked_fds_list(count)) { + /* Couldn't allocate memory, skip this entry. */ + goto next; + } + } + tracked_fds_list[i++] = fd; +#ifdef DEBUG + debug(" large fd: %d\n", fd); +#endif + } + +next: last = x + 1; } - tracked_fds_count = count; + tracked_fds_list_count = i; #ifdef DEBUG tracked_fds_debug(); #endif + + errno = saved_errno; } +static char *update_environment_buffer_entry(char *x, int fd) { + assert(fd >= 0); + + int length = snprintf(x, 10 + 1, "%d", fd); + if (length >= 10 + 1 || length <= 0 /* shouldn't happen */) { + /* Integer too big to fit the buffer, skip it. */ +#ifdef WARNING + warning("update_environment_buffer_entry(): truncated fd: %d [%d]\n", + fd, getpid()); +#endif + return x; + } + + /* Write comma after number. */ + x += length; + *x++ = ','; + /* Make sure the string is always zero terminated. */ + *x = 0; + + return x; +} static void update_environment_buffer(char *x) { + assert(initialized); + size_t i; - for (i = 0; i < tracked_fds_count; i++) { - int length = snprintf(x, 10 + 1, "%d", tracked_fds[i]); - if (length >= 10 + 1) { - /* Integer too big to fit the buffer, skip it. */ - continue; + for (i = 0; i < TRACKFDS_STATIC_COUNT; i++) { + if (tracked_fds[i]) { + x = update_environment_buffer_entry(x, (int)i); } - - /* Write comma after number. */ - x += length; - *x++ = ','; - /* Make sure the string is always zero terminated. */ - *x = 0; + } + for (i = 0; i < tracked_fds_list_count; i++) { + x = update_environment_buffer_entry(x, tracked_fds_list[i]); } } inline static size_t update_environment_buffer_size(void) { - /* An integer (32-bit) has at most 10 digits, + 1 for the comma after each + assert(initialized); + + /* Use the maximum count (TRACKFDS_STATIC_COUNT) of used descriptors + * because it's simple and small enough not to be a problem. + * + * An integer (32-bit) has at most 10 digits, + 1 for the comma after each * number. Bigger file descriptors (which shouldn't occur in reality) are * skipped. */ - return tracked_fds_count * (10 + 1) + 1 /* to fit '\0' */; + return (TRACKFDS_STATIC_COUNT + tracked_fds_list_count) + * (10 + 1) + 1 /* to fit '\0' */; } static void update_environment(void) { #ifdef DEBUG @@ -155,7 +228,7 @@ static void update_environment(void) { update_environment_buffer(env); #if 0 - debug(" setenv('%s', '%s', 1)\n", ENV_NAME_FDS, env); + debug(" setenv(\"%s\", \"%s\", 1)\n", ENV_NAME_FDS, env); #endif setenv(ENV_NAME_FDS, env, 1 /* overwrite */); @@ -164,24 +237,41 @@ static void update_environment(void) { static void tracked_fds_add(int fd) { - if (tracked_fds_count >= tracked_fds_space) { - size_t new_space = tracked_fds_space + TRACKFDS_REALLOC_STEP; - int *tmp = realloc(tracked_fds, sizeof(*tracked_fds) * new_space); + assert(fd >= 0); + + if (fd < TRACKFDS_STATIC_COUNT) { + tracked_fds[fd] = 1; +#if 0 + debug("tracked_fds_add(): %-3d\t\t[%d]\n", fd, getpid()); + tracked_fds_debug(); +#endif + return; + } + + if (tracked_fds_list_count >= tracked_fds_list_space) { + int saved_errno = errno; + + size_t new_space = tracked_fds_list_space + TRACKFDS_REALLOC_STEP; + int *tmp = realloc(tracked_fds_list, + sizeof(*tracked_fds_list) * new_space); if (!tmp) { /* We can do nothing, just ignore the error. We made sure not to * destroy our state, so the new descriptor is ignored without any * other consequences. */ -#ifdef DEBUG - debug("realloc(tracked_fds, %zu) failed! [%d]\n", - sizeof(*tracked_fds) * new_space, getpid()); +#ifdef WARNING + warning("realloc(tracked_fds_list, %zu) failed! [%d]\n", + sizeof(*tracked_fds_list) * new_space, getpid()); #endif + errno = saved_errno; return; } - tracked_fds = tmp; - tracked_fds_space = new_space; + errno = saved_errno; + + tracked_fds_list = tmp; + tracked_fds_list_space = new_space; } - tracked_fds[tracked_fds_count++] = fd; + tracked_fds_list[tracked_fds_list_count++] = fd; #ifdef DEBUG debug("tracked_fds_add(): %-3d\t\t[%d]\n", fd, getpid()); @@ -189,15 +279,28 @@ static void tracked_fds_add(int fd) { #endif } static int tracked_fds_remove(int fd) { + assert(fd >= 0); + + if (fd < TRACKFDS_STATIC_COUNT) { + int old_value = tracked_fds[fd]; + tracked_fds[fd] = 0; + +#if 0 + debug("tracked_fds_remove(): %-3d\t[%d]\n", fd, getpid()); + tracked_fds_debug(); +#endif + return old_value; /* Found vs. not found. */ + } + size_t i; - for (i = 0; i < tracked_fds_count; i++) { - if (fd != tracked_fds[i]) { + for (i = 0; i < tracked_fds_list_count; i++) { + if (fd != tracked_fds_list[i]) { continue; } - memmove(tracked_fds + i, tracked_fds + i + 1, - sizeof(*tracked_fds) * (tracked_fds_count - i - 1)); - tracked_fds_count--; + memmove(tracked_fds_list + i, tracked_fds_list + i + 1, + sizeof(*tracked_fds_list) * (tracked_fds_list_count - i - 1)); + tracked_fds_list_count--; #ifdef DEBUG debug("tracked_fds_remove(): %-3d\t[%d]\n", fd, getpid()); @@ -211,10 +314,36 @@ static int tracked_fds_remove(int fd) { /* Not found. */ return 0; } -static int tracked_fds_find(int fd) { + +static int tracked_fds_find_slow(int fd) noinline; +/* + * tracked_fds_find() is called for each hook call and should be as fast as + * possible. As most file descriptors are < TRACKFDS_STATIC_COUNT, force the + * compiler to inline that part which is almost exclusively used. + * + * Inlining tracked_fds_add()/tracked_fds_remove() isn't worth the effort as + * they are not called often enough. + */ +inline static int tracked_fds_find(int fd) always_inline; +inline static int tracked_fds_find(int fd) { + assert(fd >= 0); + + if (fd < TRACKFDS_STATIC_COUNT) { + return tracked_fds[fd]; + } + + return tracked_fds_find_slow(fd); +} +static int tracked_fds_find_slow(int fd) { + assert(initialized); + + if (tracked_fds_list_count == 0) { + return 0; + } + size_t i; - for (i = 0; i < tracked_fds_count; i++) { - if (fd == tracked_fds[i]) { + for (i = 0; i < tracked_fds_list_count; i++) { + if (fd == tracked_fds_list[i]) { return 1; } }