|
Home About Us A-Z Index Search * Contact Us Register Login Press ShopThe Open Brand -- Problem Reporting and Interpretations System |
Problem Report 1090 Details
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 EndReview 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:
- View Report 1090
- List All PRs
- Search Reports
- Email the System Administrator
- View the The Open Brand Interpretations Database User Manual
Contact the Certification Authority