0

My operating system is Centos 6.4.

For select the linux programmer's manual says: "select may update the timeout parameter to indicate how much time was left...."

I wonder why it is 'may' rather than 'must'? Is there a system version or a kernel version?

I make a test. test_code01.cpp

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <pthread.h>

#define BUFSIZE 1024

struct timeval g_timeout;

int main(int argc, char* argv[])
{
    int sock_clientfd, ret_recvsize, i;
    struct sockaddr_in dest, mine;
    char buffer[BUFSIZE + 1];

    // create socket fd
    if ((sock_clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Socket");
        exit(EXIT_FAILURE);
    }

    // init server address that client will connetct to.
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(9567);
    if(argc != 2)
    {
        printf("Usage: %s <dest ip>\n", argv[0]);
        printf("Usage: %s 127.0.0.1\n", argv[0]);
        return -1;
    }

    printf("-----\n");

    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(1);
    }

    // connect to server
    printf("will connect!\n");
    if (connect(sock_clientfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
    {
        perror("Connect ");
        exit(EXIT_FAILURE);
    }

    int ret_select = 0;
    fd_set readfds;
    FD_SET(sock_clientfd, &readfds);

    g_timeout.tv_sec = 3;
    g_timeout.tv_usec = 0;

    while(1)
    {
        ret_select = select(sock_clientfd + 1, &readfds, NULL, NULL, &g_timeout);

        // Becasue select can update timeout, we need to set value for timeout.
        // g_timeout.tv_sec = 3;
        // g_timeout.tv_usec = 0;

        // Recover sock_clienfd state
        FD_SET(sock_clientfd, &readfds);

        if(ret_select == 0)
        {
            printf("select wait timeout.\n");
            continue;
        }

        bzero(buffer, BUFSIZE + 1);
        ret_recvsize = recv(sock_clientfd, buffer, BUFSIZE, 0);
        if(ret_recvsize > 0)
        {
            printf("get %d message:%s", strlen(buffer), buffer);
            ret_recvsize=0;
        }
        else
        {
            printf("no data from server\n");
        }
    }

    // close sock_clientfd
    close(sock_clientfd);

    return 0;
}

I execute this test_code01.cpp, and the result select wait for 3 seconds for only first times.In my opinion, the select modify timeout.So I modify code follow:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <pthread.h>

#define BUFSIZE 1024

// in order to facilitate the ovservation data
// the timeout is set to global varible
struct timeval g_timeout;

void* thredproc_looktimeout(void*)
{
    while(1)
    {
        printf("left time: %ds %dms\n", g_timeout.tv_sec, g_timeout.tv_usec);
    }
}

int main(int argc, char* argv[])
{
    int sock_clientfd, ret_recvsize, i;
    struct sockaddr_in dest, mine;
    char buffer[BUFSIZE + 1];

    // create socket fd
    if ((sock_clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Socket");
        exit(EXIT_FAILURE);
    }

    // init server address that client will connetct to.
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(9567);
    if(argc != 2)
    {
        printf("Usage: %s <dest ip>\n", argv[0]);
        printf("Usage: %s 127.0.0.1\n", argv[0]);
        return -1;
    }

    printf("-----\n");

    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(1);
    }

    // connect to server
    printf("will connect!\n");
    if (connect(sock_clientfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
    {
        perror("Connect ");
        exit(EXIT_FAILURE);
    }

    int ret_select = 0;
    fd_set readfds;
    FD_SET(sock_clientfd, &readfds);
    printf("fdset add fd start+++\n");
    for(int i = 0; i < 2000; i++)
    {
        FD_SET(1025+i, &readfds);
    }
    printf("%d--%s\n", errno, strerror(errno));
    printf("fdset add 2000 fd finish...\n");

    g_timeout.tv_sec = 3;
    g_timeout.tv_usec = 0;


    /**
     *  look timeout left time by create thread
     **/
    pthread_t tid;
    int ret_pthreadcreate = pthread_create(&tid, NULL, thredproc_looktimeout, NULL);
    printf("pthread_create return value is %d\n", ret_pthreadcreate);

    while(1)
    {
        ret_select = select(sock_clientfd + 1, &readfds, NULL, NULL, &g_timeout);

        // Becasue select can update timeout, we need to set value for timeout.
        g_timeout.tv_sec = 3;
        g_timeout.tv_usec = 0;

        // Recover sock_clienfd state
        FD_SET(sock_clientfd, &readfds);

        if(ret_select == 0)
        {
            printf("select wait timeout.\n");
            continue;
        }

        bzero(buffer, BUFSIZE + 1);
        ret_recvsize = recv(sock_clientfd, buffer, BUFSIZE, 0);
        if(ret_recvsize > 0)
        {
            printf("get %d message:%s", strlen(buffer), buffer);
            ret_recvsize=0;
        }
        else
        {
            printf("no data from server\n");
        }
    }

    // close sock_clientfd
    close(sock_clientfd);

    return 0;
}

I think we should see a change in timeout in thredproc_looktimeout.But no change.I don't know why?

study_20160808
  • 176
  • 1
  • 2
  • 14
  • 1
    The man page expands on that further down: "On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this". So it is *may* because the behaviour is implementation dependent. – kaylum Aug 24 '16 at 03:25
  • 1
    Your use of threads just doesn't make any sense. Among other things, the compiler may optimize your `while` loop to fetch `g_timeout` only once. – David Schwartz Aug 24 '16 at 04:13
  • @DavidSchwartz, I used -O0 when I compile the code.But result still is no change.I think the compiler don't optimize that point. – study_20160808 Aug 24 '16 at 05:44
  • 1
    @study_20160808 You miss my point. It's about your code. It doesn't matter what any particular compiler actually does because that doesn't change your code. Your code doesn't make sense because, among other things, perfectly legal compiler optimizations will break it. (If you try to fix the code you will find that you cannot because it is attempting something fundamentally broken.) – David Schwartz Aug 24 '16 at 08:51
  • @DavidSchwartz,I really don't understand for your point.Could you show a example about your idea. – study_20160808 Aug 24 '16 at 11:33
  • 1
    @study_20160808 The `select` function provides no way to synchronize with another thread. There's simply no way you can sanely write code to access something that you expect the `select` function to be modifying. Try it. – David Schwartz Aug 24 '16 at 16:43
  • @DavidSchwartz,Your point maybe that another thread can't get data modified by select while the wait is still in progress.As you said, the select function provides no way to synchronize with another thread.Do you mean this? – study_20160808 Aug 24 '16 at 23:27
  • 1
    @study_20160808 Yes. There's no possible it could update the structure while it was in operation since there's no way such a change could be observed. – David Schwartz Aug 25 '16 at 01:39
  • @DavidSchwartz,Ok,I got it, Thank you! – study_20160808 Aug 25 '16 at 02:01

4 Answers4

1

The manual page means that on return the timeout might be updated to indicate the amount of time left. It absolutely doesn't mean that the timeout will be updated while the wait is still in progress.

And even if it was, there is no possible way you could detect it. Your attempt to do with threads won't work because it has no synchronization of any kind. Among other problems, the compiler could optimize the while loop to read g_timeout only once. And there is no conceivable synchronization mechanism you could use.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
0

Because a valid timeout may also be

  • null in the case you want to wait forever
  • both fields of the timeval are set to 0, select returns immediately

In both those cases subtraction doesn't make sense, hence no "must."

Your addition of code makes this a duplicate of https://stackoverflow.com/a/7382740/4080377. You're seeing a copy of the variable, not the real thing

Community
  • 1
  • 1
Tom Scanlan
  • 113
  • 4
0

I get a answer.In my system, select actualy modify timeout value.I don't fully understand select may update timeout before. Now, (1)all_time:it is initial value of timeout.For example, timeout.tv_sec is equal to three and timeout.tv_usec is equal to zero. (2)consume_time:it is the time value that from start to return of select to spended. (3)left_time:it does't use time for select. Linux manual actually implies the meaning that left_time add consume_time is equal to all_time. Why it "may update".I think Tom Scanlan's answer is right. The follow code:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <pthread.h>


#define BUFSIZE 1024

// in order to facilitate the ovservation data
// the timeout is set to global varible
struct timeval g_timeout;


void* thredproc_looktimeout(void*)
{
    while(1)
    {
        if(g_timeout.tv_sec != 3 && g_timeout.tv_usec != 0)
        {
            printf("left time: %ds %dms\n", g_timeout.tv_sec, g_timeout.tv_usec);
        }
    }
}



int main(int argc, char* argv[])
{
    int sock_clientfd, ret_recvsize, i;
    struct sockaddr_in dest, mine;
    char buffer[BUFSIZE + 1];

    // create socket fd
    if ((sock_clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Socket");
        exit(EXIT_FAILURE);
    }

    // init server address that client will connetct to.
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(9567);
    if(argc != 2)
    {
        printf("Usage: %s <dest ip>\n", argv[0]);
        printf("Usage: %s 127.0.0.1\n", argv[0]);
        return -1;
    }

    printf("-----\n");

    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(1);
    }

    // connect to server
    printf("will connect!\n");
    if (connect(sock_clientfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
    {
        perror("Connect ");
        exit(EXIT_FAILURE);
    }

    int ret_select = 0;
    fd_set readfds;
    FD_SET(sock_clientfd, &readfds);

    g_timeout.tv_sec = 3;
    g_timeout.tv_usec = 0;


    /**
     *  look timeout left time by create thread
     **/
    pthread_t tid;
    int ret_pthreadcreate = pthread_create(&tid, NULL, thredproc_looktimeout, NULL);
    printf("pthread_create return value is %d\n", ret_pthreadcreate);

    while(1)
    {
        ret_select = select(sock_clientfd + 1, &readfds, NULL, NULL, &g_timeout);


        // Recover sock_clienfd state
        FD_SET(sock_clientfd, &readfds);

        if(ret_select == 0)
        {
            printf("select wait timeout.\n");
            // Becasue select can update timeout, we need to set value for timeout.
            g_timeout.tv_sec = 3;
            g_timeout.tv_usec = 0;

            continue;
        }

        bzero(buffer, BUFSIZE + 1);
        ret_recvsize = recv(sock_clientfd, buffer, BUFSIZE, 0);
        if(ret_recvsize > 0)
        {
            printf("get %d message:%s", strlen(buffer), buffer);
            ret_recvsize=0;
        }
        else
        {
            printf("no data from server\n");
        }

        sleep(3);
        // Becasue select can update timeout, we need to set value for timeout.
        g_timeout.tv_sec = 3;
        g_timeout.tv_usec = 0;

    }

    // close sock_clientfd
    close(sock_clientfd);

    return 0;
}

This code can get no-zero value in thredproc_looktimeout thread function. In addition, such is in case, As David Schwartz said: it absolutely doesn't mean that the timeout will be updated while the wait is still in progress.

study_20160808
  • 176
  • 1
  • 2
  • 14
0

You can try this code. I added some code and some comment.

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <pthread.h>


#define BUFSIZE 1024

// in order to facilitate the ovservation data
// the timeout is set to global varible
struct timeval g_timeout;


void* thredproc_looktimeout(void*)
{
 while(1)
{
    if(g_timeout.tv_sec != 3 && g_timeout.tv_usec != 0)
    {
        printf("left time: %ds %dms\n", g_timeout.tv_sec, g_timeout.tv_usec);
    }
}
}




int main(int argc, char* argv[])
{   

int sock_clientfd, ret_recvsize, i;    
struct sockaddr_in dest, mine;    
char buffer[BUFSIZE + 1];

// create socket fd
if ((sock_clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
    perror("Socket");
    exit(EXIT_FAILURE);
}

// init server address that client will connetct to.
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(9567);
if(argc != 2)
{
    printf("Usage: %s <dest ip>\n", argv[0]);
    printf("Usage: %s 127.0.0.1\n", argv[0]);
    return -1;
}

printf("-----\n");

if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
{
    perror(argv[1]);
    exit(1);
}

// connect to server
printf("will connect!\n");
if (connect(sock_clientfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
{
    perror("Connect ");
    exit(EXIT_FAILURE);
}

int ret_select = 0;
fd_set readfds;

/*new code added*/
FD_ZERO(&readfs);
/* before you set any fd in readfs, you should set 0 all fds in reads. For security, because some times 
readfs can alloced some old trash data place, and a chance in readfs may some data.*/

FD_SET(sock_clientfd, &readfds);

g_timeout.tv_sec = 3;
g_timeout.tv_usec = 0;


/**
 *  look timeout left time by create thread
 **/
pthread_t tid;
int ret_pthreadcreate = pthread_create(&tid, NULL, thredproc_looktimeout, NULL);
printf("pthread_create return value is %d\n", ret_pthreadcreate);

while(1)
{
    ret_select = select(sock_clientfd + 1, &readfds, NULL, NULL, &g_timeout);

     if(ret_select == -1){

         /*return some error or what you want here. Because if ret_select is equal to 1. That is a problem*/

     }

    // Recover sock_clienfd state
   /* FD_SET(sock_clientfd, &readfds);*/
   /* you don't need to same fd set again. That shuold be always some data ready to read()*/

    if(ret_select == 0)
    {
        printf("select wait timeout.\n");
        // Becasue select can update timeout, we need to set value for timeout.
        g_timeout.tv_sec = 3;
        g_timeout.tv_usec = 0;

        continue;
    }

    /*bzero(buffer, BUFSIZE + 1);
    ret_recvsize = recv(sock_clientfd, buffer, BUFSIZE, 0);
    if(ret_recvsize > 0)
    {
        printf("get %d message:%s", strlen(buffer), buffer);
        ret_recvsize=0;
    }
    else
    {
        printf("no data from server\n");
    }*/



    /*Here you can check just sock_clienfd. If that is true, some data ready to read on socket.*/
    if(FD_ISSET(sock_clientfd,&readfs){

        bzero(buffer, BUFSIZE + 1);
    ret_recvsize = recv(sock_clientfd, buffer, BUFSIZE, 0);

    }

    /*sleep(3);*/ /*your select() function already wait 3 second before go down*/
    // Becasue select can update timeout, we need to set value for timeout.
    g_timeout.tv_sec = 3;
    g_timeout.tv_usec = 0;

}

// close sock_clientfd
close(sock_clientfd);

return 0;
}
zalamon
  • 1
  • 4