Kevin MOLCARD
Kevin MOLCARD

Reputation: 2218

printf makes setrlimit not working

Following this SO question and this answer in particular, it seems that calling setrlimit after a printf make it not working.

Here is the example code:

#include <stdio.h>
#include <sys/resource.h>

int main()
{
    struct rlimit rlp;

    FILE *fp[10000];
    int i;

    printf("Hello\n");

    rlp.rlim_cur = 10000;
    rlp.rlim_max = RLIM_INFINITY;
    setrlimit(RLIMIT_NOFILE, &rlp);

    getrlimit(RLIMIT_NOFILE, &rlp);
    printf("limit %lld %lld\n", rlp.rlim_cur, rlp.rlim_max);

    for(i=0;i<10000;i++) {
        fp[i] = fopen("a.out", "r");
        if(fp[i]==0) { printf("failed after %d\n", i); break; }
    }

}

Here is the console output:

Hello
limit 10000 9223372036854775807
failed after 4861

If I comment the first printf, here is the console output:

limit 10000 9223372036854775807
failed after 9967

Is there any reason for that?

[Edit] I am running MAc OS X 10.7.5 with Xcode 4.6.2.

Upvotes: 0

Views: 942

Answers (1)

Art
Art

Reputation: 20402

Here's a better version of the program that demonstrates more facets of the problem.

#include <stdio.h>
#include <sys/resource.h>
#include <err.h>
#include <fcntl.h>

int
main(int argc, char **argv)
{
        struct rlimit rl;
        int i;

        rl.rlim_cur = 10;
        rl.rlim_max = RLIM_INFINITY;
        if (setrlimit(RLIMIT_NOFILE, &rl))
                err(1, "setrlimit");

        printf("Hello\n");

        rl.rlim_cur = 100;
        rl.rlim_max = RLIM_INFINITY;
        if (setrlimit(RLIMIT_NOFILE, &rl))
                err(1, "setrlimit");

        if (getrlimit(RLIMIT_NOFILE, &rl))
                err(1, "getrlimit");
        printf("limit %lld\n", rl.rlim_cur);

        for(i = 0; i < 10000; i++) {
                FILE *fp;
#if 1
                if ((fp = fopen("foo", "r")) == NULL) {
                        err(1, "failed after %d", i);
                }
#else
                if (open("foo", O_RDONLY) == -1) {
                        err(1, "failed after %d", i);
                }
#endif
        }
        return 0;
}

If you run this program with the "#if 1" changed to "#if 0" it works as expected. It looks like MacOS reads limits for open files once during initialization of stdio in libc and doesn't read them again later. Your first call to printf initializes stdio in libc and that caches whatever the rlimit value was for number of open files.

Running dtruss on a trivial "hello, world" shows:

$ cat > hw.c
#include <stdio.h>
int main() { printf("hello, world\n"); return 0; }
$ cc -Wall -o hw hw.c && sudo dtruss ./hw 2>&1 | grep rlimit
getrlimit(0x1008, 0x7FFF58875AE8, 0x7FFF8BE92470)        = 0 0
$

Which shows that this is in fact what happens.

This is something that you should take up with Apple though, it smells like a bug.

Upvotes: 1

Related Questions