|
Non-blocking recv() loop causing lockup of ZTP 1 Year, 1 Month ago
|
Karma: 0
|
Gday,
We are a team using about 100 eZ80F91 modules as part of a radio telescope upgrade project. We are currently using ZTP 2.2.0.
We are trying to implement a program that performs continious operations on the IO ports whilst supporting TCP connections and requests at the same time. We only need one TCP client connection at once.
Our idea is to loop alternating between IO port operations and non-blocking socket calls, eg:
| Code: |
client_s = accept();
iocttsocket(s, FIONBIO, 0);
while (1) {
DoPortStuff();
rc = recv(s);
..send etc...
}
|
The actual code is below.
However when we run code like this the entire eZ80 module becomes unresponsive: doesn't respond to ping, telnet etc. If we use blocking sockets the code works as expected but means we aren't calling DoPortStuff() continuously.
One idea is that the hot loop calling the non-blocking receive is causing RZK/ZTP to lock up for some reason, perhaps a bug?
Any help greatly appreciated!
Cheers,
The SKAMP team.
| Code: |
ioctlsocket(socket_server,FIONBIO,0); // make TCP server socket non-blocking
socket_client = -1;
while (1) {
Poll_All_Inputs();
// check for a TCP client connection
socket_client = accept(socket_server,NULL,NULL);
if (socket_client > -1) {
connected = 1;
ioctlsocket(socket_client,FIONBIO,NULL); // make the client socket non-blocking
// Wait for the 11 byte header, also polling external inputs
bytes_received = 0; // total number of bytes received
while (connected && (bytes_received < 11)) {
Poll_All_Inputs();
rc = recv(socket_client,(char *)Rx_Data,11,0);
// TODO: what rc value is returned if there is no data available?
if (rc < 0) {
connected = 0;
} else {
bytes_received += rc;
}
}
}
|
|
|
|
|
|
|
|
Re:Non-blocking recv() loop causing lockup of ZTP 1 Year, 1 Month ago
|
Karma: 4
|
Duncan,
After discussing the issue with the engineering team, the problem may be in the way you are handling the loop.
Assuming the thread that is running the while loop is at a priority higher than SYSD (Priority level less than 28), the while loop does not exit until it receives more 10 bytes, however, unless this thread periodically yields, SYSD will never receive a TCP packet, so there will be no packets to receive, thereby never terminating the loop.
To resolve the problem, there are number of options.
One option is to redesign the handling as such:
Thread_1:
| Code: |
while( 1 )
{
Poll_All_Inputs();
RZKSuspendThread(hCurr_Thread, xxx);
}
|
Where xxx is the number of 10ms intervals between checking the GPIO port for activity. If you require that there is less than 10ms latency between the GPIO becoming active and having Poll_All_Inputs detecting it, then you should consider using one or more GPIO interrupt handlers.
Thread_2:
| Code: |
socket_client = accept(socket_server,NULL,NULL);
if( socket_client > -1 ) {
rc = recv(socket_client,(char *)Rx_Data,11,0);
}
|
To keep your current loop implementation, you must either
A – Reduces the priority of the thread to below that of SYSD (such as use a priority of 30)
B – Add a sleep function to each iteration of the loop, such as:
| Code: |
RZKSuspendThread(hCurr_Thread, 10); // wait 100ms
|
Let me know how it goes.
|
|
|
|
|
|
|
Re:Non-blocking recv() loop causing lockup of ZTP 1 Year ago
|
Karma: 0
|
Hi Tom,
Thanks for your reply.
I tried adding a call to RZKSuspendThread(RZKGetCurrentThread(),1); in the loop and it worked: the OS became responsive and data could be received on the socket.
I found an issue with ZTP 2.2.0: the non-blocking recv() returned 0 when there was no data available and also when the socket disconnected, making it impossible to distinguish between the two. ZTP 2.4.0 did not have this problem (recv() returns < 0 on disconnect) so I started using that instead.
However I discovered that accept() always blocks even when set as non-blocking. This is true in both ZTP 2.2.0 and 2.4.0. It doesn't matter where the ioctlsocket() call is placed, ie after socket(), after bind(), after listen(): accept() always blocks. This makes our plan of interleaving TCP sockets and GPIO work in a single thread impossible. If accept() always blocks we'll have to use a multi-threaded approach.
Any comments?
Here is the test code:
| Code: |
void tcp_echo() {
INT16 listen_socket, client_socket;
INT16 len;
INT8 buf[500];
struct sockaddr_in addr;
listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket < 0) {
return;
}
// bind and listen
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(5050);
if (bind(listen_socket, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
return;
}
if (listen(listen_socket, 5) < 0) {
return;
}
if (ioctlsocket(listen_socket, FIONBIO, NULL) != 0) {
return;
}
while (1) {
ledmatrix_putc('*');
client_socket = accept(listen_socket, NULL, NULL);
ledmatrix_putc('A');
if (client_socket > 0) {
ledmatrix_fill();
// make non-blocking
if (ioctlsocket(client_socket, FIONBIO, NULL) != 0) {
return; //failed
}
while (client_socket > 0) {
// receive some data and echo it back
len = recv(client_socket, buf, sizeof(buf), 0);
if (len > 0) {
ledmatrix_putc((char)buf[0]);
send(client_socket, buf, len, 0);
} else if (len < 0) {
close_s(client_socket);
client_socket = -1;
} else if (len == 0) {
// no data was received, get off the CPU so the OS can recv data
RZKSuspendThread(RZKGetCurrentThread(),1);
}
}
}
RZKSuspendThread(RZKGetCurrentThread(),100);
}
}
|
|
|
|
|
|
|
|
Re:Non-blocking recv() loop causing lockup of ZTP 1 Year ago
|
Karma: 4
|
|
Duncan,
Yes, the accept() does always block.
|
|
|
|
|
|
|