bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) { // Calculate timing information struct timeval *ptvWait = NULL; struct timeval tvWait; struct timeval tvStop; if (cmsWait > 0) { // Calculate wait timeval tvWait.tv_sec = cmsWait / 1000; tvWait.tv_usec = (cmsWait % 1000) * 1000; ptvWait = &tvWait; // Calculate when to return in a timeval gettimeofday(&tvStop, NULL); tvStop.tv_sec += tvWait.tv_sec; tvStop.tv_usec += tvWait.tv_usec; if (tvStop.tv_usec >= 1000000) { tvStop.tv_usec -= 1000000; tvStop.tv_sec += 1; } } // Zero all fd_sets. Don't need to do this inside the loop since // select() zeros the descriptors not signaled fd_set fdsRead; FD_ZERO(&fdsRead); fd_set fdsWrite; FD_ZERO(&fdsWrite); fWait_ = true; while (fWait_) { int fdmax = -1; { CriticalSectionScoped cr(&crit_); for (size_t i = 0; i < dispatchers_.size(); ++i) { // Query dispatchers for read and write wait state Dispatcher *pdispatcher = dispatchers_[i]; if (!process_io && (pdispatcher != signal_wakeup_)) continue; int fd = pdispatcher->GetDescriptor(); if (fd > fdmax) fdmax = fd; uint32 ff = pdispatcher->GetRequestedEvents(); if (ff & (DE_READ | DE_ACCEPT)) FD_SET(fd, &fdsRead); if (ff & (DE_WRITE | DE_CONNECT)) FD_SET(fd, &fdsWrite); } } // Wait then call handlers as appropriate // < 0 means error // 0 means timeout // > 0 means count of descriptors ready int n = select(fdmax + 1, &fdsRead, &fdsWrite, NULL, ptvWait); // If error, return error. if (n < 0) { if (errno != EINTR) { printf("select error. %d.\n", errno); return false; } // Else ignore the error and keep going. If this EINTR was for one of the // signals managed by this PhysicalSocketServer, the // PosixSignalDeliveryDispatcher will be in the signaled state in the next // iteration. } else if (n == 0) { // If timeout, return success return true; } else { // We have signaled descriptors CriticalSectionScoped cr(&crit_); for (size_t i = 0; i < dispatchers_.size(); ++i) { Dispatcher *pdispatcher = dispatchers_[i]; int fd = pdispatcher->GetDescriptor(); uint32 ff = 0; int errcode = 0; // Reap any error code, which can be signaled through reads or writes. // TODO: Should we set errcode if getsockopt fails? if (FD_ISSET(fd, &fdsRead) || FD_ISSET(fd, &fdsWrite)) { socklen_t len = sizeof(errcode); ::getsockopt(fd, SOL_SOCKET, SO_ERROR, &errcode, &len); } // Check readable descriptors. If we're waiting on an accept, signal // that. Otherwise we're waiting for data, check to see if we're // readable or really closed. // TODO: Only peek at TCP descriptors. if (FD_ISSET(fd, &fdsRead)) { FD_CLR(fd, &fdsRead); if (pdispatcher->GetRequestedEvents() & DE_ACCEPT) { ff |= DE_ACCEPT; } else if (errcode || pdispatcher->IsDescriptorClosed()) { ff |= DE_CLOSE; } else { ff |= DE_READ; } } // Check writable descriptors. If we're waiting on a connect, detect // success versus failure by the reaped error code. if (FD_ISSET(fd, &fdsWrite)) { FD_CLR(fd, &fdsWrite); if (pdispatcher->GetRequestedEvents() & DE_CONNECT) { if (!errcode) { ff |= DE_CONNECT; } else { ff |= DE_CLOSE; } } else { ff |= DE_WRITE; } } // Tell the descriptor about the event. if (ff != 0) { pdispatcher->OnPreEvent(ff); pdispatcher->OnEvent(ff, errcode); } } } // Recalc the time remaining to wait. Doing it here means it doesn't get // calced twice the first time through the loop if (ptvWait) { ptvWait->tv_sec = 0; ptvWait->tv_usec = 0; struct timeval tvT; gettimeofday(&tvT, NULL); if ((tvStop.tv_sec > tvT.tv_sec) || ((tvStop.tv_sec == tvT.tv_sec)&& (tvStop.tv_usec > tvT.tv_usec))) { ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec; ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec; if (ptvWait->tv_usec < 0) { ptvWait->tv_usec += 1000000; ptvWait->tv_sec -= 1; } } } } return true; }