HomeAbout Us A-Z IndexSearch * Contact Us Register LoginPress Shop

The Open Brand -- Problem Reporting and Interpretations System


Problem Report 1090 Details

Help Show help | Quick Search | Submit a Test Suite Support Request | Click here to view your privileges

This page provides all information on Problem Report 1090.


Report 1090 Actions


    Problem Report Number 1090
    Submitter's Classification Test Suite problem
    State Resolved
    Resolution Rejected (REJ)
    Problem Resolution ID REJ.X.0305
    Raised 2002-07-24 08:00
    Updated 2003-03-13 08:00
    Published null
    Product Standard Internationalised System Calls and Libraries Extended V2 (UNIX 98)
    Certification Program The Open Brand certification program
    Test Suite VSRT version 5.1.2
    Test Identification rt.os/aio/aio_read 6
    Problem Summary PG4R.00019 This IR claims there is a race condition in the test.
    Problem Text
    The problem seems to be a race condition in the test. If a short delay is
    introduced into the test, no fails are recorded.

    The test is checking the following assertion:

    If _POSIX_ASYNCHRONOUS_IO is defined or the implementa-
    tion supports the aio_read() function as described in
    System Interfaces and Headers, Issue 5:
    On a call to aio_read() when aiocbp-
    >aio_sigevent.sigev_notify is SIGEV_SIGNAL the signal
    specified in aiocbp->aio_sigevent.sigev_signo shall
    be generated for the process when the operation com-
    pletes.

    After spawning a slew of aio_read operations and then waiting until
    aio_error() no longer returns a EINPROGRESS error for each spawned
    aio_read(), the test checks to see if the signal handler has caught the
    completion signal from the read operations.

    Here is the offending code from the test-suite.

    tset/rt.os/aio/aio_read/aio_read.c:

    1695 DBUG_PRINTF("t6", "check the results");
    1696 for (i = aio_max1-1; i >= 0; i--) {
    1697 retval2 = aio_error(&paiocbs[i]);
    1698 if (retval2 == -1) {
    1699 err = errno;
    1700 in_rpt("aio_error failed for read #%d, errno = %
    d (%s)", i, err, errname(err));
    1701 errors++;
    1702 continue;
    1703 }
    [snip]
    1716 }
    1717
    1718 free(plist);
    1719 free(paiocbs);
    1720 free(bufs);
    1721
    1722 if (got_sig == 0) {
    1723 in_rpt("Expected signal delivery, no signals were receiv
    ed");
    1724 errors++;
    1725 }
    1726 if (got_sig > aio_max1) {
    1727 in_rpt("Expected at most %d signals, received %d", aio_m
    ax, got_sig);
    1728 errors++;
    1729 }

    As of line 1695, all of the reads have finished. This loop checks each
    of the reads to see if any of them generated an error even though they
    finished. When this loop completes successfully, a simple test in line
    1722 checks to see if _any_ of the pending signals were delivered. In
    some cases, this test fails.

    (It should be noted that aio_reqprio = 0 for each of the reads, which means
    that the aio operations spawned by the process have the same priority as
    the process itself and should be processed in FIFO order.)

    If one introduces a sleep(1) in between lines 1720 and 1722, the test
    passes after 1000 iterations. The problem seems to be that signals
    are not being delivered in some cases before the test checks to see if
    the signals have been received. A small delay is warranted to make sure that
    signals have a chance to be delivered before the check is performed. Nothing
    in the standard states that the signals must have been sent and received before the aio_read operations are marked as complete. All the standard says is that
    the signals must have been sent. It could take anywhere from several seconds to several milliseconds before the signals are delivered to the process that spawned the aio operations.

    It might be prudent to tie the amount of time the process sleeps waiting
    for signals to be delivered to the SPEEDFACTOR for the machine in question.
    Something like sleep(SPEEDFACTOR), for instance.

    Test Output
    10|1254 /tset/rt.os/aio/aio_read/T.aio_read 23:50:09|TC Start, scenario ref 1274
    -0
    15|1254 3.3-lite 15|TCM Start
    400|1254 6 1 23:50:33|IC Start
    200|1254 6 23:50:33|TP Start
    520|1254 6 00020402 1 1|INFO: No limit on AIO_MAX
    520|1254 6 00020402 1 2|> test with file
    520|1254 6 00020402 2 1|> test with pipe
    520|1254 6 00020419 2 1|Expected signal delivery, no signals were received
    520|1254 6 00020402 3 1|> test with pty
    520|1254 6 00020402 4 1|> test with tty
    520|1254 6 00020402 4 2|tty testing not configured
    220|1254 6 1 23:50:33|FAIL
    410|1254 6 1 23:50:33|IC End

    Review Information

    Review Type TSMA Review
    Start Date null
    Completed null
    Status Complete
    Review Recommendation No Resolution Given
    Review Response
    It is recommended that this request is refused.

    Analysis of the code shows that there are very many opportunities for
    the implementation to deliver a signal between the time that the first
    call to aio_error() reports a completed operation, and the time that
    the test checks to see if a signal has been received.

    Below is the code for TP6 with comments added using ## at the start of
    the line.

    private int dotest6(readfd, writefd)
    int readfd;
    int writefd;
    {
    int i, j;
    char tbuf[32];
    int pathok = 0;
    struct aiocb *paiocbs;
    struct aiocb **plist;
    int errors;
    char *bufs;
    int err;
    ssize_t retval;
    int origflags;
    int aio_max1;
    int retval2;

    DBUG_ENTER("dotest6");

    got_sig = 0;
    errors = 0;

    ## Here the test installs a signal handler for SIGUSR1.
    ## This handler just increments the global variable got_sig, then returns.

    if (vsrt_signal(SIGUSR1, handler6) == -1)
    DBUG_RETURN(-1);

    DBUG_PRINTF("t6", "write some data");
    if ((origflags = fcntl(writefd, F_GETFL)) == -1) {
    vsrt_sysfail("fcntl");
    DBUG_RETURN(-1);
    }
    if (fcntl(writefd, F_SETFL, origflags|O_NONBLOCK) == -1) {
    vsrt_sysfail("fcntl #2");
    DBUG_RETURN(-1);
    }
    for (i = 0; i < aio_max; i++) {
    memset(tbuf, 0, sizeof(tbuf));
    sprintf(tbuf, "message %3d\n", i);
    if (write(writefd, tbuf, strlen(tbuf)) == -1) {
    if (errno == EAGAIN)
    break;
    else {
    vsrt_sysfail("write");
    DBUG_RETURN(-1);
    }
    }
    }

    aio_max1 = i;

    PATH_TRACE;

    if ((paiocbs = malloc(aio_max1*sizeof(struct aiocb))) == NULL) {
    vsrt_sysfail("malloc aiocbs");
    DBUG_RETURN(-1);
    }

    if ((plist = malloc(aio_max1*sizeof(struct aiocb *))) == NULL) {
    vsrt_sysfail("malloc plist");
    free(paiocbs);
    DBUG_RETURN(-1);
    }
    if ((bufs = malloc((aio_max1*sizeof(tbuf)+1))) == NULL) {
    vsrt_sysfail("malloc bufs");
    free(plist);
    free(paiocbs);
    DBUG_RETURN(-1);
    }

    PATH_TRACE;

    ## Here the test starts off some AIO reads.

    DBUG_PRINTF("t6", "kick off some aio reads");
    for (i = 0; i < aio_max1; i++) {
    paiocbs[i].aio_fildes = readfd;
    paiocbs[i].aio_buf = &bufs[i*strlen(tbuf)];
    paiocbs[i].aio_offset = i*strlen(tbuf);
    paiocbs[i].aio_nbytes = strlen(tbuf);
    paiocbs[i].aio_sigevent.sigev_notify = SIGEV_SIGNAL;
    paiocbs[i].aio_sigevent.sigev_signo = SIGUSR1;
    paiocbs[i].aio_sigevent.sigev_value.sival_int = i;
    paiocbs[i].aio_reqprio = 0;
    plist[i] = &paiocbs[i];
    if (tb_aio_read(&paiocbs[i]) == -1) {
    err = errno;
    in_rpt("aio_read failed for read #%d, errno = %d (%s)", i, err, errname(err));
    errors++;
    }
    }

    ## Here the test enters a polling loop that waits for the reads to complete.
    ## The polling loop checks the status of each read in turn.
    ## The first read that is found to be unfinished causes the TP to sleep
    ## for 2 seconds, then the polling loop is restarted.
    ## When no error occurs, the total timeout is somewhere between
    ## (VSRT_AIO_ITERATIONS * 2) and ((VSRT_AIO_ITERATIONS * 2) * aio_max1),
    ## depending on the order in which the reads complete.

    PATH_TRACE;
    DBUG_PRINTF("t6", "wait for them to complete");
    for (j = 0; j < VSRT_AIO_ITERATIONS; j++) {
    for (i = 0; i < aio_max1; i++) {
    if (aio_error(&paiocbs[i]) == EINPROGRESS) {
    sleep(2);
    break;
    }
    }
    if (i == aio_max1)
    break;
    }

    ## At this point, previous calls to aio_error() have shown that all the
    ## AIO operations are complete, or that VSRT_AIO_ITERATIONS of the polling
    ## loop have been done.
    ## Next, the test checks to see if there are still any reads
    ## outstanding (i.e., if the timeout described above has expired).

    if (j == VSRT_AIO_ITERATIONS) {
    for (i = 0; i < aio_max1; i++) {
    if (aio_error(&paiocbs[i]) == EINPROGRESS)
    in_rpt("request %d is still in progress", i);
    }
    (void)aio_cancel(readfd, NULL);
    free(plist);
    free(paiocbs);
    free(bufs);
    DBUG_RETURN(-1);
    }

    ## Here there are further calls to aio_error(), and also to aio_return(),
    ## to see if any of the (now completed) reads failed.

    PATH_TRACE;
    DBUG_PRINTF("t6", "check the results");
    for (i = aio_max1-1; i >= 0; i--) {
    retval2 = aio_error(&paiocbs[i]);
    if (retval2 == -1) {
    err = errno;
    in_rpt("aio_error failed for read #%d, errno = %d (%s)", i, err, errname(err));
    errors++;
    continue;
    }
    if (retval2 != 0) {
    in_rpt("Error status for read %d is %d (%s)", i, retval2, errname(retval2));
    errors++;
    continue;
    }
    retval = aio_return(&paiocbs[i]);
    if (retval == -1) {
    err = errno;
    in_rpt("aio_return failed for read #%d, errno = %d (%s)", i, err, errname(err));
    errors++;
    continue;
    }
    }

    free(plist);
    free(paiocbs);
    free(bufs);

    ## It is only at this point that the TP checks to see if a signal has
    ## arrived.
    ## This is many system calls after the first call to aio_error()
    ## indicated that an operation had completed, so there should have been
    ## ample opportunity for the signal to be delivered.

    if (got_sig == 0) {
    in_rpt("Expected signal delivery, no signals were received");
    errors++;
    }
    if (got_sig > aio_max1) {
    in_rpt("Expected at most %d signals, received %d", aio_max, got_sig);
    errors++;
    }

    if (errors == 0)
    DBUG_RETURN(0);
    else
    DBUG_RETURN(-1);
    }

    As noted previously, the TP makes many system calls between the first
    aio_error() call in the polling loop that doesn't return EINPROGRESS,
    and the check of got_sig that determines whether or not a signal has
    been received.
    Normally, a signal that is deliverable when a system call returns is
    delivered at the point of the return.
    If the same rule applies to the delivery of an AIO signal as applies
    to the delivery of a "normal" signal it would be reasonable to assume
    that there is no pending signal at the time that got_sig is checked
    (unless something in the AIO implementation has blocked the signal).

    So it is not believed this is a race condition.


    Review Type SA Review
    Start Date null
    Completed null
    Status Complete
    Review Resolution Rejected (REJ)
    Review Conclusion
    This request is refused.

    Problem Reporting System Options:

     

    Back   


Contact the Certification Authority