// Copyright (c) 2006- Facebook // Distributed under the Thrift Software License // // See accompanying file LICENSE or visit the Thrift site at: // http://developers.facebook.com/thrift/ #ifndef THRIFT_SERVER_TEVENTWORKER_H_ #define THRIFT_SERVER_TEVENTWORKER_H_ 1 #include "thrift/lib/cpp/async/TAsyncServerSocket.h" #include "thrift/lib/cpp/async/TAsyncSSLSocket.h" #include "thrift/lib/cpp/async/TEventServer.h" #include "thrift/lib/cpp/async/TEventBase.h" #include "thrift/lib/cpp/async/TEventHandler.h" #include "thrift/lib/cpp/async/TNotificationQueue.h" #include "thrift/lib/cpp/server/TServer.h" #include #include #include namespace apache { namespace thrift { namespace async { // Forward declaration of classes class TAsyncProcessorFactory; class TEventConnection; class TEventServer; class TaskCompletionMessage; /** * TEventWorker drives the actual I/O for TEventServer connections. * * The TEventServer itself accepts incoming connections, then hands off each * connection to a TEventWorker running in another thread. There should * typically be around one TEventWorker thread per core. */ class TEventWorker : public apache::thrift::server::TServer, public TAsyncServerSocket::AcceptCallback, public TAsyncSSLSocket::HandshakeCallback, public TNotificationQueue::Consumer { private: /// Object that processes requests. boost::shared_ptr asyncProcessorFactory_; /// The mother ship. TEventServer* server_; /// An instance's TEventBase for I/O. TEventBase eventBase_; /// Our ID in [0:nWorkers). uint32_t workerID_; /// Pipe that task completion notifications are sent over TNotificationQueue notificationQueue_; /** * A stack of idle TEventConnection objects for reuse. * When we close a connection, we place it on this stack so that the * object can be reused later, rather than freeing the memory and * reallocating a new object later. */ std::stack connectionStack_; /// Transport type to use TEventServer::TransportType transportType_; /** * Called when the connection is fully accepted (after SSL accept if needed) */ void finishConnectionAccepted(TAsyncSocket *asyncSock); /** * Create or reuse a TEventConnection initialized for the given socket FD. * * @param socket the FD of a freshly-connected socket. * @param address the peer address of the socket. * @return pointer to a TConenction object for that socket. */ TEventConnection* createConnection( boost::shared_ptr asyncSocket, const transport::TSocketAddress* address); /** * Handler called when a new connection may be available. */ void acceptConnections(); void makeCompletionCallback(); /** * Initialize our TEventBase to generate incoming connection events. * Note that this is called once before the main loop is executed and * sets up a READ event on the output of the listener's socktpair. */ void registerEvents(); /** * Callback used when loop latency exceeds the requested threshold. */ void maxLatencyCob(); typedef std::list ConnectionList; // pointer hash functor static const uint64_t kPtrHashMult = 5700357409661599291LL; static const uint64_t kPtrHashShift = 3; template struct hash { }; template struct hash { size_t operator()(T* p) const { return ((size_t)p ^ ((size_t)p >> kPtrHashShift)) * kPtrHashMult; } size_t operator()(const T* p) const { return ((size_t)p ^ ((size_t)p >> kPtrHashShift)) * kPtrHashMult; } }; typedef __gnu_cxx::hash_map > ConnectionMap; /** * The list of active connections (used to allow the oldest connections * to be shed during overload). */ ConnectionList activeConnectionList_; /** * A hash map used to map connections to their place in the connection list */ ConnectionMap activeConnectionMap_; public: /** * TEventWorker is the actual server object for existing connections. * One or more of these should be created by TEventServer (one per * CPU core is recommended). * * @param processorFactory a TAsyncProcessorFactory object as * obtained from the generated Thrift code (the user service * is integrated through this). * @param inputProtocolFactory the TProtocolFactory class supporting * inbound Thrift requests. * @param outputProtocolFactory the TProtocolFactory class supporting * responses (if any) after processing completes. * @param server the TEventServer which created us. * @param workerID the ID assigned to this worker */ TEventWorker(boost::shared_ptr processorFactory, boost::shared_ptr protocolFactory, TEventServer* server, uint32_t workerID) : TServer(boost::shared_ptr()), asyncProcessorFactory_(processorFactory), server_(server), eventBase_(), workerID_(workerID), transportType_(TEventServer::FRAMED) { setDuplexProtocolFactory(protocolFactory); transportType_ = server->getTransportType(); if (transportType_ == TEventServer::FRAMED) { // do the dynamic cast once rather than per connection if (dynamic_cast( protocolFactory.get())) { transportType_ = TEventServer::HEADER; } } } /** * Destroy a TEventWorker. We've use boost::scoped_ptr<> to take care * of freeing up memory, so nothing to be done here but release the * connection stack. */ virtual ~TEventWorker(); /** * Get my TAsyncProcessorFactory object. * * @returns pointer to my TAsyncProcessorFactory object. */ boost::shared_ptr getAsyncProcessorFactory() { return asyncProcessorFactory_; } /** * Get my TEventBase object. * * @returns pointer to my TEventBase object. */ TEventBase* getEventBase() { return &eventBase_; } /** * Get underlying server. * * @returns pointer to TEventServer */ TEventServer* getServer() const { return server_; } /** * Get my numeric worker ID (for diagnostics). * * @return integer ID of this worker */ int32_t getID() { return workerID_; } /** * Dispose of a TEventConnection object. * Will add to a pool of these objects or destroy as necessary. * * @param connection a now-idle connection. */ void returnConnection(TEventConnection* connection); /** * Cause a completion callback for the requested connection to occur * within that connection's context. * * @param msg task completion message * @return true if notification was sent, false if it failed. */ bool notifyCompletion(TaskCompletionMessage &&msg); /** * Task completed (called in this worker's thread) */ void messageAvailable(TaskCompletionMessage &&msg); virtual void connectionAccepted(int fd, const transport::TSocketAddress& clientAddr) THRIFT_NOEXCEPT; virtual void acceptError(const std::exception& ex) THRIFT_NOEXCEPT; virtual void acceptStopped() THRIFT_NOEXCEPT; /** * TAsyncSSLSocket::HandshakeCallback interface */ void handshakeSuccess(TAsyncSSLSocket *sock) THRIFT_NOEXCEPT; void handshakeError(TAsyncSSLSocket *sock, const transport::TTransportException& ex) THRIFT_NOEXCEPT; /** * Enter event loop and serve. */ void serve(); /** * Exit event loop. */ void shutdown(); }; }}} // apache::thrift::async #endif // #ifndef THRIFT_SERVER_TEVENTWORKER_H_