0

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.

Community
  • 1
  • 1
Kevin MOLCARD
  • 2,168
  • 3
  • 22
  • 35
  • 1. Your code doesn't match the output. 2. I just tried it on a Red Hat 5 system and got the same numbers with and without the "Hello". I believe it will also depend on what else is going on on your system. – BoBTFish Jul 18 '13 at 09:40
  • This is the exact code I am using. I am on Mac OS 10.7 with Xcode 4.6.2, maybe that's why. – Kevin MOLCARD Jul 18 '13 at 09:41
  • Really? The **exact** code you are using contains `printf("after...")` but prints `"limit ..."`? Your `printf` implementation is seriously broken!! – BoBTFish Jul 18 '13 at 09:43
  • sorry, I did not see this typo, thanks for pointing it to me. but this does not change anything to the problem. – Kevin MOLCARD Jul 18 '13 at 09:46
  • 2
    Well it does matter. You should always post output from the **exact** code you are posting. How are people supposed to guess the differences between the code you posted and the code you ran? But anyway, are these results repeatedly reproducible? I have a feeling your results depend on how many files are open in other programs on the system. I'm hoping someone who knows for sure will chime in here, just trying to push for more helpful information since no one else is saying anything. – BoBTFish Jul 18 '13 at 09:52
  • I have changed the code on my question, sorry for that. Concerning the numbers yes they are repeatedly reproducible: 4861 = 4864-3 and 4864 is the defeault value of rlim_cur before the call of setrlimit – Kevin MOLCARD Jul 18 '13 at 10:04
  • BTW: I suspect the type of `rlp.rlim_max` is `rlim_t`. On your system, is `rlim_t` a `signed long long` to match the `%lld` or something else? – chux - Reinstate Monica Jul 18 '13 at 12:46
  • Since `printf()` appears to affect results, recommend performing 3 `getrlimit(RLIMIT_NOFILE, &rlp[0,1 then 2])` at beginning of main(), after setrlimit() and after for() loop, _then_ print the 3 results. Then editing in/out your suspect `printf("Hello\n");` to see variation. I suspect an editing error. – chux - Reinstate Monica Jul 18 '13 at 13:11
  • @chux: I have tried things like that and the result of getrLimit is what I have set (10000 in my case). But it seems that Mac OS does not care anymore to that after the call a printf. – Kevin MOLCARD Jul 18 '13 at 14:34

1 Answers1

1

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.

Art
  • 19,807
  • 1
  • 34
  • 60