1. Preface
In the previous article in this series, the use of Libcurl for poll () was described. Refer to "Libcurl (2)-Libcurl use of poll".
This article mainly analysis of the encapsulation of Select () in Curl_poll () is used. Similar to the previous one, we only isolate the code associated with select.
2.curl_poll Function Analysis
Some of the other data structures used in this function can be referred to in the previous article. This article is no longer covered.
/* This function is the encapsulation of poll (). If poll () does not exist, a select () substitution is used. If you are using select () and the file descriptor FD is too large to exceed fd_setsize, the error is returned. If the passed-in timeout value is a negative number, it waits indefinitely until no valid FD is provided. When this occurs (without a valid FD), the negative timeout value is ignored and the function immediately times out. return value:-1 = system call error or fd>=fd_setsize.0 = timeout. N = number of POLLFD structures returned, with revents members not 0.*/int curl_poll (struct POLLFD ufds[], unsigned int nfds, int timeout_ms) {struct time Val pending_tv;struct timeval *ptimeout;fd_set fds_read;fd_set fds_write;fd_set fds_err;curl_socket_t maxfd;struct Timeval Initial_tv = {0, 0};bool fds_none = TRUE; Used to verify that the incoming UFDs array is valid unsigned int i;int Pending_ms = 0;int error; Save error code int r;//detects if there is a valid FD in all FD. If there is at least one valid FD, the Fds_none is set to false, stop detection if (UFDS) {for (i = 0; i < Nfds; i++) {if (ufds[i].fd! = Curl_socket_bad) {fds_no ne = False;break;}}} If all FD is invalid (i.e. bad socket,-1), wait for a period of time before returning directly. if (fds_none) {r = Curl_wait_ms (Timeout_ms); This function is then parsed to return R;} When the passed-in timeout value is a negative number (blocking case) or 0 o'clock, you do not need to measure elapsed time. Otherwise, gets the current time. if (Timeout_ms > 0) {pending_ms = TIMEOUT_MS;INITIAL_TV = CURLX_tvnow ();//Call Gettimeofday () or time () to get the current}//the fdset needs to be reinitialized each time a select () is called, because they are both input parameters and output parameters. Fd_zero (&fds_read); Fd_zero (&fds_write); Fd_zero (&fds_err); maxfd = (curl_socket_t) -1;for (i = 0; i < Nfds; i++) {ufds[i].revents = 0;if (ufds[i].fd = CURL_) Socket_bad)//skip Invalid fdcontinue; Verify_sock (UFDS[I].FD); Detects if 0<=fd<fd_setsize. Out of this range, it returns -1.if (Ufds[i].events & (Pollin | Pollout | Pollpri | Pollrdnorm | Pollwrnorm | Pollrdband) {if (Ufds[i].fd > Maxfd)//gets to the largest FD, as the first parameter of select (). MAXFD = Ufds[i].fd;if (ufds[i].events & (Pollrdnorm | Pollin) Fd_set (UFDS[I].FD, &fds_read); if (Ufds[i].events & (Pollwrnorm | Pollout) Fd_set (UFDS[I].FD, &fds_write); if (Ufds[i].events & (Pollrdband | POLLPRI) Fd_set (UFDS[I].FD, &fds_err);}} Timeout parameter as select () Ptimeout = (Timeout_ms < 0)? NULL: &pending_tv;do {if (Timeout_ms > 0) {pending_tv.tv_sec = Pending_ms/1000;pending_tv.tv_usec = (Pending_ms % 1000) * 1000;} else if (!timeout_ms) {Pending_tv.tv_sec = 0;pending_tv.tv_usec = 0;} Really call Select (). The 2,3,4 parameter has been initialized (emptied) before. R = Select ((int) Maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout); if (r! =-1)//select call succeeded, end Loop break;//s Elect call failed, return-1. The error code can be obtained by errno. Error = Sockerrno; Macro definition. #define Sockerrno (errno)//The following error_not_eintr is a macro definition. #define ERROR_NOT_EINTR (0 | | Error! = EINTR) if (Error && error_not_eintr)//Detect if error is present and not eintr error break;//no EINTR error or if there is a fault, then you can determine whether to continue with select () if (Timeout_ms > 0) {//elapsed_ms is a macro definition. #define ELAPSED_MS (int) Curlx_tvdiff (Curlx_tvnow (), initial_tv) Pending_ms = timeout_ms-elapsed_ms;if (Pending_ms &l t;= 0) {r = 0; Simulate the case of a select timeout break;}}} while (r = =-1);/* You can now summarize this while loop above: 1. If the select call succeeds (that is, the return value >=0), the Loop 2 is ended. If the select call fails (that is, the return value ==-1), Detects if the errno is a eintr error. If it is not eintr, the loop is ended. If it is eintr, the detection has timed out. The timeout then ends the loop, and the Select () continues without a timeout. */if (R < 0)//select () call failed return-1;if (r = = 0)//select () timeout return 0;//select () call succeeded, count the number of FD in which the state has changed, save to R.R = 0;f or (i = 0; i < Nfds; i++) {ufds[i]. revents = 0;if (ufds[i].fd = = Curl_socket_bad) continue;if (Fd_isset (UFDS[I].FD, &fds_read))//fd readable ufds[i].revents |= pollin;if (Fd_isset (UFDS[I].FD, &fds_write))//fd writable ufds[i].revents |= pollout;if (Fd_isset (UFDS[I].FD, &fds _ERR))//fd error ufds[i].revents |= pollpri;if (ufds[i].revents! = 0) r++;} return r;}
When this function is completed, the member revents in the first input parameter UFDs may be modified. Finally, the function returns the actual return value of select ().
3.curl_wait_ms Function Analysis
The following is a concrete implementation of the Curl_wait_ms () function. is also based on poll or select. This also discusses only its select () implementation version.
/* This function is used to wait for a specific time value. Called in the function Curl_socket_ready () and Curl_poll (). When no FD is provided to detect, it is just waiting for a specific period of time. In the case of Windows platforms, the poll () and select () timeout mechanisms in Winsock require a valid socket FD. This function does not allow infinite waits, and returns immediately if the value passed in is 0 or negative. The precision and maximum value of the time-out depends on the system. return value: 1 = system call error, or invalid input value (timeout), or interrupted. 0 = The specified time has expired */int curl_wait_ms (int timeout_ms) {struct Timeval pending_tv;struct timeval initial_tv;int pending_ms;int Error;int r = 0;if (!timeout_ms)//timeout value 0, return immediately 0;if (Timeout_ms < 0)//cannot be negative {Set_sockerrno (EINVAL); return -1;} Pending_ms = TIMEOUT_MS;INITIAL_TV = Curlx_tvnow ();d o {pending_tv.tv_sec = Pending_ms/1000;pending_tv.tv_usec = (Pendin G_ms%) * 1000;r = SELECT (0, NULL, NULL, NULL, &PENDING_TV), if (r! =-1) the//select () call succeeds, then jumps out of the loop break;//select With a failure, return-1. The error code can be obtained by errno. Error = Sockerrno; Macro definition. #define Sockerrno (errno)//The following error_not_eintr is a macro definition. #define ERROR_NOT_EINTR (0 | | Error! = EINTR) if (Error && error_not_eintr)////detects if there is an error and is not a eintr error break;/ /elapsed_ms is a macro definition://#define ELAPSED_MS (iNT) Curlx_tvdiff (Curlx_tvnow (), initial_tv) Pending_ms = timeout_ms-elapsed_ms;if (Pending_ms <= 0) {r = 0; Simulate the case of a select timeout break;}} while (r = =-1);//Ensure that the return value R can only be 1 (timeout failed) or 0 (timeout succeeded). R cannot be greater than 0 because the 3 fdset arrays passed into the Select () function are all null. If the return value of select is >0, then the call is faulty,//So the R is set to 1, that is, the call time-out fails. if (r) r = -1;return r;}
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
Libcurl implementation Resolution (3)-Libcurl use of Select