core: Restore get_max_fds optimization on Linux
* src/posix-io.c (get_max_fds): Restore Linux optimization, this time using open/getdents/close rather than opendir/readdir/closedir. -- opendir/readdir/closedir may allocate/free memory, and aren't required to do so in an async-signal-safe way. On the other hand, opening /proc/self/fd directly and iterating over it using getdents is safe. (getdents is not strictly speaking documented to be async-signal-safe because it's not in POSIX. However, the Linux implementation is essentially just a souped-up read. Python >= 3.2.3 makes the same assumption.) Signed-off-by: Colin Watson <cjwatson@debian.org>
This commit is contained in:
parent
bff9448428
commit
b5b996b1a1
@ -48,6 +48,7 @@
|
|||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
|
||||||
#if __linux__
|
#if __linux__
|
||||||
|
# include <sys/syscall.h>
|
||||||
# include <sys/types.h>
|
# include <sys/types.h>
|
||||||
# include <dirent.h>
|
# include <dirent.h>
|
||||||
#endif /*__linux__ */
|
#endif /*__linux__ */
|
||||||
@ -279,6 +280,20 @@ _gpgme_io_set_nonblocking (int fd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
/* This is not declared in public headers; getdents(2) says that we must
|
||||||
|
* define it ourselves. */
|
||||||
|
struct linux_dirent
|
||||||
|
{
|
||||||
|
unsigned long d_ino;
|
||||||
|
unsigned long d_off;
|
||||||
|
unsigned short d_reclen;
|
||||||
|
char d_name[];
|
||||||
|
};
|
||||||
|
|
||||||
|
# define DIR_BUF_SIZE 1024
|
||||||
|
#endif /* __linux__ */
|
||||||
|
|
||||||
static long int
|
static long int
|
||||||
get_max_fds (void)
|
get_max_fds (void)
|
||||||
{
|
{
|
||||||
@ -291,39 +306,57 @@ get_max_fds (void)
|
|||||||
* than for example doing 4096 close calls where almost all of them
|
* than for example doing 4096 close calls where almost all of them
|
||||||
* will fail.
|
* will fail.
|
||||||
*
|
*
|
||||||
* Unfortunately we can't call opendir between fork and exec in a
|
* We can't use the normal opendir/readdir/closedir interface between
|
||||||
* multi-threaded process because opendir uses malloc and thus a
|
* fork and exec in a multi-threaded process because opendir uses
|
||||||
* mutex which may deadlock with a malloc in another thread. Thus
|
* malloc and thus a mutex which may deadlock with a malloc in another
|
||||||
* the code is not used until we can have a opendir variant which
|
* thread. However, the underlying getdents system call is safe. */
|
||||||
* does not use malloc. */
|
#ifdef __linux__
|
||||||
/* #ifdef __linux__ */
|
{
|
||||||
/* { */
|
int dir_fd;
|
||||||
/* DIR *dir = NULL; */
|
char dir_buf[DIR_BUF_SIZE];
|
||||||
/* struct dirent *dir_entry; */
|
struct linux_dirent *dir_entry;
|
||||||
/* const char *s; */
|
int r, pos;
|
||||||
/* int x; */
|
const char *s;
|
||||||
|
int x;
|
||||||
|
|
||||||
/* dir = opendir ("/proc/self/fd"); */
|
dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
|
||||||
/* if (dir) */
|
if (dir_fd != -1)
|
||||||
/* { */
|
{
|
||||||
/* while ((dir_entry = readdir (dir))) */
|
for (;;)
|
||||||
/* { */
|
{
|
||||||
/* s = dir_entry->d_name; */
|
r = syscall(SYS_getdents, dir_fd, dir_buf, DIR_BUF_SIZE);
|
||||||
/* if ( *s < '0' || *s > '9') */
|
if (r == -1)
|
||||||
/* continue; */
|
{
|
||||||
/* x = atoi (s); */
|
/* Fall back to other methods. */
|
||||||
/* if (x > fds) */
|
fds = -1;
|
||||||
/* fds = x; */
|
break;
|
||||||
/* } */
|
}
|
||||||
/* closedir (dir); */
|
if (r == 0)
|
||||||
/* } */
|
break;
|
||||||
/* if (fds != -1) */
|
|
||||||
/* { */
|
for (pos = 0; pos < r; pos += dir_entry->d_reclen)
|
||||||
/* fds++; */
|
{
|
||||||
/* source = "/proc"; */
|
dir_entry = (struct linux_dirent *) (dir_buf + pos);
|
||||||
/* } */
|
s = dir_entry->d_name;
|
||||||
/* } */
|
if (*s < '0' || *s > '9')
|
||||||
/* #endif /\* __linux__ *\/ */
|
continue;
|
||||||
|
/* atoi is not guaranteed to be async-signal-safe. */
|
||||||
|
for (x = 0; *s >= '0' && *s <= '9'; s++)
|
||||||
|
x = x * 10 + (*s - '0');
|
||||||
|
if (!*s && x > fds && x != dir_fd)
|
||||||
|
fds = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close (dir_fd);
|
||||||
|
}
|
||||||
|
if (fds != -1)
|
||||||
|
{
|
||||||
|
fds++;
|
||||||
|
source = "/proc";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* __linux__ */
|
||||||
|
|
||||||
#ifdef RLIMIT_NOFILE
|
#ifdef RLIMIT_NOFILE
|
||||||
if (fds == -1)
|
if (fds == -1)
|
||||||
|
Loading…
Reference in New Issue
Block a user