Asynchronous RWDBReaders
All classes in the DB Interface Module behave exactly the same way whether used with a synchronous or an asynchronous connection—all classes except RWDBReader. If used incorrectly with an asynchronous connection, an RWDBReader may report one more than the actual number of rows in a result table. Let’s see how to identify and avoid this problem.
Reading with an asynchronous RWDBReader differs slightly from reading with a synchronous RWDBReader. Consider the following example:
 
RWDBReader syncReader = aSelector.reader( syncConn );
 
int numOfRowsRead = 0;
while ( syncReader() ){ //1
 
//Read the rows here
//This assumes this reader has 2 columns and v1 & v2
//are proper types declared somewhere
 
syncReader >> v1 >> v2;
 
cout << "VALUES read : First Column: " << v1
<< " Second Column:" << v2 << endl;
 
numOfRowsRead++;
 
}
 
cout << "Total Number of Rows=" << numOfRowsRead << endl;
The while loop above is the standard method for reading data from an RWDBReader. If the example is read with a synchronous connection, the variable numOfRowsRead after the while loop indicates the actual number of rows read. If the example is read using an asynchronous connection, however, the last row is read twice. The variable numOfRowsRead is reported as one more than the correct value because it is incremented for every iteration regardless of the reader status.
To clarify this point, let’s examine the output. Assume that the database table we are reading from has the following values:
Column 1
Column 2
1
101
2
102
For a synchronous reader, the example code produces the following output:
 
Values read : First Column: 1 Second Column: 101
Values read : First Column: 2 Second Column: 102
 
Total Number Of Rows = 2
For an asynchronous reader, the same code produces the following output:
 
Values read : First Column: 1 Second Column: 101
Values read : First Column: 2 Second Column: 102
Values read : First Column: 2 Second Column: 102
 
Total Number Of Rows = 3
The reason for this discrepancy lies in the method call RWDBReader::operator() in //1 of the above code. This method is an asynchronous method that returns immediately before evaluating whether the call results in an endOfFetch status or not.
Since the actual return value is not known, the call to RWDBReader::operator() returns a true value in order to stay in the while loop. This asynchronous call can be evaluated after the shiftout operator, RWDBReader::operator>>(), has been called, and may result in an endOfFetch. Therefore, the variables v1 and v2 hold the values from the last row read, and the variable numOfRowsRead after the while loop is reported as one more than the actual value.
This inaccurate processing by RWDBReader can be avoided by doing another check before the shiftout operators are invoked, as shown in the following code:
 
RWDBReader asyncReader = aSelector.reader( aSyncConn );
 
int numOfRowsRead = 0;
while ( asyncReader() ){
 
// Do something here until we are ready to get the new rows
 
// THIS CALL BLOCKS AND COMPLETES THE ASYNC CALL.
// This ensures that the data is used if and only if
// the call resulted in success.
if( !asyncReader.isValid() ) //1
break;
 
//Read the rows here.
//This assumes this reader has 2 columns and that
//v1 & v2 are proper types declared somewhere
asyncReader >> v1 >> v2;
 
cout << “VALUES read : First Column: “ << v1
<< “ Second Column“ << v2 << endl;
numOfRowsRead++;
 
}
 
cout << “Total Number of Rows=” << numofRowsRead << endl;
On //1, we check the return value of isValid() to determine is there is indeed data left to be shifted out. If isValid() returns false, we immediately exit the loop, since there is no data left to be read.
This corrected code produces identical output for both synchronous and asynchronous RWDBReaders. For the database table used in this example, the output is:
 
Values read : First Column: 1 Second Column: 101
Values read : First Column: 2 Second Column: 102
 
Total Number Of Rows = 2
The extra check in the corrected code ensures that the data is used if and only if the reader call results in success. Note that this technique works for both synchronous readers and asynchronous readers, although it is not necessary for synchronous readers. This technique is recommended, however, if the same code is expected to work with both synchronous and asynchronous connections.