[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;
> }