Skip to main content
Fix wrong calling sequence, and hence conclusions
Source Link
Ale
  • 185
  • 2
  • 10
$ ./a.out w5 s1 w5 s1 w 50000w50 | ./a.out r | ./a.out r1 check r &  ps -ef  | grep a.out
[3] 840810908
ale       840610906  8066  0 1910:3324 pts/6    00:00:00 ./a.out w5 s1 w5 s1 w 50000w50
ale       840710907  8066  0 1910:3324 pts/6    00:00:00 ./a.out r
ale       840810908  8066  0 1910:3324 pts/6    00:00:00 ./a.out r1 check r
ale       841010910  8066  0 1910:3324 pts/6    00:00:00 grep a.out
$ 2025-03-03T1904T10:3324:5056.616080+01628434+01:00 pcale dummy pipe[8406]pipe[10906]: exiting (read 0 bytes)
2025-03-03T1904T10:3324:5056.616455+01628925+01:00 pcale dummy pipe[8407]pipe[10907]: exiting (read 2769 bytes)
2025-03-03T1904T10:3324:5056.616691+01629188+01:00 pcale dummy pipe[8408]pipe[10908]: kill 840610906: No such process
2025-03-03T1904T10:3324:5056.616843+01629377+01:00 pcale dummy pipe[8408]pipe[10908]: exiting (read 2769 bytes)

That is, by the time the third instance starts reading, the first one has already finished, despite the time it spent sleeping and the huge amount it writes. This means that at some point all of those 502,000769 bytes rows are stored together.

Increasing the amount of data, as Scott noted, concurrency enters.

$ ./a.out w5 s1 w5 s1 w150 | ./a.out r | ./a.out r1 check r &  ps -ef  | grep a.out
[3] 11254
ale      11252  8066  0 10:31 pts/6    00:00:00 ./a.out w5 s1 w5 s1 w150
ale      11253  8066  0 10:31 pts/6    00:00:00 ./a.out r
ale      11254  8066  0 10:31 pts/6    00:00:00 ./a.out r1 check r
ale      11256  8066  0 10:31 pts/6    00:00:00 grep a.out
$ 2025-03-04T10:31:13.002766+01:00 pcale dummy pipe[11252]: exiting (read 0 bytes)
2025-03-04T10:31:13.003068+01:00 pcale dummy pipe[11254]: kill 11252: still running
2025-03-04T10:31:13.003235+01:00 pcale dummy pipe[11253]: exiting (read 7569 bytes)
2025-03-04T10:31:13.003502+01:00 pcale dummy pipe[11254]: exiting (read 7569 bytes)

I tried to determine by dichotomic search the value that triggers the buffering strategy, but couldn't. It changes from time to time.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <limits.h>

static void do_open(void)
{
    openlog("dummy pipe", LOG_PID, LOG_USER);
}

int main(int argc, char *argv[])
{
    do_open();
    pid_t me = 1;
    int wrote = 0, n;
    long total_read = 0;
    for (int i = 1; i < argc; ++i)
    {
        if (argv[i][0] == 'w' && (n = atoi(&argv[i][1])) > 0)
        {
            if (wrote == 0)
            {
                me = getpid();
                setsid();
                wrote = 1;
            }
            if (n --> 0)
                printf("Pid: %d\n", me);
            while (n --> 0)
                puts("Lorem ipsum lorem ipsum lorem ipsum lorem ipsum");
        }
        else if (argv[i][0] == 's' && (n = atoi(&argv[i][1])) > 0)
            sleep(n);
        else if (argv[i][0] == 'r')
        {
            if (argv[i][1] == 0)
                n = INT_MAX;
            else
                n = atoi(&argv[i][1]);

            char buf[1024];
            while (n --> 0)
            {
                char *p = fgets(buf, sizeof buf, stdin);
                if (p == NULL)
                    break;
                if (buf[0] == 'P')
                    sscanf(buf, "Pid: %d\n", &me);
                total_read += strlen(buf);
                if (!isatty(fileno(stdout)))
                    printf("%s", buf);
            }
        }
        else if (strcmp(argv[i], "check") == 0)
        {
            if (me != 1)
            {
                n = kill(me, 0);
                syslog(LOG_NOTICE, "kill %d: %s\n", me,
                    n? strerror(errno): "still running");
                closelog();
                do_open();
            }
        }
    }
    syslog(LOG_NOTICE, "exiting\n""exiting (read %'ld bytes)\n", total_read);
    closelog();
    return 0;
}
$ ./a.out w5 s1 w5 s1 w 50000 | ./a.out r | ./a.out r1 check r &  ps -ef  | grep a.out
[3] 8408
ale       8406  8066  0 19:33 pts/6    00:00:00 ./a.out w5 s1 w5 s1 w 50000
ale       8407  8066  0 19:33 pts/6    00:00:00 ./a.out r
ale       8408  8066  0 19:33 pts/6    00:00:00 ./a.out r1 check r
ale       8410  8066  0 19:33 pts/6    00:00:00 grep a.out
$ 2025-03-03T19:33:50.616080+01:00 pcale dummy pipe[8406]: exiting
2025-03-03T19:33:50.616455+01:00 pcale dummy pipe[8407]: exiting
2025-03-03T19:33:50.616691+01:00 pcale dummy pipe[8408]: kill 8406: No such process
2025-03-03T19:33:50.616843+01:00 pcale dummy pipe[8408]: exiting

That is, by the time the third instance starts reading, the first one has already finished, despite the time it spent sleeping and the huge amount it writes. This means that at some point all of those 50,000 rows are stored together.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <limits.h>

static void do_open(void)
{
    openlog("dummy pipe", LOG_PID, LOG_USER);
}

int main(int argc, char *argv[])
{
    do_open();
    pid_t me = 1;
    int wrote = 0, n;
    for (int i = 1; i < argc; ++i)
    {
        if (argv[i][0] == 'w' && (n = atoi(&argv[i][1])) > 0)
        {
            if (wrote == 0)
            {
                me = getpid();
                setsid();
                wrote = 1;
            }
            if (n --> 0)
                printf("Pid: %d\n", me);
            while (n --> 0)
                puts("Lorem ipsum lorem ipsum lorem ipsum lorem ipsum");
        }
        else if (argv[i][0] == 's' && (n = atoi(&argv[i][1])) > 0)
            sleep(n);
        else if (argv[i][0] == 'r')
        {
            if (argv[i][1] == 0)
                n = INT_MAX;
            else
                n = atoi(&argv[i][1]);

            char buf[1024];
            while (n --> 0)
            {
                char *p = fgets(buf, sizeof buf, stdin);
                if (p == NULL)
                    break;
                if (buf[0] == 'P')
                    sscanf(buf, "Pid: %d\n", &me);
                if (!isatty(fileno(stdout)))
                    printf("%s", buf);
            }
        }
        else if (strcmp(argv[i], "check") == 0)
        {
            if (me != 1)
            {
                n = kill(me, 0);
                syslog(LOG_NOTICE, "kill %d: %s\n", me,
                    n? strerror(errno): "still running");
                closelog();
                do_open();
            }
        }
    }
    syslog(LOG_NOTICE, "exiting\n");
    closelog();
    return 0;
}
$ ./a.out w5 s1 w5 s1 w50 | ./a.out r | ./a.out r1 check r &  ps -ef  | grep a.out
[3] 10908
ale      10906  8066  0 10:24 pts/6    00:00:00 ./a.out w5 s1 w5 s1 w50
ale      10907  8066  0 10:24 pts/6    00:00:00 ./a.out r
ale      10908  8066  0 10:24 pts/6    00:00:00 ./a.out r1 check r
ale      10910  8066  0 10:24 pts/6    00:00:00 grep a.out
$ 2025-03-04T10:24:56.628434+01:00 pcale dummy pipe[10906]: exiting (read 0 bytes)
2025-03-04T10:24:56.628925+01:00 pcale dummy pipe[10907]: exiting (read 2769 bytes)
2025-03-04T10:24:56.629188+01:00 pcale dummy pipe[10908]: kill 10906: No such process
2025-03-04T10:24:56.629377+01:00 pcale dummy pipe[10908]: exiting (read 2769 bytes)

That is, by the time the third instance starts reading, the first one has already finished, despite the time it spent sleeping. This means that at some point all of those 2,769 bytes rows are stored together.

Increasing the amount of data, as Scott noted, concurrency enters.

$ ./a.out w5 s1 w5 s1 w150 | ./a.out r | ./a.out r1 check r &  ps -ef  | grep a.out
[3] 11254
ale      11252  8066  0 10:31 pts/6    00:00:00 ./a.out w5 s1 w5 s1 w150
ale      11253  8066  0 10:31 pts/6    00:00:00 ./a.out r
ale      11254  8066  0 10:31 pts/6    00:00:00 ./a.out r1 check r
ale      11256  8066  0 10:31 pts/6    00:00:00 grep a.out
$ 2025-03-04T10:31:13.002766+01:00 pcale dummy pipe[11252]: exiting (read 0 bytes)
2025-03-04T10:31:13.003068+01:00 pcale dummy pipe[11254]: kill 11252: still running
2025-03-04T10:31:13.003235+01:00 pcale dummy pipe[11253]: exiting (read 7569 bytes)
2025-03-04T10:31:13.003502+01:00 pcale dummy pipe[11254]: exiting (read 7569 bytes)

I tried to determine by dichotomic search the value that triggers the buffering strategy, but couldn't. It changes from time to time.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <limits.h>

static void do_open(void)
{
    openlog("dummy pipe", LOG_PID, LOG_USER);
}

int main(int argc, char *argv[])
{
    do_open();
    pid_t me = 1;
    int wrote = 0, n;
    long total_read = 0;
    for (int i = 1; i < argc; ++i)
    {
        if (argv[i][0] == 'w' && (n = atoi(&argv[i][1])) > 0)
        {
            if (wrote == 0)
            {
                me = getpid();
                setsid();
                wrote = 1;
            }
            if (n --> 0)
                printf("Pid: %d\n", me);
            while (n --> 0)
                puts("Lorem ipsum lorem ipsum lorem ipsum lorem ipsum");
        }
        else if (argv[i][0] == 's' && (n = atoi(&argv[i][1])) > 0)
            sleep(n);
        else if (argv[i][0] == 'r')
        {
            if (argv[i][1] == 0)
                n = INT_MAX;
            else
                n = atoi(&argv[i][1]);

            char buf[1024];
            while (n --> 0)
            {
                char *p = fgets(buf, sizeof buf, stdin);
                if (p == NULL)
                    break;
                if (buf[0] == 'P')
                    sscanf(buf, "Pid: %d\n", &me);
                total_read += strlen(buf);
                if (!isatty(fileno(stdout)))
                    printf("%s", buf);
            }
        }
        else if (strcmp(argv[i], "check") == 0)
        {
            if (me != 1)
            {
                n = kill(me, 0);
                syslog(LOG_NOTICE, "kill %d: %s\n", me,
                    n? strerror(errno): "still running");
                closelog();
                do_open();
            }
        }
    }
    syslog(LOG_NOTICE, "exiting (read %'ld bytes)\n", total_read);
    closelog();
    return 0;
}
Source Link
Ale
  • 185
  • 2
  • 10

Albeit all programs seem to start concurrently, they don't seem to execute in a real concurrent fashion. I wrote a dummy program (below) that can write 1 or more lines, sleep a number of seconds, read 1 or more lines, or check if the head of the pipe is alive, and I get the following:

$ ./a.out w5 s1 w5 s1 w 50000 | ./a.out r | ./a.out r1 check r &  ps -ef  | grep a.out
[3] 8408
ale       8406  8066  0 19:33 pts/6    00:00:00 ./a.out w5 s1 w5 s1 w 50000
ale       8407  8066  0 19:33 pts/6    00:00:00 ./a.out r
ale       8408  8066  0 19:33 pts/6    00:00:00 ./a.out r1 check r
ale       8410  8066  0 19:33 pts/6    00:00:00 grep a.out
$ 2025-03-03T19:33:50.616080+01:00 pcale dummy pipe[8406]: exiting
2025-03-03T19:33:50.616455+01:00 pcale dummy pipe[8407]: exiting
2025-03-03T19:33:50.616691+01:00 pcale dummy pipe[8408]: kill 8406: No such process
2025-03-03T19:33:50.616843+01:00 pcale dummy pipe[8408]: exiting

That is, by the time the third instance starts reading, the first one has already finished, despite the time it spent sleeping and the huge amount it writes. This means that at some point all of those 50,000 rows are stored together.

The output is logged, and tail -f syslog runs in the background. closelog() is used to flush syslog, and the output appears all of a sudden.

Here's the dummy program, if you wanna play with it:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <limits.h>

static void do_open(void)
{
    openlog("dummy pipe", LOG_PID, LOG_USER);
}

int main(int argc, char *argv[])
{
    do_open();
    pid_t me = 1;
    int wrote = 0, n;
    for (int i = 1; i < argc; ++i)
    {
        if (argv[i][0] == 'w' && (n = atoi(&argv[i][1])) > 0)
        {
            if (wrote == 0)
            {
                me = getpid();
                setsid();
                wrote = 1;
            }
            if (n --> 0)
                printf("Pid: %d\n", me);
            while (n --> 0)
                puts("Lorem ipsum lorem ipsum lorem ipsum lorem ipsum");
        }
        else if (argv[i][0] == 's' && (n = atoi(&argv[i][1])) > 0)
            sleep(n);
        else if (argv[i][0] == 'r')
        {
            if (argv[i][1] == 0)
                n = INT_MAX;
            else
                n = atoi(&argv[i][1]);

            char buf[1024];
            while (n --> 0)
            {
                char *p = fgets(buf, sizeof buf, stdin);
                if (p == NULL)
                    break;
                if (buf[0] == 'P')
                    sscanf(buf, "Pid: %d\n", &me);
                if (!isatty(fileno(stdout)))
                    printf("%s", buf);
            }
        }
        else if (strcmp(argv[i], "check") == 0)
        {
            if (me != 1)
            {
                n = kill(me, 0);
                syslog(LOG_NOTICE, "kill %d: %s\n", me,
                    n? strerror(errno): "still running");
                closelog();
                do_open();
            }
        }
    }
    syslog(LOG_NOTICE, "exiting\n");
    closelog();
    return 0;
}