// 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 <ext/hash_map>
#include <list>
#include <stack>

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<TaskCompletionMessage>::Consumer {
 private:
  /// Object that processes requests.
  boost::shared_ptr<TAsyncProcessorFactory> 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<TaskCompletionMessage> 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<TEventConnection*> 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<TAsyncSocket> 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<TEventConnection*> ConnectionList;

  // pointer hash functor
  static const uint64_t kPtrHashMult = 5700357409661599291LL;
  static const uint64_t kPtrHashShift = 3;
  template<typename T>
    struct hash { };
  template<typename T>
    struct hash<T*> {
      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<const TEventConnection*,
                              ConnectionList::iterator,
                              hash<TEventConnection*> > 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_;

  // Max number of active connections
  int32_t maxNumActiveConnections_;

 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<TAsyncProcessorFactory> processorFactory,
               boost::shared_ptr<apache::thrift::server::TDuplexProtocolFactory>
                protocolFactory,
               TEventServer* server,
               uint32_t workerID) :
    TServer(boost::shared_ptr<apache::thrift::server::TProcessor>()),
    asyncProcessorFactory_(processorFactory),
    server_(server),
    eventBase_(),
    workerID_(workerID),
    transportType_(TEventServer::FRAMED),
    maxNumActiveConnections_(0) {

    setDuplexProtocolFactory(protocolFactory);
    transportType_ = server->getTransportType();
    if (transportType_ == TEventServer::FRAMED) {
      // do the dynamic cast once rather than per connection
      if (dynamic_cast<apache::thrift::protocol::THeaderProtocolFactory*>(
            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<TAsyncProcessorFactory> 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_;
  }

  void setMaxNumActiveConnections(int32_t numActiveConnections) {
    maxNumActiveConnections_ = numActiveConnections;
  }

  int32_t getMaxNumActiveConnections() const {
    return maxNumActiveConnections_;
  }

  /**
   * 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_