void TNonblockingServer::TConnection::setFlags(short eventFlags) { // Catch the do nothing case if (eventFlags_ == eventFlags) { return; } // Delete a previously existing event if (eventFlags_ != 0) { if (event_del(&event_) == -1) { GlobalOutput("TConnection::setFlags event_del"); return; } } // Update in memory structure eventFlags_ = eventFlags; // Do not call event_set if there are no flags if (!eventFlags_) { return; } /* * event_set: * * Prepares the event structure &event to be used in future calls to * event_add() and event_del(). The event will be prepared to call the * eventHandler using the 'sock' file descriptor to monitor events. * * The events can be either EV_READ, EV_WRITE, or both, indicating * that an application can read or write from the file respectively without * blocking. * * The eventHandler will be called with the file descriptor that triggered * the event and the type of event which will be one of: EV_TIMEOUT, * EV_SIGNAL, EV_READ, EV_WRITE. * * The additional flag EV_PERSIST makes an event_add() persistent until * event_del() has been called. * * Once initialized, the &event struct can be used repeatedly with * event_add() and event_del() and does not need to be reinitialized unless * the eventHandler and/or the argument to it are to be changed. However, * when an ev structure has been added to libevent using event_add() the * structure must persist until the event occurs (assuming EV_PERSIST * is not set) or is removed using event_del(). You may not reuse the same * ev structure for multiple monitored descriptors; each descriptor needs * its own ev. */ event_set(&event_, tSocket_->getSocketFD(), eventFlags_, TConnection::eventHandler, this); event_base_set(ioThread_->getEventBase(), &event_); // Add the event if (event_add(&event_, 0) == -1) { GlobalOutput("TConnection::setFlags(): could not event_add"); } }
inline void TZlibTransport::checkZlibRvNothrow(int status, const char* message) { if (status != Z_OK) { string output = "TZlibTransport: zlib failure in destructor: " + TZlibTransportException::errorMessage(status, message); GlobalOutput(output.c_str()); } }
void TFileTransport::seekToChunk(int32_t chunk) { if (fd_ <= 0) { throw TTransportException("File not open"); } int32_t numChunks = getNumChunks(); // file is empty, seeking to chunk is pointless if (numChunks == 0) { return; } // negative indicates reverse seek (from the end) if (chunk < 0) { chunk += numChunks; } // too large a value for reverse seek, just seek to beginning if (chunk < 0) { T_DEBUG("%s", "Incorrect value for reverse seek. Seeking to beginning..."); chunk = 0; } // cannot seek past EOF bool seekToEnd = false; off_t minEndOffset = 0; if (chunk >= numChunks) { T_DEBUG("%s", "Trying to seek past EOF. Seeking to EOF instead..."); seekToEnd = true; chunk = numChunks - 1; // this is the min offset to process events till minEndOffset = lseek(fd_, 0, SEEK_END); } off_t newOffset = off_t(chunk) * chunkSize_; offset_ = lseek(fd_, newOffset, SEEK_SET); readState_.resetAllValues(); currentEvent_ = NULL; if (offset_ == -1) { GlobalOutput("TFileTransport: lseek error in seekToChunk"); throw TTransportException("TFileTransport: lseek error in seekToChunk"); } // seek to EOF if user wanted to go to last chunk if (seekToEnd) { uint32_t oldReadTimeout = getReadTimeout(); setReadTimeout(NO_TAIL_READ_TIMEOUT); // keep on reading unti the last event at point of seekChunk call boost::scoped_ptr<eventInfo> event; while ((offset_ + readState_.bufferPtr_) < minEndOffset) { event.reset(readEvent()); if (event.get() == NULL) { break; } } setReadTimeout(oldReadTimeout); } }
bool TFileTransportBuffer::addEvent(eventInfo *event) { if (bufferMode_ == READ) { GlobalOutput("Trying to write to a buffer in read mode"); } if (writePoint_ < size_) { buffer_[writePoint_++] = event; return true; } else { // buffer is full return false; } }
void TFileTransport::performRecovery() { // perform some kickass recovery uint32_t curChunk = getCurChunk(); if (lastBadChunk_ == curChunk) { numCorruptedEventsInChunk_++; } else { lastBadChunk_ = curChunk; numCorruptedEventsInChunk_ = 1; } if (numCorruptedEventsInChunk_ < maxCorruptedEvents_) { // maybe there was an error in reading the file from disk // seek to the beginning of chunk and try again seekToChunk(curChunk); } else { // just skip ahead to the next chunk if we not already at the last chunk if (curChunk != (getNumChunks() - 1)) { seekToChunk(curChunk + 1); } else if (readTimeout_ == TAIL_READ_TIMEOUT) { // if tailing the file, wait until there is enough data to start // the next chunk while(curChunk == (getNumChunks() - 1)) { usleep(DEFAULT_CORRUPTED_SLEEP_TIME_US); } seekToChunk(curChunk + 1); } else { // pretty hosed at this stage, rewind the file back to the last successful // point and punt on the error readState_.resetState(readState_.lastDispatchPtr_); currentEvent_ = NULL; char errorMsg[1024]; sprintf(errorMsg, "TFileTransport: log file corrupted at offset: %lu", (offset_ + readState_.lastDispatchPtr_)); GlobalOutput(errorMsg); throw TTransportException(errorMsg); } } }
void TSocket::setRecvTimeout(int ms) { if (ms < 0) { char errBuf[512]; sprintf(errBuf, "TSocket::setRecvTimeout with negative input: %d\n", ms); GlobalOutput(errBuf); return; } if (socket_ >= 0) { struct timeval r = {(int)(ms/1000), (int)((ms%1000)*1000)}; int ret = setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, &r, sizeof(r)); if (ret == -1) { int errno_copy = errno; // Copy errno because we're allocating memory. GlobalOutput.perror("TSocket::setRecvTimeout() setsockopt() " + getSocketInfo(), errno_copy); return; } } options_.recvTimeout = ms; }
// note caller is responsible for freeing returned events eventInfo* TFileTransport::readEvent() { int readTries = 0; if (!readBuff_) { readBuff_ = new uint8_t[readBuffSize_]; } while (1) { // read from the file if read buffer is exhausted if (readState_.bufferPtr_ == readState_.bufferLen_) { // advance the offset pointer offset_ += readState_.bufferLen_; readState_.bufferLen_ = ::read(fd_, readBuff_, readBuffSize_); // if (readState_.bufferLen_) { // T_DEBUG_L(1, "Amount read: %u (offset: %lu)", readState_.bufferLen_, offset_); // } readState_.bufferPtr_ = 0; readState_.lastDispatchPtr_ = 0; // read error if (readState_.bufferLen_ == -1) { readState_.resetAllValues(); GlobalOutput("TFileTransport: error while reading from file"); throw TTransportException("TFileTransport: error while reading from file"); } else if (readState_.bufferLen_ == 0) { // EOF // wait indefinitely if there is no timeout if (readTimeout_ == TAIL_READ_TIMEOUT) { usleep(eofSleepTime_); continue; } else if (readTimeout_ == NO_TAIL_READ_TIMEOUT) { // reset state readState_.resetState(0); return NULL; } else if (readTimeout_ > 0) { // timeout already expired once if (readTries > 0) { readState_.resetState(0); return NULL; } else { usleep(readTimeout_ * 1000); readTries++; continue; } } } } readTries = 0; // attempt to read an event from the buffer while(readState_.bufferPtr_ < readState_.bufferLen_) { if (readState_.readingSize_) { if(readState_.eventSizeBuffPos_ == 0) { if ( (offset_ + readState_.bufferPtr_)/chunkSize_ != ((offset_ + readState_.bufferPtr_ + 3)/chunkSize_)) { // skip one byte towards chunk boundary // T_DEBUG_L(1, "Skipping a byte"); readState_.bufferPtr_++; continue; } } readState_.eventSizeBuff_[readState_.eventSizeBuffPos_++] = readBuff_[readState_.bufferPtr_++]; if (readState_.eventSizeBuffPos_ == 4) { // 0 length event indicates padding if (*((uint32_t *)(readState_.eventSizeBuff_)) == 0) { // T_DEBUG_L(1, "Got padding"); readState_.resetState(readState_.lastDispatchPtr_); continue; } // got a valid event readState_.readingSize_ = false; if (readState_.event_) { delete(readState_.event_); } readState_.event_ = new eventInfo(); readState_.event_->eventSize_ = *((uint32_t *)(readState_.eventSizeBuff_)); // check if the event is corrupted and perform recovery if required if (isEventCorrupted()) { performRecovery(); // start from the top break; } } } else { if (!readState_.event_->eventBuff_) { readState_.event_->eventBuff_ = new uint8_t[readState_.event_->eventSize_]; readState_.event_->eventBuffPos_ = 0; } // take either the entire event or the remaining bytes in the buffer int reclaimBuffer = min((uint32_t)(readState_.bufferLen_ - readState_.bufferPtr_), readState_.event_->eventSize_ - readState_.event_->eventBuffPos_); // copy data from read buffer into event buffer memcpy(readState_.event_->eventBuff_ + readState_.event_->eventBuffPos_, readBuff_ + readState_.bufferPtr_, reclaimBuffer); // increment position ptrs readState_.event_->eventBuffPos_ += reclaimBuffer; readState_.bufferPtr_ += reclaimBuffer; // check if the event has been read in full if (readState_.event_->eventBuffPos_ == readState_.event_->eventSize_) { // set the completed event to the current event eventInfo* completeEvent = readState_.event_; completeEvent->eventBuffPos_ = 0; readState_.event_ = NULL; readState_.resetState(readState_.bufferPtr_); // exit criteria return completeEvent; } } } } }
/** * Creates a socket to listen on and binds it to the local port. */ void TNonblockingServer::createAndListenOnSocket() { THRIFT_SOCKET s; struct addrinfo hints, *res, *res0; int error; char port[sizeof("65536") + 1]; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; sprintf(port, "%d", port_); // Wildcard address error = getaddrinfo(NULL, port, &hints, &res0); if (error) { throw TException("TNonblockingServer::serve() getaddrinfo " + string(THRIFT_GAI_STRERROR(error))); } // Pick the ipv6 address first since ipv4 addresses can be mapped // into ipv6 space. for (res = res0; res; res = res->ai_next) { if (res->ai_family == AF_INET6 || res->ai_next == NULL) break; } // Create the server socket s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) { freeaddrinfo(res0); throw TException("TNonblockingServer::serve() socket() -1"); } #ifdef IPV6_V6ONLY if (res->ai_family == AF_INET6) { int zero = 0; if (-1 == setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, const_cast_sockopt(&zero), sizeof(zero))) { GlobalOutput("TServerSocket::listen() IPV6_V6ONLY"); } } #endif // #ifdef IPV6_V6ONLY int one = 1; // Set THRIFT_NO_SOCKET_CACHING to avoid 2MSL delay on server restart setsockopt(s, SOL_SOCKET, THRIFT_NO_SOCKET_CACHING, const_cast_sockopt(&one), sizeof(one)); if (::bind(s, res->ai_addr, static_cast<int>(res->ai_addrlen)) == -1) { ::THRIFT_CLOSESOCKET(s); freeaddrinfo(res0); throw TTransportException(TTransportException::NOT_OPEN, "TNonblockingServer::serve() bind", THRIFT_GET_SOCKET_ERROR); } // Done with the addr info freeaddrinfo(res0); // Set up this file descriptor for listening listenSocket(s); }