Bug Fix in modesReadFromClient()

The modesReadFromClient() funtion is called from modesReadFromClients(),
which in turn is called from backgroundTasks(). backgroundTasks() is
called from within the main processing loop.

However, modesReadFromClient() can and does block. It attempts to read
characters from the input stream, and loops whilst there was no error.
This stalls the main RTL processing loop until an error occurs. In order
to support simultaneous local reception (via our RTL dongle) and remote
forwarding (data received from the interweb) we cannot allow this
internet read to stall.

To fix this, in modesReadFromClient() attempt to read a buffer of data
(currently 0x400 bytes). If we get a full buffer of bytes, then process
them, and attempt to read another full buffer. Keep doing thios untill
we read only a partial buffer (less than 0x400 bytes). Process the
partial buffer bytes and return.

This allows us to occasionally process data that is arriving from the
internet (which is buffered anyway in the TCP stack), without blocking
local RTL dongle decoding.
This commit is contained in:
Malcolm Robb 2013-09-20 16:48:15 +01:00
parent 52ac50b018
commit 7ea2e8fdef
3 changed files with 59 additions and 16 deletions

View file

@ -181,8 +181,8 @@
struct client { struct client {
int fd; // File descriptor int fd; // File descriptor
int service; // TCP port the client is connected to int service; // TCP port the client is connected to
char buf[MODES_CLIENT_BUF_SIZE+1]; // Read buffer
int buflen; // Amount of data on buffer int buflen; // Amount of data on buffer
char buf[MODES_CLIENT_BUF_SIZE+1]; // Read buffer
}; };
// Structure used to describe an aircraft in iteractive mode // Structure used to describe an aircraft in iteractive mode
@ -405,6 +405,8 @@ void modesInitErrorInfo ();
struct aircraft* interactiveReceiveData(struct modesMessage *mm); struct aircraft* interactiveReceiveData(struct modesMessage *mm);
void interactiveShowData(void); void interactiveShowData(void);
void interactiveRemoveStaleAircrafts(void); void interactiveRemoveStaleAircrafts(void);
int decodeBinMessage (struct client *c, char *p);
// //
// Functions exported from net_io.c // Functions exported from net_io.c
// //
@ -412,6 +414,7 @@ void modesInitNet (void);
void modesReadFromClients (void); void modesReadFromClients (void);
void modesSendAllClients (int service, void *msg, int len); void modesSendAllClients (int service, void *msg, int len);
void modesQueueOutput (struct modesMessage *mm); void modesQueueOutput (struct modesMessage *mm);
void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *));
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -41,6 +41,40 @@ static uint64_t mstime(void) {
mst += tv.tv_usec/1000; mst += tv.tv_usec/1000;
return mst; return mst;
} }
#ifdef _WIN32
// Standard error macro for reporting API errors
#define PERR(bSuccess, api){if(!(bSuccess)) printf("%s:Error %d from %s on line %d\n", __FILE__, GetLastError(), api, __LINE__);}
void cls( HANDLE hConsole ) {
COORD coordScreen = { 0, 0 }; // here's where we'll home the cursor
BOOL bSuccess;
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi; // to get buffer info
DWORD dwConSize; // number of character cells in the current buffer
// get the number of character cells in the current buffer
bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
PERR( bSuccess, "GetConsoleScreenBufferInfo" );
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
// fill the entire screen with blanks
bSuccess = FillConsoleOutputCharacter( hConsole, (TCHAR) ' ', dwConSize, coordScreen, &cCharsWritten );
PERR( bSuccess, "FillConsoleOutputCharacter" );
// get the current text attribute
bSuccess = GetConsoleScreenBufferInfo( hConsole, &csbi );
PERR( bSuccess, "ConsoleScreenBufferInfo" );
// now set the buffer's attributes accordingly
bSuccess = FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten );
PERR( bSuccess, "FillConsoleOutputAttribute" );
// put the cursor at (0, 0)
bSuccess = SetConsoleCursorPosition( hConsole, coordScreen );
PERR( bSuccess, "SetConsoleCursorPosition" );
}
#endif
// //
//========================= Interactive mode =============================== //========================= Interactive mode ===============================
// //
@ -342,7 +376,7 @@ void interactiveShowData(void) {
#ifndef _WIN32 #ifndef _WIN32
printf("\x1b[H\x1b[2J"); // Clear the screen printf("\x1b[H\x1b[2J"); // Clear the screen
#else #else
system("cls"); cls(GetStdHandle(STD_OUTPUT_HANDLE));
#endif #endif
if (Modes.interactive_rtl1090 == 0) { if (Modes.interactive_rtl1090 == 0) {
@ -439,7 +473,7 @@ void interactiveShowData(void) {
//========================================================================= //=========================================================================
// //
// When in interactive mode If we don't receive new nessages within // When in interactive mode If we don't receive new nessages within
// MODES_INTERACTIVE__DELETE_TTL seconds we remove the aircraft from the list. // MODES_INTERACTIVE_DELETE_TTL seconds we remove the aircraft from the list.
// //
void interactiveRemoveStaleAircrafts(void) { void interactiveRemoveStaleAircrafts(void) {
struct aircraft *a = Modes.aircrafts; struct aircraft *a = Modes.aircrafts;

View file

@ -759,24 +759,29 @@ void modesReadFromClient(struct client *c, char *sep,
int left; int left;
int nread; int nread;
int fullmsg; int fullmsg;
int bContinue = 1;
char *s, *e; char *s, *e;
while(1) { while(bContinue) {
fullmsg = 0; fullmsg = 0;
left = MODES_CLIENT_BUF_SIZE - c->buflen; left = MODES_CLIENT_BUF_SIZE - c->buflen;
// If our buffer is full discard it, this is some badly formatted shit // If our buffer is full discard it, this is some badly formatted shit
if (left == 0) { if (left <= 0) {
c->buflen = 0; c->buflen = 0;
left = MODES_CLIENT_BUF_SIZE; left = MODES_CLIENT_BUF_SIZE;
// If there is garbage, read more to discard it ASAP // If there is garbage, read more to discard it ASAP
} }
nread = read(c->fd, c->buf+c->buflen, left); nread = read(c->fd, c->buf+c->buflen, left);
if (nread <= 0) { // If we didn't get all the data we asked for, then return once we've processed what we did get.
if (nread == 0 || errno != EAGAIN) { // Error, or end of file if (nread != left) {
bContinue = 0;
}
if ( (nread < 0) && (errno != EAGAIN)) { // Error, or end of file
modesFreeClient(c->fd); modesFreeClient(c->fd);
} }
if (nread <= 0) {
break; // Serve next client break; // Serve next client
} }
c->buflen += nread; c->buflen += nread;
@ -787,12 +792,12 @@ void modesReadFromClient(struct client *c, char *sep,
e = s = c->buf; // Start with the start of buffer, first message e = s = c->buf; // Start with the start of buffer, first message
if (c->service == Modes.bis) { if (c->service == Modes.bis) {
// This is the Bease Binary scanning case. // This is the Beast Binary scanning case.
// If there is a complete message still in the buffer, there must be the separator 'sep' // If there is a complete message still in the buffer, there must be the separator 'sep'
// in the buffer, note that we full-scan the buffer at every read for simplicity. // in the buffer, note that we full-scan the buffer at every read for simplicity.
left = c->buflen; // Length of valid search for memchr() left = c->buflen; // Length of valid search for memchr()
while (left && ((s = memchr(e, (char) 0x1a, left)) != NULL)) { // In reality the first byte of buffer 'should' be 0x1a while (left && ((s = memchr(e, (char) 0x1a, left)) != NULL)) { // The first byte of buffer 'should' be 0x1a
s++; // skip the 0x1a s++; // skip the 0x1a
if (*s == '1') { if (*s == '1') {
e = s + MODEAC_MSG_BYTES + 8; // point past remainder of message e = s + MODEAC_MSG_BYTES + 8; // point past remainder of message
@ -820,10 +825,11 @@ void modesReadFromClient(struct client *c, char *sep,
s = e; // For the buffer remainder below s = e; // For the buffer remainder below
} else { } else {
//
// This is the ASCII scanning case, AVR RAW or HTTP at present // This is the ASCII scanning case, AVR RAW or HTTP at present
// If there is a complete message still in the buffer, there must be the separator 'sep' // If there is a complete message still in the buffer, there must be the separator 'sep'
// in the buffer, note that we full-scan the buffer at every read for simplicity. // in the buffer, note that we full-scan the buffer at every read for simplicity.
//
while ((e = strstr(s, sep)) != NULL) { // end of first message if found while ((e = strstr(s, sep)) != NULL) { // end of first message if found
*e = '\0'; // The handler expects null terminated strings *e = '\0'; // The handler expects null terminated strings
if (handler(c, s)) { // Pass message to handler. if (handler(c, s)) { // Pass message to handler.
@ -836,8 +842,8 @@ void modesReadFromClient(struct client *c, char *sep,
} }
if (fullmsg) { // We processed something - so if (fullmsg) { // We processed something - so
c->buflen = &(c->buf[c->buflen]) - s; // The unprocessed buffer length c->buflen = &(c->buf[c->buflen]) - s; // Update the unprocessed buffer length
memmove(c->buf, s, c->buflen); // move what's remaining to the start of the buffer memmove(c->buf, s, c->buflen); // Move what's remaining to the start of the buffer
} else { // If no message was decoded process the next client } else { // If no message was decoded process the next client
break; break;
} }