/** * Takes a socket created by listenSocket() and sets various options on it * to prepare for use in the server. */ void TNonblockingServer::listenSocket(THRIFT_SOCKET s) { // Set socket to nonblocking mode int flags; if ((flags = THRIFT_FCNTL(s, THRIFT_F_GETFL, 0)) < 0 || THRIFT_FCNTL(s, THRIFT_F_SETFL, flags | THRIFT_O_NONBLOCK) < 0) { ::THRIFT_CLOSESOCKET(s); throw TException("TNonblockingServer::serve() THRIFT_O_NONBLOCK"); } int one = 1; struct linger ling = {0, 0}; // Keepalive to ensure full result flushing setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, const_cast_sockopt(&one), sizeof(one)); // Turn linger off to avoid hung sockets setsockopt(s, SOL_SOCKET, SO_LINGER, const_cast_sockopt(&ling), sizeof(ling)); // Set TCP nodelay if available, MAC OS X Hack // See http://lists.danga.com/pipermail/memcached/2005-March/001240.html #ifndef TCP_NOPUSH setsockopt(s, IPPROTO_TCP, TCP_NODELAY, const_cast_sockopt(&one), sizeof(one)); #endif #ifdef TCP_LOW_MIN_RTO if (TSocket::getUseLowMinRto()) { setsockopt(s, IPPROTO_TCP, TCP_LOW_MIN_RTO, const_cast_sockopt(&one), sizeof(one)); } #endif if (listen(s, LISTEN_BACKLOG) == -1) { ::THRIFT_CLOSESOCKET(s); throw TException("TNonblockingServer::serve() listen"); } // Cool, this socket is good to go, set it as the serverSocket_ serverSocket_ = s; if (!port_) { struct sockaddr_storage addr; socklen_t size = sizeof(addr); if (!getsockname(serverSocket_, reinterpret_cast<sockaddr*>(&addr), &size)) { if (addr.ss_family == AF_INET6) { const struct sockaddr_in6* sin = reinterpret_cast<const struct sockaddr_in6*>(&addr); listenPort_ = ntohs(sin->sin6_port); } else { const struct sockaddr_in* sin = reinterpret_cast<const struct sockaddr_in*>(&addr); listenPort_ = ntohs(sin->sin_port); } } else { GlobalOutput.perror("TNonblocking: failed to get listen port: ", THRIFT_GET_SOCKET_ERROR); } } }
/** * Takes a socket created by listenSocket() and sets various options on it * to prepare for use in the server. */ void TNonblockingServer::listenSocket(THRIFT_SOCKET s) { // Set socket to nonblocking mode int flags; if ((flags = THRIFT_FCNTL(s, THRIFT_F_GETFL, 0)) < 0 || THRIFT_FCNTL(s, THRIFT_F_SETFL, flags | THRIFT_O_NONBLOCK) < 0) { ::THRIFT_CLOSESOCKET(s); throw TException("TNonblockingServer::serve() THRIFT_O_NONBLOCK"); } int one = 1; struct linger ling = {0, 0}; // Keepalive to ensure full result flushing setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, const_cast_sockopt(&one), sizeof(one)); // Turn linger off to avoid hung sockets setsockopt(s, SOL_SOCKET, SO_LINGER, const_cast_sockopt(&ling), sizeof(ling)); // Set TCP nodelay if available, MAC OS X Hack // See http://lists.danga.com/pipermail/memcached/2005-March/001240.html #ifndef TCP_NOPUSH setsockopt(s, IPPROTO_TCP, TCP_NODELAY, const_cast_sockopt(&one), sizeof(one)); #endif #ifdef TCP_LOW_MIN_RTO if (TSocket::getUseLowMinRto()) { setsockopt(s, IPPROTO_TCP, TCP_LOW_MIN_RTO, const_cast_sockopt(&one), sizeof(one)); } #endif if (listen(s, LISTEN_BACKLOG) == -1) { ::THRIFT_CLOSESOCKET(s); throw TException("TNonblockingServer::serve() listen"); } // Cool, this socket is good to go, set it as the serverSocket_ serverSocket_ = s; }
/** * Server socket had something happen. We accept all waiting client * connections on fd and assign TConnection objects to handle those requests. */ void TNonblockingServer::handleEvent(THRIFT_SOCKET fd, short which) { (void) which; // Make sure that libevent didn't mess up the socket handles assert(fd == serverSocket_); // Server socket accepted a new connection socklen_t addrLen; sockaddr_storage addrStorage; sockaddr* addrp = (sockaddr*)&addrStorage; addrLen = sizeof(addrStorage); // Going to accept a new client socket THRIFT_SOCKET clientSocket; // Accept as many new clients as possible, even though libevent signaled only // one, this helps us to avoid having to go back into the libevent engine so // many times while ((clientSocket = ::accept(fd, addrp, &addrLen)) != -1) { // If we're overloaded, take action here if (overloadAction_ != T_OVERLOAD_NO_ACTION && serverOverloaded()) { Guard g(connMutex_); nConnectionsDropped_++; nTotalConnectionsDropped_++; if (overloadAction_ == T_OVERLOAD_CLOSE_ON_ACCEPT) { ::THRIFT_CLOSESOCKET(clientSocket); return; } else if (overloadAction_ == T_OVERLOAD_DRAIN_TASK_QUEUE) { if (!drainPendingTask()) { // Nothing left to discard, so we drop connection instead. ::THRIFT_CLOSESOCKET(clientSocket); return; } } } // Explicitly set this socket to NONBLOCK mode int flags; if ((flags = THRIFT_FCNTL(clientSocket, THRIFT_F_GETFL, 0)) < 0 || THRIFT_FCNTL(clientSocket, THRIFT_F_SETFL, flags | THRIFT_O_NONBLOCK) < 0) { GlobalOutput.perror("thriftServerEventHandler: set THRIFT_O_NONBLOCK (THRIFT_FCNTL) ", THRIFT_GET_SOCKET_ERROR); ::THRIFT_CLOSESOCKET(clientSocket); return; } // Create a new TConnection for this client socket. TConnection* clientConnection = createConnection(clientSocket, addrp, addrLen); // Fail fast if we could not create a TConnection object if (clientConnection == NULL) { GlobalOutput.printf("thriftServerEventHandler: failed TConnection factory"); ::THRIFT_CLOSESOCKET(clientSocket); return; } /* * Either notify the ioThread that is assigned this connection to * start processing, or if it is us, we'll just ask this * connection to do its initial state change here. * * (We need to avoid writing to our own notification pipe, to * avoid possible deadlocks if the pipe is full.) * * The IO thread #0 is the only one that handles these listen * events, so unless the connection has been assigned to thread #0 * we know it's not on our thread. */ if (clientConnection->getIOThreadNumber() == 0) { clientConnection->transition(); } else { clientConnection->notifyIOThread(); } // addrLen is written by the accept() call, so needs to be set before the next call. addrLen = sizeof(addrStorage); } // Done looping accept, now we have to make sure the error is due to // blocking. Any other error is a problem if (THRIFT_GET_SOCKET_ERROR != THRIFT_EAGAIN && THRIFT_GET_SOCKET_ERROR != THRIFT_EWOULDBLOCK) { GlobalOutput.perror("thriftServerEventHandler: accept() ", THRIFT_GET_SOCKET_ERROR); } }
void TNonblockingIOThread::createNotificationPipe() { if(evutil_socketpair(AF_LOCAL, SOCK_STREAM, 0, notificationPipeFDs_) == -1) { GlobalOutput.perror("TNonblockingServer::createNotificationPipe ", EVUTIL_SOCKET_ERROR()); throw TException("can't create notification pipe"); } if(evutil_make_socket_nonblocking(notificationPipeFDs_[0])<0 || evutil_make_socket_nonblocking(notificationPipeFDs_[1])<0) { ::THRIFT_CLOSESOCKET(notificationPipeFDs_[0]); ::THRIFT_CLOSESOCKET(notificationPipeFDs_[1]); throw TException("TNonblockingServer::createNotificationPipe() THRIFT_O_NONBLOCK"); } for (int i = 0; i < 2; ++i) { #if LIBEVENT_VERSION_NUMBER < 0x02000000 int flags; if ((flags = THRIFT_FCNTL(notificationPipeFDs_[i], F_GETFD, 0)) < 0 || THRIFT_FCNTL(notificationPipeFDs_[i], F_SETFD, flags | FD_CLOEXEC) < 0) { #else if (evutil_make_socket_closeonexec(notificationPipeFDs_[i]) < 0) { #endif ::THRIFT_CLOSESOCKET(notificationPipeFDs_[0]); ::THRIFT_CLOSESOCKET(notificationPipeFDs_[1]); throw TException("TNonblockingServer::createNotificationPipe() " "FD_CLOEXEC"); } } } /** * Register the core libevent events onto the proper base. */ void TNonblockingIOThread::registerEvents() { threadId_ = Thread::get_current(); assert(eventBase_ == 0); eventBase_ = getServer()->getUserEventBase(); if (eventBase_ == NULL) { eventBase_ = event_base_new(); ownEventBase_ = true; } // Print some libevent stats if (number_ == 0) { GlobalOutput.printf("TNonblockingServer: using libevent %s method %s", event_get_version(), event_base_get_method(eventBase_)); } if (listenSocket_ >= 0) { // Register the server event event_set(&serverEvent_, listenSocket_, EV_READ | EV_PERSIST, TNonblockingIOThread::listenHandler, server_); event_base_set(eventBase_, &serverEvent_); // Add the event and start up the server if (-1 == event_add(&serverEvent_, 0)) { throw TException("TNonblockingServer::serve(): " "event_add() failed on server listen event"); } GlobalOutput.printf("TNonblocking: IO thread #%d registered for listen.", number_); } createNotificationPipe(); // Create an event to be notified when a task finishes event_set(¬ificationEvent_, getNotificationRecvFD(), EV_READ | EV_PERSIST, TNonblockingIOThread::notifyHandler, this); // Attach to the base event_base_set(eventBase_, ¬ificationEvent_); // Add the event and start up the server if (-1 == event_add(¬ificationEvent_, 0)) { throw TException("TNonblockingServer::serve(): " "event_add() failed on task-done notification event"); } GlobalOutput.printf("TNonblocking: IO thread #%d registered for notify.", number_); }