<commit_before>/*=========================================================================

  Program:   Monteverdi
  Language:  C++

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See Copyright.txt for details.

  Monteverdi is distributed under the CeCILL licence version 2. See
  Licence_CeCILL_V2-en.txt or
  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt for more details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

//
// Configuration include.
//// Included at first position before any other ones.
#include "ConfigureMonteverdi.h"

/*****************************************************************************/
/* INCLUDE SECTION                                                           */

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.
#include <QPixmap>
#include <QSplashScreen>

#define USE_SPLASH_SCREEN 0

//
// System includes (sorted by alphabetic order)

//
// ITK includes (sorted by alphabetic order)
// #include "itksys/SystemTools.hxx"

//
// OTB includes (sorted by alphabetic order)

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdApplication.h"
#include "mvdMainWindow.h"

enum ERROR_CODE
{
  ERROR_CODE_I18N = -1,
  ERROR_CODE_CACHE_DIR = -2,
  ERROR_CODE_DATABASE = -3,
  ERROR_CODE_GL_VERSION = -4,
  ERROR_CODE_USAGE = -5,
};

/*****************************************************************************/
/* FUNCTIONS DECLARATION                                                     */

/*****************************************************************************/
/* MAIN                                                                      */

int
main( int argc, char* argv[] )
{
  QApplication qtApp( argc, argv );

  //
  // 0. Splash-screen.
#if !defined( _DEBUG ) && USE_SPLASH_SCREEN
  QPixmap pixmap(QLatin1String( ":/images/application_splash" ));
  QSplashScreen splash(pixmap);
  splash.show();
  qtApp.processEvents();//This is used to accept a click on the screen so that user can cancel the screen
#endif

  //
  // 0bis. Parse pre-initialization command-line arguments.
  QStringList args( qtApp.arguments() );
  {
  for( QStringList::iterator it( args.begin() );
       it!=args.end(); )
    if( it->compare( "-h" )==0 ||
	it->compare( "--help" )==0 )
      {
      std::cout
	<< mvd::ToStdString(
	  QCoreApplication::translate(
	    PROJECT_NAME,
	    "Usage: %1 [-h|--help] [-a|--applications] [<filename>...]\n"
	    "  -h, --help         display this help message.\n"
	    "  -a, --applications load OTB-applications from OTB_APPLICATIONS_PATH."
	  )
	  .arg( basename( argv[ 0 ] ) )
	)
	<< std::endl;

      return ERROR_CODE_USAGE;
      }
    else
      {
      ++ it;
      }
  }

  //
  // 1. Initialize application and sync settings.
  //
  // Coverity-14835
  // {
  mvd::Application * application = NULL;

  try
    {
    application = new mvd::Application( &qtApp );
    assert( application!=NULL );

    application->Initialize();
    }
  catch( std::exception & exc )
    {
    QMessageBox::StandardButton button =
      QMessageBox::question(
	NULL,
	QCoreApplication::translate(
	  PROJECT_NAME,
	  "Question!"
	),
	QCoreApplication::translate(
	  PROJECT_NAME,
	  "The following exception has been caught while initializing the software:\n\n"
	  "%1\n\n"
	  "The application may not function as expected. Do you want to continue?"
	)
	.arg( exc.what() ),
	QMessageBox::Yes | QMessageBox::No,
	QMessageBox::Yes
      );

    if( button==QMessageBox::No )
      return ERROR_CODE_I18N;
    }
  // }
  // Coverity-14835

  //
  // 2. Initialize main-window (UI).
  mvd::MainWindow mainWindow;
  mainWindow.Initialize();

  //
  // 3. Show window.
#if defined( _DEBUG )
  // Usefull when developping/debugging to avoid overlapping other windows.
  mainWindow.show();

#else // _DEBUG

#if USE_SPLASH_SCREEN
  splash.finish( &mainWindow );
#endif // USE_SPLASH_SCREEN

  // TODO: Correctly manage main-window state via application settings.
  mainWindow.showMaximized();

#endif // _DEBUG

  //
  // 4. Check OpenGL capabilities
  if( !mainWindow.CheckGLCapabilities() )
    return ERROR_CODE_GL_VERSION;

  //
  // 5. Parse command-line filenames.
  args.pop_front();
  {
  bool otbApplications = false;

  for( QStringList::iterator it( args.begin() );
       it!=args.end(); )
    if( it->compare( "-a" )==0 ||
	it->compare( "--applications" )==0 )
      {
      if( !otbApplications )
	{
#if USE_OTB_APPS
	mainWindow.SetupOTBApplications();
#else // USE_OTB_APPS
	qWarning() << "OTB-applications support is not included in this build.";
#endif // USE_OTB_APPS

	it = args.erase( it );
	}
      }
    else
      {
      ++ it;
      }
  }

  mainWindow.ImportImages( args );

  //
  // 6. Let's go: run the application and return exit code.
  int result = QCoreApplication::instance()->exec();

  /*
  application->CloseDatabase();
  */

  // Coverity-14835
  // {
  delete application;
  application = NULL;
  // }
  // Coverity-14835

  return result;
}

/*****************************************************************************/
/* FUNCTIONS IMPLEMENTATION                                                  */
/*****************************************************************************/
<commit_msg>COMP: Fixed call to basename() not available on Windows.<commit_after>/*=========================================================================

  Program:   Monteverdi
  Language:  C++

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See Copyright.txt for details.

  Monteverdi is distributed under the CeCILL licence version 2. See
  Licence_CeCILL_V2-en.txt or
  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt for more details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

//
// Configuration include.
//// Included at first position before any other ones.
#include "ConfigureMonteverdi.h"

/*****************************************************************************/
/* INCLUDE SECTION                                                           */

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.
#include <QPixmap>
#include <QSplashScreen>

#define USE_SPLASH_SCREEN 0

//
// System includes (sorted by alphabetic order)

//
// ITK includes (sorted by alphabetic order)
// #include "itksys/SystemTools.hxx"

//
// OTB includes (sorted by alphabetic order)

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdApplication.h"
#include "mvdMainWindow.h"

enum ERROR_CODE
{
  ERROR_CODE_I18N = -1,
  ERROR_CODE_CACHE_DIR = -2,
  ERROR_CODE_DATABASE = -3,
  ERROR_CODE_GL_VERSION = -4,
  ERROR_CODE_USAGE = -5,
};

/*****************************************************************************/
/* FUNCTIONS DECLARATION                                                     */

/*****************************************************************************/
/* MAIN                                                                      */

int
main( int argc, char* argv[] )
{
  QApplication qtApp( argc, argv );

  //
  // 0. Splash-screen.
#if !defined( _DEBUG ) && USE_SPLASH_SCREEN
  QPixmap pixmap(QLatin1String( ":/images/application_splash" ));
  QSplashScreen splash(pixmap);
  splash.show();
  qtApp.processEvents();//This is used to accept a click on the screen so that user can cancel the screen
#endif

  //
  // 0bis. Parse pre-initialization command-line arguments.
  QStringList args( qtApp.arguments() );
  {
  for( QStringList::iterator it( args.begin() );
       it!=args.end(); )
    if( it->compare( "-h" )==0 ||
	it->compare( "--help" )==0 )
      {
      std::cout
	<< mvd::ToStdString(
	  QCoreApplication::translate(
	    PROJECT_NAME,
	    "Usage: %1 [-h|--help] [-a|--applications] [<filename>...]\n"
	    "  -h, --help         display this help message.\n"
	    "  -a, --applications load OTB-applications from OTB_APPLICATIONS_PATH."
	  )
	  .arg( QFileInfo( argv[ 0 ] ).baseName() )
	)
	<< std::endl;

      return ERROR_CODE_USAGE;
      }
    else
      {
      ++ it;
      }
  }

  //
  // 1. Initialize application and sync settings.
  //
  // Coverity-14835
  // {
  mvd::Application * application = NULL;

  try
    {
    application = new mvd::Application( &qtApp );
    assert( application!=NULL );

    application->Initialize();
    }
  catch( std::exception & exc )
    {
    QMessageBox::StandardButton button =
      QMessageBox::question(
	NULL,
	QCoreApplication::translate(
	  PROJECT_NAME,
	  "Question!"
	),
	QCoreApplication::translate(
	  PROJECT_NAME,
	  "The following exception has been caught while initializing the software:\n\n"
	  "%1\n\n"
	  "The application may not function as expected. Do you want to continue?"
	)
	.arg( exc.what() ),
	QMessageBox::Yes | QMessageBox::No,
	QMessageBox::Yes
      );

    if( button==QMessageBox::No )
      return ERROR_CODE_I18N;
    }
  // }
  // Coverity-14835

  //
  // 2. Initialize main-window (UI).
  mvd::MainWindow mainWindow;
  mainWindow.Initialize();

  //
  // 3. Show window.
#if defined( _DEBUG )
  // Usefull when developping/debugging to avoid overlapping other windows.
  mainWindow.show();

#else // _DEBUG

#if USE_SPLASH_SCREEN
  splash.finish( &mainWindow );
#endif // USE_SPLASH_SCREEN

  // TODO: Correctly manage main-window state via application settings.
  mainWindow.showMaximized();

#endif // _DEBUG

  //
  // 4. Check OpenGL capabilities
  if( !mainWindow.CheckGLCapabilities() )
    return ERROR_CODE_GL_VERSION;

  //
  // 5. Parse command-line filenames.
  args.pop_front();
  {
  bool otbApplications = false;

  for( QStringList::iterator it( args.begin() );
       it!=args.end(); )
    if( it->compare( "-a" )==0 ||
	it->compare( "--applications" )==0 )
      {
      if( !otbApplications )
	{
#if USE_OTB_APPS
	mainWindow.SetupOTBApplications();
#else // USE_OTB_APPS
	qWarning() << "OTB-applications support is not included in this build.";
#endif // USE_OTB_APPS

	it = args.erase( it );
	}
      }
    else
      {
      ++ it;
      }
  }

  mainWindow.ImportImages( args );

  //
  // 6. Let's go: run the application and return exit code.
  int result = QCoreApplication::instance()->exec();

  /*
  application->CloseDatabase();
  */

  // Coverity-14835
  // {
  delete application;
  application = NULL;
  // }
  // Coverity-14835

  return result;
}

/*****************************************************************************/
/* FUNCTIONS IMPLEMENTATION                                                  */
/*****************************************************************************/
<|endoftext|>


<commit_before>#include "ics3/angle.hpp"

#define _USE_MATH_DEFINES
#include <cmath>

ics::Angle ics::Angle::newDegree() noexcept {
  static const double limit = 130;
  static const Angle DEGREE(-limit, limit, 800.0 / 27.0);
  return DEGREE;
}

ics::Angle ics::Angle::newRadian() noexcept {
  static const double limit = 130 * M_PI / 180;
  static const Angle RADIAN(-limit, limit, 16000.0 / 3.0 / M_PI);
  return RADIAN;
}

double ics::Angle::get() const noexcept {
  return data;
}

void ics::Angle::set(double angle) throw(std::invalid_argument) {
  if (angle < min) throw std::invalid_argument("Too small angle");
  if (max < angle) throw std::invalid_argument("Too big angle");
  data = angle;
}

uint16_t ics::Angle::getRaw() const noexcept {
  return static_cast<uint16_t>(data * rawCalibration + 7500);
}

void ics::Angle::setRaw(uint16_t raw) throw(std::invalid_argument) {
  try {
    set((raw - 7500) / rawCalibration);
  } catch (std::invalid_argument &e) {
    throw;
  }
}

ics::Angle::Angle(double min, double max, double calibration) noexcept
: min(min),
  max(max),
  rawCalibration(calibration),
  data(0)
{}
<commit_msg>Refactor place of reference operator<commit_after>#include "ics3/angle.hpp"

#define _USE_MATH_DEFINES
#include <cmath>

ics::Angle ics::Angle::newDegree() noexcept {
  static const double limit = 130;
  static const Angle DEGREE(-limit, limit, 800.0 / 27.0);
  return DEGREE;
}

ics::Angle ics::Angle::newRadian() noexcept {
  static const double limit = 130 * M_PI / 180;
  static const Angle RADIAN(-limit, limit, 16000.0 / 3.0 / M_PI);
  return RADIAN;
}

double ics::Angle::get() const noexcept {
  return data;
}

void ics::Angle::set(double angle) throw(std::invalid_argument) {
  if (angle < min) throw std::invalid_argument("Too small angle");
  if (max < angle) throw std::invalid_argument("Too big angle");
  data = angle;
}

uint16_t ics::Angle::getRaw() const noexcept {
  return static_cast<uint16_t>(data * rawCalibration + 7500);
}

void ics::Angle::setRaw(uint16_t raw) throw(std::invalid_argument) {
  try {
    set((raw - 7500) / rawCalibration);
  } catch (std::invalid_argument& e) {
    throw;
  }
}

ics::Angle::Angle(double min, double max, double calibration) noexcept
: min(min),
  max(max),
  rawCalibration(calibration),
  data(0)
{}
<|endoftext|>


<commit_before>/* listener.cc
   Mathieu Stefani, 12 August 2015

*/

#include <pistache/common.h>
#include <pistache/errors.h>
#include <pistache/listener.h>
#include <pistache/os.h>
#include <pistache/peer.h>
#include <pistache/transport.h>

#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/timerfd.h>
#include <sys/types.h>

#include <chrono>
#include <memory>
#include <vector>

#include <cerrno>
#include <signal.h>

#ifdef PISTACHE_USE_SSL

#include <openssl/err.h>
#include <openssl/ssl.h>

#endif /* PISTACHE_USE_SSL */

namespace Pistache {
namespace Tcp {

void setSocketOptions(Fd fd, Flags<Options> options) {
  if (options.hasFlag(Options::ReuseAddr)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)));
  }

  if (options.hasFlag(Options::ReusePort)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)));
  }

  if (options.hasFlag(Options::Linger)) {
    struct linger opt;
    opt.l_onoff = 1;
    opt.l_linger = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_LINGER, &opt, sizeof(opt)));
  }

  if (options.hasFlag(Options::FastOpen)) {
    int hint = 5;
    TRY(::setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &hint, sizeof(hint)));
  }
  if (options.hasFlag(Options::NoDelay)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)));
  }
}

Listener::Listener()
    : addr_(), listen_fd(-1), backlog_(Const::MaxBacklog), shutdownFd(),
      poller(), options_(), workers_(Const::DefaultWorkers), workersName_(),
      reactor_(), transportKey(), useSSL_(false), ssl_ctx_(nullptr) {}

Listener::Listener(const Address &address)
    : addr_(address), listen_fd(-1), backlog_(Const::MaxBacklog), shutdownFd(),
      poller(), options_(), workers_(Const::DefaultWorkers), workersName_(),
      reactor_(), transportKey(), useSSL_(false), ssl_ctx_(nullptr) {}

Listener::~Listener() {
  if (isBound())
    shutdown();
  if (acceptThread.joinable())
    acceptThread.join();

  if (listen_fd >= 0) {
    close(listen_fd);
    listen_fd = -1;
  }
#ifdef PISTACHE_USE_SSL
  if (this->useSSL_) {
    SSL_CTX_free((SSL_CTX *)this->ssl_ctx_);
    EVP_cleanup();
  }
#endif /* PISTACHE_USE_SSL */
}

void Listener::init(size_t workers, Flags<Options> options,
                    const std::string &workersName, int backlog) {
  if (workers > hardware_concurrency()) {
    // Log::warning() << "More workers than available cores"
  }

  options_ = options;
  backlog_ = backlog;
  useSSL_ = false;
  workers_ = workers;
  workersName_ = workersName;
}

void Listener::setHandler(const std::shared_ptr<Handler> &handler) {
  handler_ = handler;
}

void Listener::pinWorker(size_t worker, const CpuSet &set) {
  UNUSED(worker)
  UNUSED(set)
#if 0
    if (ioGroup.empty()) {
        throw std::domain_error("Invalid operation, did you call init() before ?");
    }
    if (worker > ioGroup.size()) {
        throw std::invalid_argument("Trying to pin invalid worker");
    }

    auto &wrk = ioGroup[worker];
    wrk->pin(set);
#endif
}

void Listener::bind() { bind(addr_); }

void Listener::bind(const Address &address) {
  if (!handler_)
    throw std::runtime_error("Call setHandler before calling bind()");
  addr_ = address;

  struct addrinfo hints;
  hints.ai_family = address.family();
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;
  hints.ai_protocol = 0;

  const auto &host = addr_.host();
  const auto &port = addr_.port().toString();
  AddrInfo addr_info;

  TRY(addr_info.invoke(host.c_str(), port.c_str(), &hints));

  int fd = -1;

  const addrinfo *addr = nullptr;
  for (addr = addr_info.get_info_ptr(); addr; addr = addr->ai_next) {
    fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    if (fd < 0)
      continue;

    setSocketOptions(fd, options_);

    if (::bind(fd, addr->ai_addr, addr->ai_addrlen) < 0) {
      close(fd);
      continue;
    }

    TRY(::listen(fd, backlog_));
    break;
  }

  // At this point, it is still possible that we couldn't bind any socket. If it
  // is the case, the previous loop would have exited naturally and addr will be
  // null.
  if (addr == nullptr) {
    throw std::runtime_error(strerror(errno));
  }

  make_non_blocking(fd);
  poller.addFd(fd, Flags<Polling::NotifyOn>(Polling::NotifyOn::Read),
               Polling::Tag(fd));
  listen_fd = fd;

  auto transport = std::make_shared<Transport>(handler_);

  reactor_.init(Aio::AsyncContext(workers_, workersName_));
  transportKey = reactor_.addHandler(transport);
}

bool Listener::isBound() const { return listen_fd != -1; }

// Return actual TCP port Listener is on, or 0 on error / no port.
// Notes:
// 1) Default constructor for 'Port()' sets value to 0.
// 2) Socket is created inside 'Listener::run()', which is called from
//    'Endpoint::serve()' and 'Endpoint::serveThreaded()'.  So getting the
//    port is only useful if you attempt to do so from a _different_ thread
//    than the one running 'Listener::run()'.  So for a traditional single-
//    threaded program this method is of little value.
Port Listener::getPort() const {
  if (listen_fd == -1) {
    return Port();
  }

  struct sockaddr_in sock_addr = {0};
  socklen_t addrlen = sizeof(sock_addr);
  auto sock_addr_alias = reinterpret_cast<struct sockaddr *>(&sock_addr);

  if (-1 == getsockname(listen_fd, sock_addr_alias, &addrlen)) {
    return Port();
  }

  return Port(ntohs(sock_addr.sin_port));
}

void Listener::run() {
  shutdownFd.bind(poller);
  reactor_.run();

  for (;;) {
    std::vector<Polling::Event> events;
    int ready_fds = poller.poll(events);

    if (ready_fds == -1) {
      throw Error::system("Polling");
    }
    for (const auto &event : events) {
      if (event.tag == shutdownFd.tag())
        return;

      if (event.flags.hasFlag(Polling::NotifyOn::Read)) {
        auto fd = event.tag.value();
        if (static_cast<ssize_t>(fd) == listen_fd) {
          try {
            handleNewConnection();
          } catch (SocketError &ex) {
            std::cerr << "Server: " << ex.what() << std::endl;
          } catch (ServerError &ex) {
            std::cerr << "Server: " << ex.what() << std::endl;
            throw;
          }
        }
      }
    }
  }
}

void Listener::runThreaded() {
  acceptThread = std::thread([=]() { this->run(); });
}

void Listener::shutdown() {
  if (shutdownFd.isBound())
    shutdownFd.notify();
  reactor_.shutdown();
}

Async::Promise<Listener::Load>
Listener::requestLoad(const Listener::Load &old) {
  auto handlers = reactor_.handlers(transportKey);

  std::vector<Async::Promise<rusage>> loads;
  for (const auto &handler : handlers) {
    auto transport = std::static_pointer_cast<Transport>(handler);
    loads.push_back(transport->load());
  }

  return Async::whenAll(std::begin(loads), std::end(loads))
      .then(
          [=](const std::vector<rusage> &usages) {
            Load res;
            res.raw = usages;

            if (old.raw.empty()) {
              res.global = 0.0;
              for (size_t i = 0; i < handlers.size(); ++i)
                res.workers.push_back(0.0);
            } else {

              auto totalElapsed = [](rusage usage) {
                return (usage.ru_stime.tv_sec * 1e6 + usage.ru_stime.tv_usec) +
                       (usage.ru_utime.tv_sec * 1e6 + usage.ru_utime.tv_usec);
              };

              auto now = std::chrono::system_clock::now();
              auto diff = now - old.tick;
              auto tick =
                  std::chrono::duration_cast<std::chrono::microseconds>(diff);
              res.tick = now;

              for (size_t i = 0; i < usages.size(); ++i) {
                auto last = old.raw[i];
                const auto &usage = usages[i];

                auto nowElapsed = totalElapsed(usage);
                auto timeElapsed = nowElapsed - totalElapsed(last);

                auto loadPct = (timeElapsed * 100.0) / tick.count();
                res.workers.push_back(loadPct);
                res.global += loadPct;
              }

              res.global /= usages.size();
            }

            return res;
          },
          Async::Throw);
}

Address Listener::address() const { return addr_; }

Options Listener::options() const { return options_; }

void Listener::handleNewConnection() {
  struct sockaddr_in peer_addr;
  int client_fd = acceptConnection(peer_addr);

#ifdef PISTACHE_USE_SSL
  SSL *ssl = nullptr;

  if (this->useSSL_) {

    ssl = SSL_new((SSL_CTX *)this->ssl_ctx_);
    if (ssl == NULL) {
      close(client_fd);
      throw std::runtime_error("Cannot create SSL connection");
    }

    SSL_set_fd(ssl, client_fd);
    SSL_set_accept_state(ssl);

    if (SSL_accept(ssl) <= 0) {
      ERR_print_errors_fp(stderr);
      SSL_free(ssl);
      close(client_fd);
      return;
    }
  }
#endif /* PISTACHE_USE_SSL */

  make_non_blocking(client_fd);

  std::shared_ptr<Peer> peer;
  if (this->useSSL_) {
    peer = Peer::CreateSSL(client_fd, Address::fromUnix(&peer_addr), ssl);
  } else {
    peer = Peer::Create(client_fd, Address::fromUnix(&peer_addr));
  }

  dispatchPeer(peer);
}

int Listener::acceptConnection(struct sockaddr_in &peer_addr) const {
  socklen_t peer_addr_len = sizeof(peer_addr);
  int client_fd =
      ::accept(listen_fd, (struct sockaddr *)&peer_addr, &peer_addr_len);
  if (client_fd < 0) {
    if (errno == EBADF || errno == ENOTSOCK)
      throw ServerError(strerror(errno));
    else
      throw SocketError(strerror(errno));
  }
  return client_fd;
}

void Listener::dispatchPeer(const std::shared_ptr<Peer> &peer) {
  auto handlers = reactor_.handlers(transportKey);
  auto idx = peer->fd() % handlers.size();
  auto transport = std::static_pointer_cast<Transport>(handlers[idx]);

  transport->handleNewPeer(peer);
}

#ifdef PISTACHE_USE_SSL

static SSL_CTX *ssl_create_context(const std::string &cert,
                                   const std::string &key,
                                   bool use_compression) {
  const SSL_METHOD *method;
  SSL_CTX *ctx;

  method = SSLv23_server_method();

  ctx = SSL_CTX_new(method);
  if (ctx == NULL) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot setup SSL context");
  }

  if (!use_compression) {
    /* Disable compression to prevent BREACH and CRIME vulnerabilities. */
    if (!SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION)) {
      ERR_print_errors_fp(stderr);
      throw std::runtime_error("Cannot disable compression");
    }
  }

  /* Function introduced in 1.0.2 */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
  SSL_CTX_set_ecdh_auto(ctx, 1);
#endif /* OPENSSL_VERSION_NUMBER */

  if (SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot load SSL certificate");
  }

  if (SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot load SSL private key");
  }

  if (!SSL_CTX_check_private_key(ctx)) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error(
        "Private key does not match public key in the certificate");
  }

  return ctx;
}

void Listener::setupSSLAuth(const std::string &ca_file,
                            const std::string &ca_path,
                            int (*cb)(int, void *) = NULL) {
  const char *__ca_file = NULL;
  const char *__ca_path = NULL;

  if (this->ssl_ctx_ == NULL)
    throw std::runtime_error("SSL Context is not initialized");

  if (!ca_file.empty())
    __ca_file = ca_file.c_str();
  if (!ca_path.empty())
    __ca_path = ca_path.c_str();

  if (SSL_CTX_load_verify_locations((SSL_CTX *)this->ssl_ctx_, __ca_file,
                                    __ca_path) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot verify SSL locations");
  }

  SSL_CTX_set_verify((SSL_CTX *)this->ssl_ctx_,
                     SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
                         SSL_VERIFY_CLIENT_ONCE,
  /* Callback type did change in 1.0.1 */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
                     (int (*)(int, X509_STORE_CTX *))cb
#else
                     (SSL_verify_cb)cb
#endif /* OPENSSL_VERSION_NUMBER */
  );
}

void Listener::setupSSL(const std::string &cert_path,
                        const std::string &key_path, bool use_compression) {
  SSL_load_error_strings();
  OpenSSL_add_ssl_algorithms();

  this->ssl_ctx_ = ssl_create_context(cert_path, key_path, use_compression);
  this->useSSL_ = true;
}

#endif /* PISTACHE_USE_SSL */

} // namespace Tcp
} // namespace Pistache
<commit_msg>Issue #700: fix build<commit_after>/* listener.cc
   Mathieu Stefani, 12 August 2015

*/

#include <pistache/common.h>
#include <pistache/errors.h>
#include <pistache/listener.h>
#include <pistache/os.h>
#include <pistache/peer.h>
#include <pistache/transport.h>

#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/timerfd.h>
#include <sys/types.h>

#include <chrono>
#include <memory>
#include <vector>

#include <cerrno>
#include <signal.h>

#ifdef PISTACHE_USE_SSL

#include <openssl/err.h>
#include <openssl/ssl.h>

#endif /* PISTACHE_USE_SSL */

namespace Pistache {
namespace Tcp {

void setSocketOptions(Fd fd, Flags<Options> options) {
  if (options.hasFlag(Options::ReuseAddr)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)));
  }

  if (options.hasFlag(Options::ReusePort)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)));
  }

  if (options.hasFlag(Options::Linger)) {
    struct linger opt;
    opt.l_onoff = 1;
    opt.l_linger = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_LINGER, &opt, sizeof(opt)));
  }

  if (options.hasFlag(Options::FastOpen)) {
    int hint = 5;
    TRY(::setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &hint, sizeof(hint)));
  }
  if (options.hasFlag(Options::NoDelay)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)));
  }
}

Listener::Listener()
    : addr_(), listen_fd(-1), backlog_(Const::MaxBacklog), shutdownFd(),
      poller(), options_(), workers_(Const::DefaultWorkers), workersName_(),
      reactor_(), transportKey(), useSSL_(false), ssl_ctx_(nullptr) {}

Listener::Listener(const Address &address)
    : addr_(address), listen_fd(-1), backlog_(Const::MaxBacklog), shutdownFd(),
      poller(), options_(), workers_(Const::DefaultWorkers), workersName_(),
      reactor_(), transportKey(), useSSL_(false), ssl_ctx_(nullptr) {}

Listener::~Listener() {
  if (isBound())
    shutdown();
  if (acceptThread.joinable())
    acceptThread.join();

  if (listen_fd >= 0) {
    close(listen_fd);
    listen_fd = -1;
  }
#ifdef PISTACHE_USE_SSL
  if (this->useSSL_) {
    SSL_CTX_free((SSL_CTX *)this->ssl_ctx_);
    EVP_cleanup();
  }
#endif /* PISTACHE_USE_SSL */
}

void Listener::init(size_t workers, Flags<Options> options,
                    const std::string &workersName, int backlog) {
  if (workers > hardware_concurrency()) {
    // Log::warning() << "More workers than available cores"
  }

  options_ = options;
  backlog_ = backlog;
  useSSL_ = false;
  workers_ = workers;
  workersName_ = workersName;
}

void Listener::setHandler(const std::shared_ptr<Handler> &handler) {
  handler_ = handler;
}

void Listener::pinWorker(size_t worker, const CpuSet &set) {
  UNUSED(worker)
  UNUSED(set)
#if 0
    if (ioGroup.empty()) {
        throw std::domain_error("Invalid operation, did you call init() before ?");
    }
    if (worker > ioGroup.size()) {
        throw std::invalid_argument("Trying to pin invalid worker");
    }

    auto &wrk = ioGroup[worker];
    wrk->pin(set);
#endif
}

void Listener::bind() { bind(addr_); }

void Listener::bind(const Address &address) {
  if (!handler_)
    throw std::runtime_error("Call setHandler before calling bind()");
  addr_ = address;

  struct addrinfo hints;
  hints.ai_family = address.family();
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;
  hints.ai_protocol = 0;

  const auto &host = addr_.host();
  const auto &port = addr_.port().toString();
  AddrInfo addr_info;

  TRY(addr_info.invoke(host.c_str(), port.c_str(), &hints));

  int fd = -1;

  const addrinfo *addr = nullptr;
  for (addr = addr_info.get_info_ptr(); addr; addr = addr->ai_next) {
    fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    if (fd < 0)
      continue;

    setSocketOptions(fd, options_);

    if (::bind(fd, addr->ai_addr, addr->ai_addrlen) < 0) {
      close(fd);
      continue;
    }

    TRY(::listen(fd, backlog_));
    break;
  }

  // At this point, it is still possible that we couldn't bind any socket. If it
  // is the case, the previous loop would have exited naturally and addr will be
  // null.
  if (addr == nullptr) {
    throw std::runtime_error(strerror(errno));
  }

  make_non_blocking(fd);
  poller.addFd(fd, Flags<Polling::NotifyOn>(Polling::NotifyOn::Read),
               Polling::Tag(fd));
  listen_fd = fd;

  auto transport = std::make_shared<Transport>(handler_);

  reactor_.init(Aio::AsyncContext(workers_, workersName_));
  transportKey = reactor_.addHandler(transport);
}

bool Listener::isBound() const { return listen_fd != -1; }

// Return actual TCP port Listener is on, or 0 on error / no port.
// Notes:
// 1) Default constructor for 'Port()' sets value to 0.
// 2) Socket is created inside 'Listener::run()', which is called from
//    'Endpoint::serve()' and 'Endpoint::serveThreaded()'.  So getting the
//    port is only useful if you attempt to do so from a _different_ thread
//    than the one running 'Listener::run()'.  So for a traditional single-
//    threaded program this method is of little value.
Port Listener::getPort() const {
  if (listen_fd == -1) {
    return Port();
  }

  struct sockaddr_in sock_addr = {0};
  socklen_t addrlen = sizeof(sock_addr);
  auto sock_addr_alias = reinterpret_cast<struct sockaddr *>(&sock_addr);

  if (-1 == getsockname(listen_fd, sock_addr_alias, &addrlen)) {
    return Port();
  }

  return Port(ntohs(sock_addr.sin_port));
}

void Listener::run() {
  shutdownFd.bind(poller);
  reactor_.run();

  for (;;) {
    std::vector<Polling::Event> events;
    int ready_fds = poller.poll(events);

    if (ready_fds == -1) {
      throw Error::system("Polling");
    }
    for (const auto &event : events) {
      if (event.tag == shutdownFd.tag())
        return;

      if (event.flags.hasFlag(Polling::NotifyOn::Read)) {
        auto fd = event.tag.value();
        if (static_cast<ssize_t>(fd) == listen_fd) {
          try {
            handleNewConnection();
          } catch (SocketError &ex) {
            std::cerr << "Server: " << ex.what() << std::endl;
          } catch (ServerError &ex) {
            std::cerr << "Server: " << ex.what() << std::endl;
            throw;
          }
        }
      }
    }
  }
}

void Listener::runThreaded() {
  acceptThread = std::thread([=]() { this->run(); });
}

void Listener::shutdown() {
  if (shutdownFd.isBound())
    shutdownFd.notify();
  reactor_.shutdown();
}

Async::Promise<Listener::Load>
Listener::requestLoad(const Listener::Load &old) {
  auto handlers = reactor_.handlers(transportKey);

  std::vector<Async::Promise<rusage>> loads;
  for (const auto &handler : handlers) {
    auto transport = std::static_pointer_cast<Transport>(handler);
    loads.push_back(transport->load());
  }

  return Async::whenAll(std::begin(loads), std::end(loads))
      .then(
          [=](const std::vector<rusage> &usages) {
            Load res;
            res.raw = usages;

            if (old.raw.empty()) {
              res.global = 0.0;
              for (size_t i = 0; i < handlers.size(); ++i)
                res.workers.push_back(0.0);
            } else {

              auto totalElapsed = [](rusage usage) {
                return (usage.ru_stime.tv_sec * 1e6 + usage.ru_stime.tv_usec) +
                       (usage.ru_utime.tv_sec * 1e6 + usage.ru_utime.tv_usec);
              };

              auto now = std::chrono::system_clock::now();
              auto diff = now - old.tick;
              auto tick =
                  std::chrono::duration_cast<std::chrono::microseconds>(diff);
              res.tick = now;

              for (size_t i = 0; i < usages.size(); ++i) {
                auto last = old.raw[i];
                const auto &usage = usages[i];

                auto nowElapsed = totalElapsed(usage);
                auto timeElapsed = nowElapsed - totalElapsed(last);

                auto loadPct = (timeElapsed * 100.0) / tick.count();
                res.workers.push_back(loadPct);
                res.global += loadPct;
              }

              res.global /= usages.size();
            }

            return res;
          },
          Async::Throw);
}

Address Listener::address() const { return addr_; }

Options Listener::options() const { return options_; }

void Listener::handleNewConnection() {
  struct sockaddr_in peer_addr;
  int client_fd = acceptConnection(peer_addr);

  void *ssl = nullptr;

#ifdef PISTACHE_USE_SSL
  if (this->useSSL_) {

    ssl = SSL_new((SSL_CTX *)this->ssl_ctx_);
    if (ssl == NULL) {
      close(client_fd);
      throw std::runtime_error("Cannot create SSL connection");
    }

    SSL_set_fd(ssl, client_fd);
    SSL_set_accept_state(ssl);

    if (SSL_accept(ssl) <= 0) {
      ERR_print_errors_fp(stderr);
      SSL_free(ssl);
      close(client_fd);
      return;
    }
  }
#endif /* PISTACHE_USE_SSL */

  make_non_blocking(client_fd);

  std::shared_ptr<Peer> peer;
  if (this->useSSL_) {
    peer = Peer::CreateSSL(client_fd, Address::fromUnix(&peer_addr), ssl);
  } else {
    peer = Peer::Create(client_fd, Address::fromUnix(&peer_addr));
  }

  dispatchPeer(peer);
}

int Listener::acceptConnection(struct sockaddr_in &peer_addr) const {
  socklen_t peer_addr_len = sizeof(peer_addr);
  int client_fd =
      ::accept(listen_fd, (struct sockaddr *)&peer_addr, &peer_addr_len);
  if (client_fd < 0) {
    if (errno == EBADF || errno == ENOTSOCK)
      throw ServerError(strerror(errno));
    else
      throw SocketError(strerror(errno));
  }
  return client_fd;
}

void Listener::dispatchPeer(const std::shared_ptr<Peer> &peer) {
  auto handlers = reactor_.handlers(transportKey);
  auto idx = peer->fd() % handlers.size();
  auto transport = std::static_pointer_cast<Transport>(handlers[idx]);

  transport->handleNewPeer(peer);
}

#ifdef PISTACHE_USE_SSL

static SSL_CTX *ssl_create_context(const std::string &cert,
                                   const std::string &key,
                                   bool use_compression) {
  const SSL_METHOD *method;
  SSL_CTX *ctx;

  method = SSLv23_server_method();

  ctx = SSL_CTX_new(method);
  if (ctx == NULL) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot setup SSL context");
  }

  if (!use_compression) {
    /* Disable compression to prevent BREACH and CRIME vulnerabilities. */
    if (!SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION)) {
      ERR_print_errors_fp(stderr);
      throw std::runtime_error("Cannot disable compression");
    }
  }

  /* Function introduced in 1.0.2 */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
  SSL_CTX_set_ecdh_auto(ctx, 1);
#endif /* OPENSSL_VERSION_NUMBER */

  if (SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot load SSL certificate");
  }

  if (SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot load SSL private key");
  }

  if (!SSL_CTX_check_private_key(ctx)) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error(
        "Private key does not match public key in the certificate");
  }

  return ctx;
}

void Listener::setupSSLAuth(const std::string &ca_file,
                            const std::string &ca_path,
                            int (*cb)(int, void *) = NULL) {
  const char *__ca_file = NULL;
  const char *__ca_path = NULL;

  if (this->ssl_ctx_ == NULL)
    throw std::runtime_error("SSL Context is not initialized");

  if (!ca_file.empty())
    __ca_file = ca_file.c_str();
  if (!ca_path.empty())
    __ca_path = ca_path.c_str();

  if (SSL_CTX_load_verify_locations((SSL_CTX *)this->ssl_ctx_, __ca_file,
                                    __ca_path) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot verify SSL locations");
  }

  SSL_CTX_set_verify((SSL_CTX *)this->ssl_ctx_,
                     SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
                         SSL_VERIFY_CLIENT_ONCE,
  /* Callback type did change in 1.0.1 */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
                     (int (*)(int, X509_STORE_CTX *))cb
#else
                     (SSL_verify_cb)cb
#endif /* OPENSSL_VERSION_NUMBER */
  );
}

void Listener::setupSSL(const std::string &cert_path,
                        const std::string &key_path, bool use_compression) {
  SSL_load_error_strings();
  OpenSSL_add_ssl_algorithms();

  this->ssl_ctx_ = ssl_create_context(cert_path, key_path, use_compression);
  this->useSSL_ = true;
}

#endif /* PISTACHE_USE_SSL */

} // namespace Tcp
} // namespace Pistache
<|endoftext|>


<commit_before>/* listener.cc
   Mathieu Stefani, 12 August 2015

*/

#include <pistache/listener.h>
#include <pistache/peer.h>
#include <pistache/common.h>
#include <pistache/os.h>
#include <pistache/transport.h>
#include <pistache/errors.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>

#include <chrono>
#include <memory>
#include <vector>

#include <cerrno>
#include <signal.h>

#ifdef PISTACHE_USE_SSL

#include <openssl/ssl.h>
#include <openssl/err.h>

#endif /* PISTACHE_USE_SSL */

namespace Pistache {
namespace Tcp {

void setSocketOptions(Fd fd, Flags<Options> options) {
    if (options.hasFlag(Options::ReuseAddr)) {
        int one = 1;
        TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one)));
    }

    if (options.hasFlag(Options::Linger)) {
        struct linger opt;
        opt.l_onoff = 1;
        opt.l_linger = 1;
        TRY(::setsockopt(fd, SOL_SOCKET, SO_LINGER, &opt, sizeof (opt)));
    }

    if (options.hasFlag(Options::FastOpen)) {
        int hint = 5;
        TRY(::setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &hint, sizeof (hint)));
    }
    if (options.hasFlag(Options::NoDelay)) {
        int one = 1;
        TRY(::setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof (one)));
    }

}

Listener::Listener()
    : addr_()
    , listen_fd(-1)
    , backlog_(Const::MaxBacklog)
    , shutdownFd()
    , poller()
    , options_()
    , workers_(Const::DefaultWorkers)
    , workersName_()
    , reactor_()
    , transportKey()
    , useSSL_(false)
{ }

Listener::Listener(const Address& address)
    : addr_(address)
    , listen_fd(-1)
    , backlog_(Const::MaxBacklog)
    , shutdownFd()
    , poller()
    , options_()
    , workers_(Const::DefaultWorkers)
    , workersName_()
    , reactor_()
    , transportKey()
    , useSSL_(false)
{
}

Listener::~Listener() {
    if (isBound())
        shutdown();
    if (acceptThread.joinable())
        acceptThread.join();
#ifdef PISTACHE_USE_SSL
    if (this->useSSL_)
    {
        SSL_CTX_free((SSL_CTX *)this->ssl_ctx_);
        EVP_cleanup();
    }
#endif /* PISTACHE_USE_SSL */
}

void
Listener::init(
    size_t workers,
    Flags<Options> options,
    std::string workersName,
    int backlog)
{
    if (workers > hardware_concurrency()) {
        // Log::warning() << "More workers than available cores"
    }

    options_ = options;
    backlog_ = backlog;
    useSSL_ = false;
    workers_ = workers;
    workersName_ = workersName;

}

void
Listener::setHandler(const std::shared_ptr<Handler>& handler) {
    handler_ = handler;
}

void
Listener::pinWorker(size_t worker, const CpuSet& set)
{
    UNUSED(worker)
    UNUSED(set)
#if 0
    if (ioGroup.empty()) {
        throw std::domain_error("Invalid operation, did you call init() before ?");
    }
    if (worker > ioGroup.size()) {
        throw std::invalid_argument("Trying to pin invalid worker");
    }

    auto &wrk = ioGroup[worker];
    wrk->pin(set);
#endif
}

void
Listener::bind() {
    bind(addr_);
}

void
Listener::bind(const Address& address) {
    if (!handler_)
        throw std::runtime_error("Call setHandler before calling bind()");
    addr_ = address;

    struct addrinfo hints;
    hints.ai_family = address.family();
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    hints.ai_protocol = 0;

    const auto& host = addr_.host();
    const auto& port = addr_.port().toString();
    AddrInfo addr_info;

    TRY(addr_info.invoke(host.c_str(), port.c_str(), &hints));

    int fd = -1;

    const addrinfo * addr = nullptr;
    for (addr = addr_info.get_info_ptr(); addr; addr = addr->ai_next) {
        fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (fd < 0) continue;

        setSocketOptions(fd, options_);

        if (::bind(fd, addr->ai_addr, addr->ai_addrlen) < 0) {
            close(fd);
            continue;
        }

        TRY(::listen(fd, backlog_));
        break;
    }

    // At this point, it is still possible that we couldn't bind any socket. If it is the case, the previous
    // loop would have exited naturally and addr will be null.
    if (addr == nullptr) {
        throw std::runtime_error(strerror(errno));
    }

    make_non_blocking(fd);
    poller.addFd(fd, Polling::NotifyOn::Read, Polling::Tag(fd));
    listen_fd = fd;

    auto transport = std::make_shared<Transport>(handler_);

    reactor_.init(Aio::AsyncContext(workers_, workersName_));
    transportKey = reactor_.addHandler(transport);
}

bool
Listener::isBound() const {
    return listen_fd != -1;
}

// Return actual TCP port Listener is on, or 0 on error / no port.
// Notes:
// 1) Default constructor for 'Port()' sets value to 0.
// 2) Socket is created inside 'Listener::run()', which is called from
//    'Endpoint::serve()' and 'Endpoint::serveThreaded()'.  So getting the
//    port is only useful if you attempt to do so from a _different_ thread
//    than the one running 'Listener::run()'.  So for a traditional single-
//    threaded program this method is of little value.
Port
Listener::getPort() const {
    if (listen_fd == -1) {
        return Port();
    }

    struct sockaddr_in sock_addr = {0};
    socklen_t addrlen = sizeof(sock_addr);
    auto sock_addr_alias = reinterpret_cast<struct sockaddr*>(&sock_addr);

    if (-1 == getsockname(listen_fd, sock_addr_alias, &addrlen)) {
        return Port();
    }

    return Port(ntohs(sock_addr.sin_port));
}

void
Listener::run() {
    shutdownFd.bind(poller);
    reactor_.run();

    for (;;) {
        std::vector<Polling::Event> events;
        int ready_fds = poller.poll(events);

        if (ready_fds == -1) {
            throw Error::system("Polling");
        }
        for (const auto& event: events) {
            if (event.tag == shutdownFd.tag())
                return;

            if (event.flags.hasFlag(Polling::NotifyOn::Read)) {
                auto fd = event.tag.value();
                if (static_cast<ssize_t>(fd) == listen_fd) {
                    try {
                        handleNewConnection();
                    }
                    catch (SocketError& ex) {
                        std::cerr << "Server: " << ex.what() << std::endl;
                    }
                    catch (ServerError& ex) {
                        std::cerr << "Server: " << ex.what() << std::endl;
                        throw;
                    }
                }
            }
        }
    }
}

void
Listener::runThreaded() {
    acceptThread = std::thread([=]() { this->run(); });
}

void
Listener::shutdown() {
    if (shutdownFd.isBound()) shutdownFd.notify();
    reactor_.shutdown();
}

Async::Promise<Listener::Load>
Listener::requestLoad(const Listener::Load& old) {
    auto handlers = reactor_.handlers(transportKey);

    std::vector<Async::Promise<rusage>> loads;
    for (const auto& handler: handlers) {
        auto transport = std::static_pointer_cast<Transport>(handler);
        loads.push_back(transport->load());
    }

    return Async::whenAll(std::begin(loads), std::end(loads)).then([=](const std::vector<rusage>& usages) {

        Load res;
        res.raw = usages;

        if (old.raw.empty()) {
            res.global = 0.0;
            for (size_t i = 0; i < handlers.size(); ++i) res.workers.push_back(0.0);
        } else {

            auto totalElapsed = [](rusage usage) {
                return (usage.ru_stime.tv_sec * 1e6 + usage.ru_stime.tv_usec)
                     + (usage.ru_utime.tv_sec * 1e6 + usage.ru_utime.tv_usec);
            };

            auto now = std::chrono::system_clock::now();
            auto diff = now - old.tick;
            auto tick = std::chrono::duration_cast<std::chrono::microseconds>(diff);
            res.tick = now;

            for (size_t i = 0; i < usages.size(); ++i) {
                auto last = old.raw[i];
                const auto& usage = usages[i];

                auto nowElapsed = totalElapsed(usage);
                auto timeElapsed = nowElapsed - totalElapsed(last);

                auto loadPct = (timeElapsed * 100.0) / tick.count();
                res.workers.push_back(loadPct);
                res.global += loadPct;

            }

            res.global /= usages.size();
        }

        return res;

     }, Async::Throw);
}

Address
Listener::address() const {
    return addr_;
}

Options
Listener::options() const {
    return options_;
}

void Listener::handleNewConnection()
{
    struct sockaddr_in peer_addr;
    int client_fd = acceptConnection(peer_addr);

#ifdef PISTACHE_USE_SSL
    SSL *ssl = nullptr;

    if (this->useSSL_) {

        ssl = SSL_new((SSL_CTX *)this->ssl_ctx_);
        if (ssl == NULL)
            throw std::runtime_error("Cannot create SSL connection");

        SSL_set_fd(ssl, client_fd);
        SSL_set_accept_state(ssl);

        if (SSL_accept(ssl) <= 0) {
            ERR_print_errors_fp(stderr);
            SSL_free(ssl);
            close(client_fd);
            return ;
        }
    }
#endif /* PISTACHE_USE_SSL */

    make_non_blocking(client_fd);

    auto peer = std::make_shared<Peer>(Address::fromUnix((struct sockaddr *)&peer_addr));
    peer->associateFd(client_fd);

#ifdef PISTACHE_USE_SSL
    if (this->useSSL_)
        peer->associateSSL(ssl);
#endif /* PISTACHE_USE_SSL */

    dispatchPeer(peer);
}

int Listener::acceptConnection(struct sockaddr_in& peer_addr) const
{
    socklen_t peer_addr_len = sizeof(peer_addr);
    int client_fd = ::accept(listen_fd, (struct sockaddr *)&peer_addr, &peer_addr_len);
    if (client_fd < 0) {
        if (errno == EBADF || errno == ENOTSOCK)
            throw ServerError(strerror(errno));
        else
            throw SocketError(strerror(errno));
    }
    return client_fd;
}

void
Listener::dispatchPeer(const std::shared_ptr<Peer>& peer) {
    auto handlers = reactor_.handlers(transportKey);
    auto idx = peer->fd() % handlers.size();
    auto transport = std::static_pointer_cast<Transport>(handlers[idx]);

    transport->handleNewPeer(peer);

}

#ifdef PISTACHE_USE_SSL

static SSL_CTX *ssl_create_context(const std::string &cert, const std::string &key, bool use_compression)
{
    const SSL_METHOD    *method;
    SSL_CTX             *ctx;

    method = SSLv23_server_method();

    ctx = SSL_CTX_new(method);
    if (ctx == NULL) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Cannot setup SSL context");
    }

    if (!use_compression) {
        /* Disable compression to prevent BREACH and CRIME vulnerabilities. */
        if (!SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION)) {
            ERR_print_errors_fp(stderr);
            throw std::runtime_error("Cannot disable compression");
        }
    }

    /* Function introduced in 1.0.2 */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
    SSL_CTX_set_ecdh_auto(ctx, 1);
#endif /* OPENSSL_VERSION_NUMBER */

    if (SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Cannot load SSL certificate");
    }

    if (SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Cannot load SSL private key");
    }

    if (!SSL_CTX_check_private_key(ctx)) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Private key does not match public key in the certificate");
    }

    return ctx;
}

void
Listener::setupSSLAuth(const std::string &ca_file, const std::string &ca_path, int (*cb)(int, void *) = NULL)
{
    const char *__ca_file = NULL;
    const char *__ca_path = NULL;

    if (this->ssl_ctx_ == NULL)
        throw std::runtime_error("SSL Context is not initialized");

    if (!ca_file.empty())
        __ca_file = ca_file.c_str();
    if (!ca_path.empty())
        __ca_path = ca_path.c_str();

    if (SSL_CTX_load_verify_locations((SSL_CTX *)this->ssl_ctx_, __ca_file, __ca_path) <= 0) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Cannot verify SSL locations");
    }

    SSL_CTX_set_verify((SSL_CTX *)this->ssl_ctx_,
        SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE,
        /* Callback type did change in 1.0.1 */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
        (int (*)(int, X509_STORE_CTX *))cb
#else
        (SSL_verify_cb)cb
#endif /* OPENSSL_VERSION_NUMBER */
    );
}

void
Listener::setupSSL(const std::string &cert_path, const std::string &key_path, bool use_compression)
{
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();

    this->ssl_ctx_ = ssl_create_context(cert_path, key_path, use_compression);
    this->useSSL_ = true;
}

#endif /* PISTACHE_USE_SSL */

} // namespace Tcp
} // namespace Pistache
<commit_msg>Update listener.cc<commit_after>/* listener.cc
   Mathieu Stefani, 12 August 2015

*/

#include <pistache/listener.h>
#include <pistache/peer.h>
#include <pistache/common.h>
#include <pistache/os.h>
#include <pistache/transport.h>
#include <pistache/errors.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>

#include <chrono>
#include <memory>
#include <vector>

#include <cerrno>
#include <signal.h>

#ifdef PISTACHE_USE_SSL

#include <openssl/ssl.h>
#include <openssl/err.h>

#endif /* PISTACHE_USE_SSL */

namespace Pistache {
namespace Tcp {

void setSocketOptions(Fd fd, Flags<Options> options) {
    if (options.hasFlag(Options::ReuseAddr)) {
        int one = 1;
        TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one)));
    }

    if (options.hasFlag(Options::Linger)) {
        struct linger opt;
        opt.l_onoff = 1;
        opt.l_linger = 1;
        TRY(::setsockopt(fd, SOL_SOCKET, SO_LINGER, &opt, sizeof (opt)));
    }

    if (options.hasFlag(Options::FastOpen)) {
        int hint = 5;
        TRY(::setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &hint, sizeof (hint)));
    }
    if (options.hasFlag(Options::NoDelay)) {
        int one = 1;
        TRY(::setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof (one)));
    }

}

Listener::Listener()
    : addr_()
    , listen_fd(-1)
    , backlog_(Const::MaxBacklog)
    , shutdownFd()
    , poller()
    , options_()
    , workers_(Const::DefaultWorkers)
    , workersName_()
    , reactor_()
    , transportKey()
    , useSSL_(false)
{ }

Listener::Listener(const Address& address)
    : addr_(address)
    , listen_fd(-1)
    , backlog_(Const::MaxBacklog)
    , shutdownFd()
    , poller()
    , options_()
    , workers_(Const::DefaultWorkers)
    , workersName_()
    , reactor_()
    , transportKey()
    , useSSL_(false)
{
}

Listener::~Listener() {
    if (isBound())
        shutdown();
    if (acceptThread.joinable())
        acceptThread.join();
#ifdef PISTACHE_USE_SSL
    if (this->useSSL_)
    {
        SSL_CTX_free((SSL_CTX *)this->ssl_ctx_);
        EVP_cleanup();
    }
#endif /* PISTACHE_USE_SSL */
}

void
Listener::init(
    size_t workers,
    Flags<Options> options,
    const std::string& workersName,
    int backlog)
{
    if (workers > hardware_concurrency()) {
        // Log::warning() << "More workers than available cores"
    }

    options_ = options;
    backlog_ = backlog;
    useSSL_ = false;
    workers_ = workers;
    workersName_ = workersName;

}

void
Listener::setHandler(const std::shared_ptr<Handler>& handler) {
    handler_ = handler;
}

void
Listener::pinWorker(size_t worker, const CpuSet& set)
{
    UNUSED(worker)
    UNUSED(set)
#if 0
    if (ioGroup.empty()) {
        throw std::domain_error("Invalid operation, did you call init() before ?");
    }
    if (worker > ioGroup.size()) {
        throw std::invalid_argument("Trying to pin invalid worker");
    }

    auto &wrk = ioGroup[worker];
    wrk->pin(set);
#endif
}

void
Listener::bind() {
    bind(addr_);
}

void
Listener::bind(const Address& address) {
    if (!handler_)
        throw std::runtime_error("Call setHandler before calling bind()");
    addr_ = address;

    struct addrinfo hints;
    hints.ai_family = address.family();
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    hints.ai_protocol = 0;

    const auto& host = addr_.host();
    const auto& port = addr_.port().toString();
    AddrInfo addr_info;

    TRY(addr_info.invoke(host.c_str(), port.c_str(), &hints));

    int fd = -1;

    const addrinfo * addr = nullptr;
    for (addr = addr_info.get_info_ptr(); addr; addr = addr->ai_next) {
        fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (fd < 0) continue;

        setSocketOptions(fd, options_);

        if (::bind(fd, addr->ai_addr, addr->ai_addrlen) < 0) {
            close(fd);
            continue;
        }

        TRY(::listen(fd, backlog_));
        break;
    }

    // At this point, it is still possible that we couldn't bind any socket. If it is the case, the previous
    // loop would have exited naturally and addr will be null.
    if (addr == nullptr) {
        throw std::runtime_error(strerror(errno));
    }

    make_non_blocking(fd);
    poller.addFd(fd, Polling::NotifyOn::Read, Polling::Tag(fd));
    listen_fd = fd;

    auto transport = std::make_shared<Transport>(handler_);

    reactor_.init(Aio::AsyncContext(workers_, workersName_));
    transportKey = reactor_.addHandler(transport);
}

bool
Listener::isBound() const {
    return listen_fd != -1;
}

// Return actual TCP port Listener is on, or 0 on error / no port.
// Notes:
// 1) Default constructor for 'Port()' sets value to 0.
// 2) Socket is created inside 'Listener::run()', which is called from
//    'Endpoint::serve()' and 'Endpoint::serveThreaded()'.  So getting the
//    port is only useful if you attempt to do so from a _different_ thread
//    than the one running 'Listener::run()'.  So for a traditional single-
//    threaded program this method is of little value.
Port
Listener::getPort() const {
    if (listen_fd == -1) {
        return Port();
    }

    struct sockaddr_in sock_addr = {0};
    socklen_t addrlen = sizeof(sock_addr);
    auto sock_addr_alias = reinterpret_cast<struct sockaddr*>(&sock_addr);

    if (-1 == getsockname(listen_fd, sock_addr_alias, &addrlen)) {
        return Port();
    }

    return Port(ntohs(sock_addr.sin_port));
}

void
Listener::run() {
    shutdownFd.bind(poller);
    reactor_.run();

    for (;;) {
        std::vector<Polling::Event> events;
        int ready_fds = poller.poll(events);

        if (ready_fds == -1) {
            throw Error::system("Polling");
        }
        for (const auto& event: events) {
            if (event.tag == shutdownFd.tag())
                return;

            if (event.flags.hasFlag(Polling::NotifyOn::Read)) {
                auto fd = event.tag.value();
                if (static_cast<ssize_t>(fd) == listen_fd) {
                    try {
                        handleNewConnection();
                    }
                    catch (SocketError& ex) {
                        std::cerr << "Server: " << ex.what() << std::endl;
                    }
                    catch (ServerError& ex) {
                        std::cerr << "Server: " << ex.what() << std::endl;
                        throw;
                    }
                }
            }
        }
    }
}

void
Listener::runThreaded() {
    acceptThread = std::thread([=]() { this->run(); });
}

void
Listener::shutdown() {
    if (shutdownFd.isBound()) shutdownFd.notify();
    reactor_.shutdown();
}

Async::Promise<Listener::Load>
Listener::requestLoad(const Listener::Load& old) {
    auto handlers = reactor_.handlers(transportKey);

    std::vector<Async::Promise<rusage>> loads;
    for (const auto& handler: handlers) {
        auto transport = std::static_pointer_cast<Transport>(handler);
        loads.push_back(transport->load());
    }

    return Async::whenAll(std::begin(loads), std::end(loads)).then([=](const std::vector<rusage>& usages) {

        Load res;
        res.raw = usages;

        if (old.raw.empty()) {
            res.global = 0.0;
            for (size_t i = 0; i < handlers.size(); ++i) res.workers.push_back(0.0);
        } else {

            auto totalElapsed = [](rusage usage) {
                return (usage.ru_stime.tv_sec * 1e6 + usage.ru_stime.tv_usec)
                     + (usage.ru_utime.tv_sec * 1e6 + usage.ru_utime.tv_usec);
            };

            auto now = std::chrono::system_clock::now();
            auto diff = now - old.tick;
            auto tick = std::chrono::duration_cast<std::chrono::microseconds>(diff);
            res.tick = now;

            for (size_t i = 0; i < usages.size(); ++i) {
                auto last = old.raw[i];
                const auto& usage = usages[i];

                auto nowElapsed = totalElapsed(usage);
                auto timeElapsed = nowElapsed - totalElapsed(last);

                auto loadPct = (timeElapsed * 100.0) / tick.count();
                res.workers.push_back(loadPct);
                res.global += loadPct;

            }

            res.global /= usages.size();
        }

        return res;

     }, Async::Throw);
}

Address
Listener::address() const {
    return addr_;
}

Options
Listener::options() const {
    return options_;
}

void Listener::handleNewConnection()
{
    struct sockaddr_in peer_addr;
    int client_fd = acceptConnection(peer_addr);

#ifdef PISTACHE_USE_SSL
    SSL *ssl = nullptr;

    if (this->useSSL_) {

        ssl = SSL_new((SSL_CTX *)this->ssl_ctx_);
        if (ssl == NULL)
            throw std::runtime_error("Cannot create SSL connection");

        SSL_set_fd(ssl, client_fd);
        SSL_set_accept_state(ssl);

        if (SSL_accept(ssl) <= 0) {
            ERR_print_errors_fp(stderr);
            SSL_free(ssl);
            close(client_fd);
            return ;
        }
    }
#endif /* PISTACHE_USE_SSL */

    make_non_blocking(client_fd);

    auto peer = std::make_shared<Peer>(Address::fromUnix((struct sockaddr *)&peer_addr));
    peer->associateFd(client_fd);

#ifdef PISTACHE_USE_SSL
    if (this->useSSL_)
        peer->associateSSL(ssl);
#endif /* PISTACHE_USE_SSL */

    dispatchPeer(peer);
}

int Listener::acceptConnection(struct sockaddr_in& peer_addr) const
{
    socklen_t peer_addr_len = sizeof(peer_addr);
    int client_fd = ::accept(listen_fd, (struct sockaddr *)&peer_addr, &peer_addr_len);
    if (client_fd < 0) {
        if (errno == EBADF || errno == ENOTSOCK)
            throw ServerError(strerror(errno));
        else
            throw SocketError(strerror(errno));
    }
    return client_fd;
}

void
Listener::dispatchPeer(const std::shared_ptr<Peer>& peer) {
    auto handlers = reactor_.handlers(transportKey);
    auto idx = peer->fd() % handlers.size();
    auto transport = std::static_pointer_cast<Transport>(handlers[idx]);

    transport->handleNewPeer(peer);

}

#ifdef PISTACHE_USE_SSL

static SSL_CTX *ssl_create_context(const std::string &cert, const std::string &key, bool use_compression)
{
    const SSL_METHOD    *method;
    SSL_CTX             *ctx;

    method = SSLv23_server_method();

    ctx = SSL_CTX_new(method);
    if (ctx == NULL) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Cannot setup SSL context");
    }

    if (!use_compression) {
        /* Disable compression to prevent BREACH and CRIME vulnerabilities. */
        if (!SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION)) {
            ERR_print_errors_fp(stderr);
            throw std::runtime_error("Cannot disable compression");
        }
    }

    /* Function introduced in 1.0.2 */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
    SSL_CTX_set_ecdh_auto(ctx, 1);
#endif /* OPENSSL_VERSION_NUMBER */

    if (SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Cannot load SSL certificate");
    }

    if (SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Cannot load SSL private key");
    }

    if (!SSL_CTX_check_private_key(ctx)) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Private key does not match public key in the certificate");
    }

    return ctx;
}

void
Listener::setupSSLAuth(const std::string &ca_file, const std::string &ca_path, int (*cb)(int, void *) = NULL)
{
    const char *__ca_file = NULL;
    const char *__ca_path = NULL;

    if (this->ssl_ctx_ == NULL)
        throw std::runtime_error("SSL Context is not initialized");

    if (!ca_file.empty())
        __ca_file = ca_file.c_str();
    if (!ca_path.empty())
        __ca_path = ca_path.c_str();

    if (SSL_CTX_load_verify_locations((SSL_CTX *)this->ssl_ctx_, __ca_file, __ca_path) <= 0) {
        ERR_print_errors_fp(stderr);
        throw std::runtime_error("Cannot verify SSL locations");
    }

    SSL_CTX_set_verify((SSL_CTX *)this->ssl_ctx_,
        SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE,
        /* Callback type did change in 1.0.1 */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
        (int (*)(int, X509_STORE_CTX *))cb
#else
        (SSL_verify_cb)cb
#endif /* OPENSSL_VERSION_NUMBER */
    );
}

void
Listener::setupSSL(const std::string &cert_path, const std::string &key_path, bool use_compression)
{
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();

    this->ssl_ctx_ = ssl_create_context(cert_path, key_path, use_compression);
    this->useSSL_ = true;
}

#endif /* PISTACHE_USE_SSL */

} // namespace Tcp
} // namespace Pistache
<|endoftext|>


<commit_before>#include "template.h"

#include <simlib/time.h>

Template::TemplateEnder Template::baseTemplate(const StringView& title,
	const StringView& styles, const StringView& scripts)
{
	resp.headers["Content-Type"] = "text/html; charset=utf-8";
	resp.content = "";

	append("<!DOCTYPE html>\n"
		"\n"
			"\n"
				"<meta charset=\"utf-8\">\n"
				"<title>", htmlSpecialChars(title), "</title>\n"
				"<link rel=\"stylesheet\" href=\"/kit/styles.css\">\n"
				"\n"
				"\n"
				"<link rel=\"shortcut icon\" href=\"/kit/img/favicon.png\">\n");

	if (scripts.size())
		append("\n");

	if (styles.size())
		append("\n");

	append("\n"
			"\n"
				"
\n"
					"SIM\n"
					"Contests\n");

	if (Session::open() && Session::user_type < UTYPE_NORMAL) {
		append("Users\n");
		if (Session::user_type == UTYPE_ADMIN)
			append("Logs\n");
	}

	append("
\n"
						"<time id=\"clock\">", date("%H:%M:%S"),
							" UTC</time>");

	if (Session::isOpen())
		append("
\n"
				""
					"", htmlSpecialChars(Session::username), ""
				"\n"
				"
\n"
					"Edit profile\n"
					"Change password\n"
					"Logout\n"
				"
\n"
				"
");

	else
		append("Log in\n"
			"Sign up");

	append("
\n"
		"
\n"
		"
\n");

	return TemplateEnder(*this);
}

void Template::endTemplate() {
	append("
\n"
			"\n"
			"\n"
		"\n");
}
<commit_msg>Added protection form clickjacking and XSS attack<commit_after>#include "template.h"

#include <simlib/time.h>

Template::TemplateEnder Template::baseTemplate(const StringView& title,
	const StringView& styles, const StringView& scripts)
{
	// Protect from clickjacking
	resp.headers["X-Frame-Options"] = "DENY";
	resp.headers["Content-Security-Policy"] = "frame-ancestors 'none'";
	// Protect from XSS attack
	resp.headers["X-XSS-Protection"] = "1; mode=block";

	resp.headers["Content-Type"] = "text/html; charset=utf-8";
	resp.content = "";

	append("<!DOCTYPE html>\n"
		"\n"
			"\n"
				"<meta charset=\"utf-8\">\n"
				"<title>", htmlSpecialChars(title), "</title>\n"
				"<link rel=\"stylesheet\" href=\"/kit/styles.css\">\n"
				"\n"
				"\n"
				"<link rel=\"shortcut icon\" href=\"/kit/img/favicon.png\">\n");

	if (scripts.size())
		append("\n");

	if (styles.size())
		append("\n");

	append("\n"
			"\n"
				"
\n"
					"SIM\n"
					"Contests\n");

	if (Session::open() && Session::user_type < UTYPE_NORMAL) {
		append("Users\n");
		if (Session::user_type == UTYPE_ADMIN)
			append("Logs\n");
	}

	append("
\n"
						"<time id=\"clock\">", date("%H:%M:%S"),
							" UTC</time>");

	if (Session::isOpen())
		append("
\n"
				""
					"", htmlSpecialChars(Session::username), ""
				"\n"
				"
\n"
					"Edit profile\n"
					"Change password\n"
					"Logout\n"
				"
\n"
				"
");

	else
		append("Log in\n"
			"Sign up");

	append("
\n"
		"
\n"
		"
\n");

	return TemplateEnder(*this);
}

void Template::endTemplate() {
	append("
\n"
			"\n"
			"\n"
		"\n");
}
<|endoftext|>


<commit_before><commit_msg>range support in globs<commit_after><|endoftext|>


<commit_before>#include "euclidean_dist_grad.h"

#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/register_types.h"
// Not available for user made kernels
//#include "tensorflow/core/kernels/fill_functor.h"
#include <cmath>
#include <thread>

namespace tensorflow {

template <typename T>
static void threadcompute(OpKernelContext* context, Tensor* xoutput_tensor, Tensor* coutput_tensor, int i, int numThreads){

  const Tensor& input_tensor1 = context->input(0);
  const Tensor& input_tensor2 = context->input(1);
  const Tensor& input_tensor3 = context->input(2);
  const Tensor& input_tensor4 = context->input(3);

  const int r1=input_tensor1.shape().dim_size(0); //num of data = n
  const int c1=input_tensor1.shape().dim_size(1); //dimensions = d
  const int c2=input_tensor2.shape().dim_size(1); //clusters = k

  auto in1 = input_tensor1.shaped<double, 2>({r1,c1});
  auto in2 = input_tensor2.shaped<double, 2>({c1,c2});
  auto in3 = input_tensor3.shaped<double, 2>({r1,c2});
  auto in4 = input_tensor4.shaped<double, 2>({r1,c2});

  auto xgrad = xoutput_tensor->shaped<double, 2>({r1,c1});
  auto cgrad = coutput_tensor->shaped<double, 2>({c1,c2});

  //printf("i: %d  r1: %d  numThreads: %d  \n",i,r1,numThreads);

  for(int in=i*r1/float(numThreads); in<(i+1)*r1/float(numThreads); in++){
    for (int id=0; id<c1; id++){
      xgrad(in,id) = 0;
    }
  }

  for(int ik=i*c2/float(numThreads); ik<(i+1)*c2/float(numThreads); ik++){
    for (int id=0; id<c1; id++){
      cgrad(id,ik) = 0;
    }
  }

  //printf("%f\n",(double)gradients(0,0));
  //printf("%f\n",(double)output(0,0));

  if (numThreads == 1) {
    for(int in=0; in<r1; in++){
      for (int ik=0; ik<c2; ik++){

        // Points where x==c cannot be differentiated.
        //  Often those are points that don't want to be
        //  moved anyway, since that's typically the "best"
        //  cluster representative input.
        if (in3(in,ik)!=0){
          auto tempMultiplicand = in4(in,ik)/in3(in,ik);
          for (int id=0; id<c1; id++){
            auto temp = (in1(in,id)-in2(id,ik))*tempMultiplicand;
            xgrad(in,id) += temp;
            cgrad(id,ik) -= temp;
          }
        }
      }
    }
  } else { // In multithreading, the two gradients have to be computed apart.
    for(int in=i*r1/float(numThreads); in<(i+1)*r1/float(numThreads); in++){
      for (int ik=0; ik<c2; ik++){
        if (in3(in,ik)!=0){
          auto tempMultiplicand = in4(in,ik)/in3(in,ik);
          for (int id=0; id<c1; id++){
            xgrad(in,id) += (in1(in,id)-in2(id,ik))*tempMultiplicand;
          }
        }
      }
    }
    for(int ik=i*c2/float(numThreads); ik<(i+1)*c2/float(numThreads); ik++){
      for (int in=0; in<r1; in++){
        if (in3(in,ik)!=0){
          auto tempMultiplicand = in4(in,ik)/in3(in,ik);
          for (int id=0; id<c1; id++){
            cgrad(id,ik) -= (in1(in,id)-in2(id,ik))*tempMultiplicand;
          }
        }
      }
    }
  }

}

template <typename Device, typename T>
class EuclideanDistGradOp;

template <typename T>
struct LaunchEuclideanDistGradCPU {
  static void launch(
      OpKernelContext* ctx, OpKernel* kernel, const Tensor& a, const Tensor& b,
      const Tensor& d, const Tensor& g, Tensor* xout, Tensor* cout) {

    int numThreads = (( EuclideanDistGradOp<CPUDevice, T>*) kernel)->get_number_of_threads();

    std::thread myThreads[numThreads-1];

    for (int id=0;id<numThreads-1;id++){
      myThreads[id]=std::thread(threadcompute<T>, ctx, xout, cout, id, numThreads);
    }

    // Remember that the thread launching these threads is a usuable thread as well.
    threadcompute<T>(ctx, xout, cout, numThreads-1, numThreads);

    for (int id=1; id<numThreads-1; id++){
      myThreads[id].join();
    }
  }
};

template <typename T>
struct LaunchEuclideanDistGrad<CPUDevice, T> : public LaunchEuclideanDistGradCPU<T> {};

template <typename T>
struct LaunchEuclideanDistGrad<GPUDevice, T> : public LaunchEuclideanDistGradGPU<T> {};

REGISTER_OP("EuclideanDistGrad")
    .Input("data: T")
    .Input("clusters: T")
    .Input("distances: T")
    .Input("gradients: T")
    .Output("xgrad: T")
    .Output("cgrad: T")
    .Attr("number_of_threads: int >= 1 = 1")
    .Attr("T: {half, float, double, int32, int64, complex64, complex128}")
    .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) {

      ::tensorflow::shape_inference::ShapeHandle a;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 2, &a));

      ::tensorflow::shape_inference::ShapeHandle b;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 2, &b));

      ::tensorflow::shape_inference::ShapeHandle d;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 2, &d));

      ::tensorflow::shape_inference::ShapeHandle g;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 2, &g));

      ::tensorflow::shape_inference::DimensionHandle xoutput_rows = c->Dim(a, 0);
      ::tensorflow::shape_inference::DimensionHandle xoutput_cols = c->Dim(a, 1);

      ::tensorflow::shape_inference::DimensionHandle coutput_rows = c->Dim(b, 0);
      ::tensorflow::shape_inference::DimensionHandle coutput_cols = c->Dim(b, 1);

      // Validate that the inner shapes are compatible.
      ::tensorflow::shape_inference::DimensionHandle inner_a = c->Dim(a, 1);
      ::tensorflow::shape_inference::DimensionHandle inner_b = c->Dim(b, 0);
      ::tensorflow::shape_inference::DimensionHandle merged;
      TF_RETURN_IF_ERROR(c->Merge(inner_a, inner_b, &merged));
      // I actually don't know how to check the shapes of d and g here.
      // I don't know if that's necessary.
      // Documentation on shape_inference is sparse.

      c->set_output(0, c->Matrix(xoutput_rows, xoutput_cols));
      c->set_output(1, c->Matrix(coutput_rows, coutput_cols));
      return Status::OK();
    })
    .Doc(R"doc(
Computes the gradient of the pairwise Euclidean distance calculation in
the euclidean distance op with respect to the original inputs, the ouput,
and the backpropagated gradients. The inputs must be two-dimensional matrices,
the inner dimension of "data" must match the inner dimension of "clusters",
the outer dimensions of "data" and "clusters" must match the dimensions of
both "distances" and "gradients".
)doc");

template <typename Device, typename T>
class EuclideanDistGradOp : public OpKernel {
 public:
  EuclideanDistGradOp(OpKernelConstruction* context) : OpKernel(context) {
    // Get the number of the threads to use
    OP_REQUIRES_OK(context,
                   context->GetAttr("number_of_threads", &number_of_threads_));
    // Check that number_of_threads is strictly positive
    OP_REQUIRES(context, number_of_threads_ >= 1,
                errors::InvalidArgument("Need number_of_threads >= 1, got ",
                                        number_of_threads_));
  }

  void Compute(OpKernelContext* ctx) override {
    const Tensor& a = ctx->input(0);
    const Tensor& b = ctx->input(1);
    const Tensor& d = ctx->input(2);
    const Tensor& g = ctx->input(3);

    // Check that the dimensions of the four matrices are valid.
    OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(a.shape()),
                errors::InvalidArgument("In[0] is not a matrix"));
    OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(b.shape()),
                errors::InvalidArgument("In[1] is not a matrix"));
    OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(d.shape()),
                errors::InvalidArgument("In[2] is not a matrix"));
    OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(g.shape()),
                errors::InvalidArgument("In[3] is not a matrix"));

    OP_REQUIRES(ctx,
                a.dim_size(1) == b.dim_size(0),
                errors::InvalidArgument("Matrix size-incompatible: In[0]: ",
                                        a.shape().DebugString(), ", In[1]: ",
                                        b.shape().DebugString()));
    OP_REQUIRES(ctx,
                a.dim_size(0) == d.dim_size(0),
                errors::InvalidArgument("Matrix size-incompatible: In[0]: ",
                                        a.shape().DebugString(), ", In[2]: ",
                                        d.shape().DebugString()));
    OP_REQUIRES(ctx,
                b.dim_size(1) == d.dim_size(1),
                errors::InvalidArgument("Matrix size-incompatible: In[1]: ",
                                        b.shape().DebugString(), ", In[2]: ",
                                        d.shape().DebugString()));
    OP_REQUIRES(ctx,
                a.dim_size(0) == g.dim_size(0),
                errors::InvalidArgument("Matrix size-incompatible: In[0]: ",
                                        a.shape().DebugString(), ", In[3]: ",
                                        g.shape().DebugString()));
    OP_REQUIRES(ctx,
                b.dim_size(1) == g.dim_size(1),
                errors::InvalidArgument("Matrix size-incompatible: In[1]: ",
                                        b.shape().DebugString(), ", In[3]: ",
                                        g.shape().DebugString()));
    TensorShape xout_shape({a.dim_size(0), a.dim_size(1)});
    TensorShape cout_shape({b.dim_size(0), b.dim_size(1)});
    Tensor* xout = nullptr;
    Tensor* cout = nullptr;
    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, xout_shape, &xout));
    OP_REQUIRES_OK(ctx, ctx->allocate_output(1, cout_shape, &cout));

    if (a.NumElements() == 0) {
      // If a has zero elements, the first output shape is
      // a 0-element matrix, so there is nothing to do for it.
      if (b.NumElements() != 0) {
        // But if b has non-zero elements, we have to
        // set all of the second output to zero.
        auto output = cout->shaped<T, 2>({b.shape().dim_size(0),b.shape().dim_size(1)});

        // Not available for user made kernels
        //functor::SetZeroFunctor<Device, T> f;
        //f(ctx->eigen_device<Device>(), out->flat<T>());
        for (int k=0; k<b.dim_size(1); k++){
          for(int n=0; n<b.dim_size(0); n++){
            output(n,k) = 0;
          }
        }
      }
      return;
    }
    if (b.NumElements() == 0) {
      // If b has zero elements, and a doesn't then we
      // have to set all of the first output to zero.
      auto output = xout->shaped<T, 2>({a.shape().dim_size(0),a.shape().dim_size(1)});

      // Not available for user made kernels
      //functor::SetZeroFunctor<Device, T> f;
      //f(ctx->eigen_device<Device>(), out->flat<T>());
      for (int k=0; k<a.dim_size(1); k++){
        for(int n=0; n<a.dim_size(0); n++){
          output(n,k) = 0;
        }
      }
      return;
    }

    LaunchEuclideanDistGrad<Device, T>::launch(ctx, this, a, b, d, g, xout, cout);

  }

  int get_number_of_threads() {
    return number_of_threads_;
  }
 private:
  int number_of_threads_;
};

#define REGISTER_KERNEL(type)              \
  REGISTER_KERNEL_BUILDER(                 \
    Name("EuclideanDistGrad")              \
    .Device(DEVICE_CPU)                    \
    .TypeConstraint<type>("T"),            \
    EuclideanDistGradOp<CPUDevice, type>); \
  REGISTER_KERNEL_BUILDER(                 \
    Name("EuclideanDistGrad")              \
    .Device(DEVICE_GPU)                    \
    .TypeConstraint<type>("T"),            \
    EuclideanDistGradOp<GPUDevice, type>);

REGISTER_KERNEL(int32);
REGISTER_KERNEL(float);
REGISTER_KERNEL(double);

#undef REGISTER_KERNEL

}  // namespace tensorflow
<commit_msg>I forgot to add some templating. TensorFlow will fatal crash without this fix.<commit_after>#include "euclidean_dist_grad.h"

#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/register_types.h"
// Not available for user made kernels
//#include "tensorflow/core/kernels/fill_functor.h"
#include <cmath>
#include <thread>

namespace tensorflow {

template <typename T>
static void threadcompute(OpKernelContext* context, Tensor* xoutput_tensor, Tensor* coutput_tensor, int i, int numThreads){

  const Tensor& input_tensor1 = context->input(0);
  const Tensor& input_tensor2 = context->input(1);
  const Tensor& input_tensor3 = context->input(2);
  const Tensor& input_tensor4 = context->input(3);

  const int r1=input_tensor1.shape().dim_size(0); //num of data = n
  const int c1=input_tensor1.shape().dim_size(1); //dimensions = d
  const int c2=input_tensor2.shape().dim_size(1); //clusters = k

  auto in1 = input_tensor1.shaped<T, 2>({r1,c1});
  auto in2 = input_tensor2.shaped<T, 2>({c1,c2});
  auto in3 = input_tensor3.shaped<T, 2>({r1,c2});
  auto in4 = input_tensor4.shaped<T, 2>({r1,c2});

  auto xgrad = xoutput_tensor->shaped<T, 2>({r1,c1});
  auto cgrad = coutput_tensor->shaped<T, 2>({c1,c2});

  //printf("i: %d  r1: %d  numThreads: %d  \n",i,r1,numThreads);

  for(int in=i*r1/float(numThreads); in<(i+1)*r1/float(numThreads); in++){
    for (int id=0; id<c1; id++){
      xgrad(in,id) = 0;
    }
  }

  for(int ik=i*c2/float(numThreads); ik<(i+1)*c2/float(numThreads); ik++){
    for (int id=0; id<c1; id++){
      cgrad(id,ik) = 0;
    }
  }

  //printf("%f\n",(double)gradients(0,0));
  //printf("%f\n",(double)output(0,0));

  if (numThreads == 1) {
    for(int in=0; in<r1; in++){
      for (int ik=0; ik<c2; ik++){

        // Points where x==c cannot be differentiated.
        //  Often those are points that don't want to be
        //  moved anyway, since that's typically the "best"
        //  cluster representative input.
        if (in3(in,ik)!=0){
          auto tempMultiplicand = in4(in,ik)/in3(in,ik);
          for (int id=0; id<c1; id++){
            auto temp = (in1(in,id)-in2(id,ik))*tempMultiplicand;
            xgrad(in,id) += temp;
            cgrad(id,ik) -= temp;
          }
        }
      }
    }
  } else { // In multithreading, the two gradients have to be computed apart.
    for(int in=i*r1/float(numThreads); in<(i+1)*r1/float(numThreads); in++){
      for (int ik=0; ik<c2; ik++){
        if (in3(in,ik)!=0){
          auto tempMultiplicand = in4(in,ik)/in3(in,ik);
          for (int id=0; id<c1; id++){
            xgrad(in,id) += (in1(in,id)-in2(id,ik))*tempMultiplicand;
          }
        }
      }
    }
    for(int ik=i*c2/float(numThreads); ik<(i+1)*c2/float(numThreads); ik++){
      for (int in=0; in<r1; in++){
        if (in3(in,ik)!=0){
          auto tempMultiplicand = in4(in,ik)/in3(in,ik);
          for (int id=0; id<c1; id++){
            cgrad(id,ik) -= (in1(in,id)-in2(id,ik))*tempMultiplicand;
          }
        }
      }
    }
  }

}

template <typename Device, typename T>
class EuclideanDistGradOp;

template <typename T>
struct LaunchEuclideanDistGradCPU {
  static void launch(
      OpKernelContext* ctx, OpKernel* kernel, const Tensor& a, const Tensor& b,
      const Tensor& d, const Tensor& g, Tensor* xout, Tensor* cout) {

    int numThreads = (( EuclideanDistGradOp<CPUDevice, T>*) kernel)->get_number_of_threads();

    std::thread myThreads[numThreads-1];

    for (int id=0;id<numThreads-1;id++){
      myThreads[id]=std::thread(threadcompute<T>, ctx, xout, cout, id, numThreads);
    }

    // Remember that the thread launching these threads is a usuable thread as well.
    threadcompute<T>(ctx, xout, cout, numThreads-1, numThreads);

    for (int id=1; id<numThreads-1; id++){
      myThreads[id].join();
    }
  }
};

template <typename T>
struct LaunchEuclideanDistGrad<CPUDevice, T> : public LaunchEuclideanDistGradCPU<T> {};

template <typename T>
struct LaunchEuclideanDistGrad<GPUDevice, T> : public LaunchEuclideanDistGradGPU<T> {};

REGISTER_OP("EuclideanDistGrad")
    .Input("data: T")
    .Input("clusters: T")
    .Input("distances: T")
    .Input("gradients: T")
    .Output("xgrad: T")
    .Output("cgrad: T")
    .Attr("number_of_threads: int >= 1 = 1")
    .Attr("T: {half, float, double, int32, int64, complex64, complex128}")
    .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) {

      ::tensorflow::shape_inference::ShapeHandle a;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 2, &a));

      ::tensorflow::shape_inference::ShapeHandle b;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 2, &b));

      ::tensorflow::shape_inference::ShapeHandle d;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 2, &d));

      ::tensorflow::shape_inference::ShapeHandle g;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 2, &g));

      ::tensorflow::shape_inference::DimensionHandle xoutput_rows = c->Dim(a, 0);
      ::tensorflow::shape_inference::DimensionHandle xoutput_cols = c->Dim(a, 1);

      ::tensorflow::shape_inference::DimensionHandle coutput_rows = c->Dim(b, 0);
      ::tensorflow::shape_inference::DimensionHandle coutput_cols = c->Dim(b, 1);

      // Validate that the inner shapes are compatible.
      ::tensorflow::shape_inference::DimensionHandle inner_a = c->Dim(a, 1);
      ::tensorflow::shape_inference::DimensionHandle inner_b = c->Dim(b, 0);
      ::tensorflow::shape_inference::DimensionHandle merged;
      TF_RETURN_IF_ERROR(c->Merge(inner_a, inner_b, &merged));
      // I actually don't know how to check the shapes of d and g here.
      // I don't know if that's necessary.
      // Documentation on shape_inference is sparse.

      c->set_output(0, c->Matrix(xoutput_rows, xoutput_cols));
      c->set_output(1, c->Matrix(coutput_rows, coutput_cols));
      return Status::OK();
    })
    .Doc(R"doc(
Computes the gradient of the pairwise Euclidean distance calculation in
the euclidean distance op with respect to the original inputs, the ouput,
and the backpropagated gradients. The inputs must be two-dimensional matrices,
the inner dimension of "data" must match the inner dimension of "clusters",
the outer dimensions of "data" and "clusters" must match the dimensions of
both "distances" and "gradients".
)doc");

template <typename Device, typename T>
class EuclideanDistGradOp : public OpKernel {
 public:
  EuclideanDistGradOp(OpKernelConstruction* context) : OpKernel(context) {
    // Get the number of the threads to use
    OP_REQUIRES_OK(context,
                   context->GetAttr("number_of_threads", &number_of_threads_));
    // Check that number_of_threads is strictly positive
    OP_REQUIRES(context, number_of_threads_ >= 1,
                errors::InvalidArgument("Need number_of_threads >= 1, got ",
                                        number_of_threads_));
  }

  void Compute(OpKernelContext* ctx) override {
    const Tensor& a = ctx->input(0);
    const Tensor& b = ctx->input(1);
    const Tensor& d = ctx->input(2);
    const Tensor& g = ctx->input(3);

    // Check that the dimensions of the four matrices are valid.
    OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(a.shape()),
                errors::InvalidArgument("In[0] is not a matrix"));
    OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(b.shape()),
                errors::InvalidArgument("In[1] is not a matrix"));
    OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(d.shape()),
                errors::InvalidArgument("In[2] is not a matrix"));
    OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(g.shape()),
                errors::InvalidArgument("In[3] is not a matrix"));

    OP_REQUIRES(ctx,
                a.dim_size(1) == b.dim_size(0),
                errors::InvalidArgument("Matrix size-incompatible: In[0]: ",
                                        a.shape().DebugString(), ", In[1]: ",
                                        b.shape().DebugString()));
    OP_REQUIRES(ctx,
                a.dim_size(0) == d.dim_size(0),
                errors::InvalidArgument("Matrix size-incompatible: In[0]: ",
                                        a.shape().DebugString(), ", In[2]: ",
                                        d.shape().DebugString()));
    OP_REQUIRES(ctx,
                b.dim_size(1) == d.dim_size(1),
                errors::InvalidArgument("Matrix size-incompatible: In[1]: ",
                                        b.shape().DebugString(), ", In[2]: ",
                                        d.shape().DebugString()));
    OP_REQUIRES(ctx,
                a.dim_size(0) == g.dim_size(0),
                errors::InvalidArgument("Matrix size-incompatible: In[0]: ",
                                        a.shape().DebugString(), ", In[3]: ",
                                        g.shape().DebugString()));
    OP_REQUIRES(ctx,
                b.dim_size(1) == g.dim_size(1),
                errors::InvalidArgument("Matrix size-incompatible: In[1]: ",
                                        b.shape().DebugString(), ", In[3]: ",
                                        g.shape().DebugString()));
    TensorShape xout_shape({a.dim_size(0), a.dim_size(1)});
    TensorShape cout_shape({b.dim_size(0), b.dim_size(1)});
    Tensor* xout = nullptr;
    Tensor* cout = nullptr;
    OP_REQUIRES_OK(ctx, ctx->allocate_output(0, xout_shape, &xout));
    OP_REQUIRES_OK(ctx, ctx->allocate_output(1, cout_shape, &cout));

    if (a.NumElements() == 0) {
      // If a has zero elements, the first output shape is
      // a 0-element matrix, so there is nothing to do for it.
      if (b.NumElements() != 0) {
        // But if b has non-zero elements, we have to
        // set all of the second output to zero.
        auto output = cout->shaped<T, 2>({b.shape().dim_size(0),b.shape().dim_size(1)});

        // Not available for user made kernels
        //functor::SetZeroFunctor<Device, T> f;
        //f(ctx->eigen_device<Device>(), out->flat<T>());
        for (int k=0; k<b.dim_size(1); k++){
          for(int n=0; n<b.dim_size(0); n++){
            output(n,k) = 0;
          }
        }
      }
      return;
    }
    if (b.NumElements() == 0) {
      // If b has zero elements, and a doesn't then we
      // have to set all of the first output to zero.
      auto output = xout->shaped<T, 2>({a.shape().dim_size(0),a.shape().dim_size(1)});

      // Not available for user made kernels
      //functor::SetZeroFunctor<Device, T> f;
      //f(ctx->eigen_device<Device>(), out->flat<T>());
      for (int k=0; k<a.dim_size(1); k++){
        for(int n=0; n<a.dim_size(0); n++){
          output(n,k) = 0;
        }
      }
      return;
    }

    LaunchEuclideanDistGrad<Device, T>::launch(ctx, this, a, b, d, g, xout, cout);

  }

  int get_number_of_threads() {
    return number_of_threads_;
  }
 private:
  int number_of_threads_;
};

#define REGISTER_KERNEL(type)              \
  REGISTER_KERNEL_BUILDER(                 \
    Name("EuclideanDistGrad")              \
    .Device(DEVICE_CPU)                    \
    .TypeConstraint<type>("T"),            \
    EuclideanDistGradOp<CPUDevice, type>); \
  REGISTER_KERNEL_BUILDER(                 \
    Name("EuclideanDistGrad")              \
    .Device(DEVICE_GPU)                    \
    .TypeConstraint<type>("T"),            \
    EuclideanDistGradOp<GPUDevice, type>);

REGISTER_KERNEL(int32);
REGISTER_KERNEL(float);
REGISTER_KERNEL(double);

#undef REGISTER_KERNEL

}  // namespace tensorflow
<|endoftext|>


<commit_before>
#include <stdio.h>
#include "eudaq/FileReader.hh"
#include "eudaq/PluginManager.hh"
#include "eudaq/OptionParser.hh"
#include "eudaq/StandardEvent.hh"

#include <iostream>
#include <vector>
#include <map>

#ifndef WIN32
#include <inttypes.h>
#endif

#include "cfunc.h"

using namespace eudaq;

void eudaq_data_map(const std::string & filename, unsigned int begin, unsigned int end, std::map<std::string, std::vector<data_row> > & data_map)
{
  // Create a reader for this iframele
  eudaq::FileReader reader(filename);

  eudaq::PluginManager::Initialize(reader.GetDetectorEvent());

  // Display the actual iframelename (argument could have been a run number)
  //std::cout << "Opened file: " << reader.Filename() << std::endl;
  while (reader.NextEvent()) {
      if (reader.GetDetectorEvent().IsEORE()) {
          //std::cout << "End of run detected" << std::endl;
          // Don't try to process if it is an EORE
          break;
      }
      unsigned int eventnumber=reader.GetDetectorEvent().GetEventNumber();
      if (eventnumber<begin){
          continue;
      }
      if (eventnumber>=end){
          break;
      }

      try {
        unsigned int eventnumber=reader.GetDetectorEvent().GetEventNumber();
        eudaq::StandardEvent sev =  eudaq::PluginManager::ConvertToStandard(reader.GetDetectorEvent());
        //std::cout << sev << std::endl;

        for(unsigned int iplane = 0; iplane < sev.NumPlanes(); iplane++){
          const StandardPlane & plane = sev.GetPlane(iplane);

	        //std::cout<< " sensor=" << plane.Sensor() << " type=" << plane.Type() << "" << std::endl;

	        for(unsigned int iframe = 0; iframe < plane.NumFrames(); iframe++){

		        const std::vector<double> & xv = plane.XVector(iframe);
		        const std::vector<double> & yv = plane.YVector(iframe);
		        const std::vector<double> & pix = plane.PixVector(iframe);

		        for(unsigned int ipix = 0; ipix < pix.size(); ipix++){
                      data_row data_pix = {eventnumber,plane.TLUEvent(), iplane, iframe, xv[ipix],  yv[ipix], pix[ipix]};
		              data_map[plane.Type()].push_back(data_pix);
		        }
	        }
        }

      } catch (const eudaq::Exception & e) {
        //std::cout << "No " << type << " subevent in event "  << reader.GetDetectorEvent().GetEventNumber() << std::endl;
      }
    }
}

<commit_msg>add eventnumber in return, add begin and end<commit_after>
#include <stdio.h>
#include "eudaq/FileReader.hh"
#include "eudaq/PluginManager.hh"
#include "eudaq/OptionParser.hh"
#include "eudaq/StandardEvent.hh"

#include <iostream>
#include <vector>
#include <map>

#ifndef WIN32
#include <inttypes.h>
#endif

#include "cfunc.h"

using namespace eudaq;

void eudaq_data_map(const std::string & filename, unsigned int begin, unsigned int end, std::map<std::string, std::vector<data_row> > & data_map)
{
  // Create a reader for this iframele
  eudaq::FileReader reader(filename);

  eudaq::PluginManager::Initialize(reader.GetDetectorEvent());

  // Display the actual iframelename (argument could have been a run number)
  //std::cout << "Opened file: " << reader.Filename() << std::endl;
  while (reader.NextEvent()) {
      if (reader.GetDetectorEvent().IsEORE()) {
          //std::cout << "End of run detected" << std::endl;
          // Don't try to process if it is an EORE
          break;
      }
      unsigned int eventnumber=reader.GetDetectorEvent().GetEventNumber();
      if (eventnumber<begin){
          continue;
      }
      if (eventnumber>=end){
          break;
      }

      try {
        unsigned int eventnumber=reader.GetDetectorEvent().GetEventNumber();
        eudaq::StandardEvent sev =  eudaq::PluginManager::ConvertToStandard(reader.GetDetectorEvent());
        //std::cout << sev << std::endl;

        for(unsigned int iplane = 0; iplane < sev.NumPlanes(); iplane++){
          const StandardPlane & plane = sev.GetPlane(iplane);

	        //std::cout<< " sensor=" << plane.Sensor() << " type=" << plane.Type() << "" << std::endl;

	        for(unsigned int iframe = 0; iframe < plane.NumFrames(); iframe++){

		        const std::vector<double> & xv = plane.XVector(iframe);
		        const std::vector<double> & yv = plane.YVector(iframe);
		        const std::vector<double> & pix = plane.PixVector(iframe);

		        for(unsigned int ipix = 0; ipix < pix.size(); ipix++){
                      data_row data_pix = {eventnumber,plane.TLUEvent(), iplane, iframe, xv[ipix],  yv[ipix], pix[ipix]};
		              data_map[plane.Type()].push_back(data_pix);
		        }
	        }
        }

      } catch (const eudaq::Exception & e) {
        //std::cout << "No " << type << " subevent in event "  << reader.GetDetectorEvent().GetEventNumber() << std::endl;
      }
  }
}

<|endoftext|>