[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [MiNT] tcp_select ( was: Re: inet4: errno is not set correct )



Hi Ole,

I'll take a look this week.

Thanks for the test case.

Alan.

On Sat, 2010-10-09 at 02:16 +0200, m0n0 wrote:
> Hello,
> 
> I just got reply from the cURL team. I described the bug and the 
> behavior seems to be non POSIX compliant.
> Message: http://article.gmane.org/gmane.comp.web.curl.library/29506
> 
> So if FreeMiNT wants to be POXIS compatible, it seems that this can be 
> described as an bug of the socket API (only if there is no other bug 
> lurking within my test case, of course...).
> 
> It seems like the poll is returning to early, it returns when the 
> socket is ready to be used for sending data ( from FreeMiNTs point of 
> view) - but it is still not ready for reading, the recv() call is 
> ending up with the ENOTCON error... but previous send() did not return 
> that error!
> 
> If you uncomment the usleep within the code, or pass an higher timeout 
> value to socket_ready (which is curios - why does poll runs longer when 
> you pass an higher timeout? Maybe the return is also wrong..., not just 
> the returned flags), the recv() returns the error EAGAIN, which is 
> fine. That's how it should behave,...
> 
> The following contains the Test Case, which is already reduced ;) The 
> same code is within the file attached.
> I compiled it with:
> 
> gcc -O2 ( Version 4.4.3 )
> 
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> 
> #include <errno.h>
> #include <sys/types.h>
> #include <sys/time.h>
> #include <unistd.h>
> #include <sys/socket.h>
> #include <netinet/in.h>
> #include <netdb.h>
> #include <netinet/in.h>
> #include <arpa/inet.h>
> #include <fcntl.h>
> #define _GNU_SOURCE
> #include <poll.h>
> 
> 
> 
> #define BUFSIZE 48
> 
> int so_pri;
> int so_sec;
> struct sockaddr_in addr;
> struct hostent *host;
> char * p_host = "google.com";
> char buf[BUFSIZE];
> int nbytes;
> int so_opt;
> 
> char * request = "GET / HTTP/1.1\n"
> "Accept: */*\n"
> "Accept-Encoding: gzip;q=0, deflate; q=0, *;q=0\n"
> "Host: google.com\n"
> "\r\n\r\n";
> 
> struct timeval curlx_tvnow(void)
> {
>   /*
>   ** gettimeofday() is not granted to be increased monotonically, due 
> to
>   ** clock drifting and external source time synchronization it can 
> jump
>   ** forward or backward in time.
>   */
>   struct timeval now;
>   (void)gettimeofday(&now, NULL);
>   return now;
> }
> 
> 
> /*
>  * Make sure that the first argument is the more recent time, as 
> otherwise
>  * we'll get a weird negative time-diff back...
>  *
>  * Returns: the time difference in number of milliseconds.
>  */
> long curlx_tvdiff(struct timeval newer, struct timeval older)
> {
>   return (newer.tv_sec-older.tv_sec)*1000+
>     (newer.tv_usec-older.tv_usec)/1000;
> }
> 
> /*
>  * Same as curlx_tvdiff but with full usec resolution.
>  *
>  * Returns: the time difference in seconds with subsecond resolution.
>  */
> double curlx_tvdiff_secs(struct timeval newer, struct timeval older)
> {
>   return (double)(newer.tv_sec-older.tv_sec)+
>     (double)(newer.tv_usec-older.tv_usec)/1000000.0;
> }
> 
> /* return the number of seconds in the given input timeval struct */
> long Curl_tvlong(struct timeval t1)
> {
>   return t1.tv_sec;
> }
> 
> #define  CURL_CSELECT_OUT 4
> #define  CURL_CSELECT_ERR 8
> 
> 
> #ifndef POLLRDNORM
> #define POLLRDNORM POLLIN
> #endif
> 
> #ifndef POLLWRNORM
> #define POLLWRNORM POLLOUT
> #endif
> 
> #ifndef POLLRDBAND
> #define POLLRDBAND POLLPRI
> #endif
> 
> #define SOCKERRNO (errno)
> 
> #define elapsed_ms  (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
> #define error_not_EINTR (error != EINTR)
> 
> int socket_ready(int writefd, int timeout_ms)
> {
> 	struct pollfd pfd[2];
> 	int num;
> 	struct timeval initial_tv = {0,0};
> 	int pending_ms = 0;
> 	int error;
> 	int r=-1;
> 	int ret;
> 
> 	if(timeout_ms > 0)
> 	{
> 		pending_ms = timeout_ms;
> 		initial_tv = curlx_tvnow();
>     }
> 
> 	num = 0;
> 	pfd[num].fd = writefd;
> 	pfd[num].events = POLLWRNORM|POLLOUT;
> 	pfd[num].revents = 0;
> 	num++;
> 
>   do {
>     if(timeout_ms < 0)
>       pending_ms = -1;
>     else if(!timeout_ms)
>       pending_ms = 0;
>     r = poll(pfd, num, pending_ms);
>     if(r != -1)
>       break;
>     error = SOCKERRNO;
>     if(error && error_not_EINTR)
>       break;
>     if(timeout_ms > 0) {
>       pending_ms = timeout_ms - elapsed_ms;
>       if(pending_ms <= 0)
>         break;
>     }
>   } while(r == -1);
> 
>   if(r < 0)
>     return -1;
>   if(r == 0)
>     return 0;
> 
>   ret = 0;
>   num = 0;
> 
>     if(pfd[num].revents & (POLLWRNORM|POLLOUT))
>       ret |= CURL_CSELECT_OUT;
>     if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
>       ret |= CURL_CSELECT_ERR;
> 
>   return ret;
> }
> 
> 
> int main()
> {
> 	int r;
> 
>    so_pri = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> 	if (so_pri == -1)
> 	{
> 		perror("socket() failed");
> 		return 1;
> 	}
> 
> 	so_opt = 1;
> 	if( setsockopt(so_pri, SOL_SOCKET, SO_REUSEADDR, (char *)&so_opt, 
> sizeof(so_opt)) == -1)
> 	{
> 		perror("setsockopt failed");
> 		return 2;
> 	}
> 
> 	printf("Resolving host: %s ...\n", p_host );
> 	host = gethostbyname( p_host );
> 	if (!host)
> 	{
> 		perror("gethostbyname() failed");
> 		return 3;
> 	}
> 	addr.sin_addr = *(struct in_addr*) host->h_addr;
> 	addr.sin_port = htons(80);
> 	addr.sin_family = AF_INET;
> 	printf("%s\n", inet_ntoa(addr.sin_addr) );
> 	fcntl(so_pri, F_SETFL, O_NONBLOCK );
> 
> 
> 	printf("C0nnecting...");
> 
> 	if( connect(so_pri, (struct sockaddr*) &addr, sizeof(addr)) == -1)
> 	{
> 		perror("connect()");
> 	}
> 	int c = 0;
> 	while( 1 ) {
> 		if( c > 100 )
> 			return( 16 );
> 		r = socket_ready( so_pri, 10 );
> 		if( r == -1 )
> 		{
> 			printf("Select error!");
> 			return( 5 );
> 		}
> 		else if( r == 0)
> 		{
> 			printf("Connect timeout!\n");
> 		}
> 		else if( r & CURL_CSELECT_ERR )
> 		{
> 			printf("CURL_CSELECT_ERR!\n");
> 		}
> 		else if( r & CURL_CSELECT_OUT ) {
> 			break;
> 		}
> 		c++;
> 	}
> 	printf("Connect OK!\n");
> 
> 	send(so_pri, request, strlen(request), 0);
> 
> 	//usleep(50000);
> 	nbytes = recv(so_pri, buf, sizeof(buf) - 1, 0);
>     if (nbytes == -1)
>     {
>         perror("recv() failed");
>         printf("Errno: %d", errno );
>         return 5;
>     }
> 
>     printf("Done\n");
> 
>     close(so_pri);
> 
>     return 0;
> }