<commit_before>/*
Copyright (c) 2017, Vadim Malyshev, lboss75@gmail.com
All rights reserved
*/

#include "stdafx.h"
#include "private/udp_transport.h"
#include "messages/dht_route_messages.h"
#include "private/dht_session.h"
#include "logger.h"
#include "dht_network_client.h"
#include "dht_network_client_p.h"

vds::dht::network::udp_transport::udp_transport(){
}

vds::dht::network::udp_transport::~udp_transport() {
}

void vds::dht::network::udp_transport::start(
  const service_provider * sp,
  const std::shared_ptr<certificate> & node_cert,
  const std::shared_ptr<asymmetric_private_key> & node_key,
  uint16_t port) {
  this->send_thread_ = std::make_shared<thread_apartment>(sp);
  this->sp_ = sp;
  this->this_node_id_ = node_cert->fingerprint(hash::sha256());
  this->node_cert_ = node_cert;
  this->node_key_ = node_key;

  try {
    auto [reader, writer] = this->server_.start(sp, network_address::any_ip6(port));
    this->reader_ = reader;
    this->writer_ = writer;
  }
  catch (...) {
    auto[reader, writer] = this->server_.start(sp, network_address::any_ip4(port));
    this->reader_ = reader;
    this->writer_ = writer;
  }

      this->continue_read().detach();
}

void vds::dht::network::udp_transport::stop() {
  this->server_.stop();
}

vds::async_task<void>
vds::dht::network::udp_transport::write_async( const udp_datagram& datagram) {
  auto result = std::make_shared<vds::async_result<void>>();
  this->send_thread_->schedule([result, this, datagram]() {
    try{
      this->writer_->write_async(datagram).get();
    }
    catch (...){
      result->set_exception(std::current_exception());
      return;
    }
    result->set_value();
  });

  return result->get_future();

  //  std::unique_lock<std::debug_mutex> lock(this->write_mutex_);
  //  while(this->write_in_progress_) {
  //    this->write_cond_.wait(*reinterpret_cast<std::unique_lock<std::mutex> *>(&lock));
  //  }
  //  this->write_in_progress_ = true;
  //#ifdef _DEBUG
  //#ifndef _WIN32
  //  this->owner_id_ = syscall(SYS_gettid);
  //#else
  //  this->owner_id_ = GetCurrentThreadId();
  //#endif
  //#endif//_DEBUG
  //co_await this->writer_->write_async(sp, datagram);
}

vds::async_task<void> vds::dht::network::udp_transport::try_handshake(
                                                                  const std::string& address_str) {

  auto address = network_address::parse(this->server_.address().family(), address_str);

  this->sessions_mutex_.lock();
  auto p = this->sessions_.find(address);
  if (this->sessions_.end() == p) {
    this->sessions_mutex_.unlock();
  }
  else {
    auto & session_info = p->second;
    this->sessions_mutex_.unlock();

    session_info.session_mutex_.lock();
    if (session_info.blocked_) {
      if ((std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(1)) {
        logger::get(this->sp_)->trace(ThisModule, "Unblock session %s", address_str.c_str());
        session_info.blocked_ = false;
      }
      else {
        session_info.session_mutex_.unlock();
        co_return;
      }
    }
    else if(session_info.session_) {
      session_info.session_mutex_.unlock();
      co_return;
    }
    session_info.session_mutex_.unlock();
  }

  resizable_data_buffer out_message;
  out_message.add((uint8_t)protocol_message_type_t::HandshakeBroadcast);
  out_message.add(MAGIC_LABEL >> 24);
  out_message.add(MAGIC_LABEL >> 16);
  out_message.add(MAGIC_LABEL >> 8);
  out_message.add(MAGIC_LABEL);
  out_message.add(PROTOCOL_VERSION);

  binary_serializer bs;
  bs << this->node_cert_->der();

  out_message += bs.move_data();

  co_await this->write_async(udp_datagram(
    address,
    out_message.move_data(),
    false));
}

vds::async_task<void> vds::dht::network::udp_transport::on_timer() {
  std::list<std::shared_ptr<dht_session>> sessions;

  this->sessions_mutex_.lock_shared();
  for(auto & p : this->sessions_) {
    if (p.second.session_) {
      sessions.push_back(p.second.session_);
    }
  }
  this->sessions_mutex_.unlock_shared();

  for(auto & s : sessions) {
    co_await s->on_timer(this->shared_from_this());
  }
}

void vds::dht::network::udp_transport::get_session_statistics(session_statistic& session_statistic) {
  session_statistic.output_size_ = this->send_thread_->size();

  std::shared_lock<std::shared_mutex> lock(this->sessions_mutex_);
  for (const auto& p : this->sessions_) {
    const auto & session = p.second;
    if (session.session_) {
      session_statistic.items_.push_back(session.session_->get_statistic());
    }
  }
}

vds::async_task<void> vds::dht::network::udp_transport::continue_read(
  ) {
  for (;;) {
    udp_datagram datagram;
    try {
      datagram = co_await this->reader_->read_async();
    }
    catch(const std::system_error & ex) {
      continue;
    }

    if (this->sp_->get_shutdown_event().is_shuting_down()) {
      co_return;
    }

    this->sessions_mutex_.lock();
    auto & session_info = this->sessions_[datagram.address()];
    this->sessions_mutex_.unlock();

    session_info.session_mutex_.lock();
    if (session_info.blocked_) {
      if ((std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(1)) {
        logger::get(this->sp_)->trace(ThisModule, "Unblock session %s", datagram.address().to_string().c_str());
        session_info.blocked_ = false;
      }
      else {
        session_info.session_mutex_.unlock();
        if (*datagram.data() != (uint8_t)protocol_message_type_t::Failed
          && *datagram.data() != (uint8_t)protocol_message_type_t::Handshake
          && *datagram.data() != (uint8_t)protocol_message_type_t::HandshakeBroadcast
          && *datagram.data() != (uint8_t)protocol_message_type_t::Welcome) {
          uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
          try {
            co_await this->write_async(udp_datagram(datagram.address(),
              const_data_buffer(out_message, sizeof(out_message))));
          }
          catch (...) {
          }
        }
        continue;
      }
    }

    switch ((protocol_message_type_t)datagram.data()[0]) {
    case protocol_message_type_t::HandshakeBroadcast:
    case protocol_message_type_t::Handshake: {

      if (session_info.session_) {
        session_info.session_mutex_.unlock();
        continue;
      }

      if (
        (uint8_t)(MAGIC_LABEL >> 24) == datagram.data()[1]
        && (uint8_t)(MAGIC_LABEL >> 16) == datagram.data()[2]
        && (uint8_t)(MAGIC_LABEL >> 8) == datagram.data()[3]
        && (uint8_t)(MAGIC_LABEL) == datagram.data()[4]
        && PROTOCOL_VERSION == datagram.data()[5]) {
        binary_deserializer bd(datagram.data() + 6, datagram.data_size() - 6);
        const_data_buffer partner_node_cert_der;
        bd >> partner_node_cert_der;
        auto partner_node_cert = certificate::parse_der(partner_node_cert_der);
        auto partner_node_id = partner_node_cert.fingerprint(hash::sha256());
        if (partner_node_id == this->this_node_id_) {
          session_info.session_mutex_.unlock();
          break;
        }

        if (!session_info.session_key_ || (std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(10)) {
          session_info.update_time_ = std::chrono::steady_clock::now();
          session_info.session_key_.resize(32);
          crypto_service::rand_bytes(session_info.session_key_.data(), session_info.session_key_.size());
        }

        session_info.session_ = std::make_shared<dht_session>(
          this->sp_,
          datagram.address(),
          this->this_node_id_,
          partner_node_id,
          session_info.session_key_);

        this->sp_->get<logger>()->debug(ThisModule, "Add session %s", datagram.address().to_string().c_str());
        (*this->sp_->get<client>())->add_session(session_info.session_, 0);

        resizable_data_buffer out_message;
        out_message.add(static_cast<uint8_t>(protocol_message_type_t::Welcome));
        out_message.add(MAGIC_LABEL >> 24);
        out_message.add(MAGIC_LABEL >> 16);
        out_message.add(MAGIC_LABEL >> 8);
        out_message.add(MAGIC_LABEL);

        binary_serializer bs;
        bs
          << this->node_cert_->der()
          << partner_node_cert.public_key().encrypt(session_info.session_key_);

        session_info.session_mutex_.unlock();

        out_message += bs.move_data();
        co_await this->write_async(udp_datagram(datagram.address(), out_message.move_data(), false));
        co_await (*this->sp_->get<client>())->send_neighbors(
          message_create<messages::dht_find_node_response>(
            std::list<messages::dht_find_node_response::target_node>({
          messages::dht_find_node_response::target_node(partner_node_id, datagram.address().to_string(), 0) })));

        co_await this->sp_->get<imessage_map>()->on_new_session(
          partner_node_id);
      }
      else {
        session_info.session_mutex_.unlock();
        throw std::runtime_error("Invalid protocol");
      }
      break;
    }
    case protocol_message_type_t::Welcome: {
      if (datagram.data_size() > 5
        && (uint8_t)(MAGIC_LABEL >> 24) == datagram.data()[1]
        && (uint8_t)(MAGIC_LABEL >> 16) == datagram.data()[2]
        && (uint8_t)(MAGIC_LABEL >> 8) == datagram.data()[3]
        && (uint8_t)(MAGIC_LABEL) == datagram.data()[4]) {

        const_data_buffer cert_buffer;
        const_data_buffer key_buffer;
        binary_deserializer bd(datagram.data() + 5, datagram.data_size() - 5);
        bd
          >> cert_buffer
          >> key_buffer;

        auto cert = certificate::parse_der(cert_buffer);
        auto key = this->node_key_->decrypt(key_buffer);

        auto partner_id = cert.fingerprint(hash::sha256());

        //TODO: validate cert
        auto session = std::make_shared<dht_session>(
          this->sp_,
          datagram.address(),
          this->this_node_id_,
          partner_id,
          key);

        session_info.session_ = session;
        session_info.session_mutex_.unlock();

        this->sp_->get<logger>()->debug(ThisModule, "Add session %s", datagram.address().to_string().c_str());
        (*this->sp_->get<client>())->add_session(session, 0);

        co_await this->sp_->get<imessage_map>()->on_new_session(
          partner_id);
      }
      else {
        session_info.session_mutex_.unlock();
        throw std::runtime_error("Invalid protocol");
      }

      break;
    }
    case protocol_message_type_t::Failed: {
      session_info.blocked_ = true;
      logger::get(this->sp_)->trace(ThisModule, "Block session %s", datagram.address().to_string().c_str());
      (*this->sp_->get<client>())->remove_session(session_info.session_);
      session_info.session_.reset();
      session_info.update_time_ = std::chrono::steady_clock::now();
      session_info.session_mutex_.unlock();
      break;
    }
    default: {
      if (session_info.session_) {
        try {
          auto session = session_info.session_;
          session_info.session_mutex_.unlock();

          bool failed = false;
          try {
            co_await session->process_datagram(
              this->shared_from_this(),
              const_data_buffer(datagram.data(), datagram.data_size()));
          }
          catch (const std::exception & ex) {
            logger::get(this->sp_)->debug(ThisModule, "%s at process message from %s",
              ex.what(),
              datagram.address().to_string().c_str());
            failed = true;
          }

          if(failed) {
            session_info.session_mutex_.lock();
            logger::get(this->sp_)->trace(ThisModule, "Block session %s", datagram.address().to_string().c_str());
            session_info.blocked_ = true;
            (*this->sp_->get<client>())->remove_session(session_info.session_);
            session_info.session_.reset();
            session_info.update_time_ = std::chrono::steady_clock::now();
            session_info.session_mutex_.unlock();

            uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
            co_await this->write_async(udp_datagram(datagram.address(),
              const_data_buffer(out_message, sizeof(out_message))));
          }
        }
        catch (...) {
          session_info.session_mutex_.lock();
          logger::get(this->sp_)->trace(ThisModule, "Block session %s", datagram.address().to_string().c_str());
          session_info.blocked_ = true;
          (*this->sp_->get<client>())->remove_session(session_info.session_);
          session_info.session_.reset();
          session_info.update_time_ = std::chrono::steady_clock::now();
          session_info.session_mutex_.unlock();
        }
      }
      else {
        logger::get(this->sp_)->trace(ThisModule, "Block session %s", datagram.address().to_string().c_str());
        session_info.blocked_ = true;
        (*this->sp_->get<client>())->remove_session(session_info.session_);
        session_info.session_.reset();
        session_info.update_time_ = std::chrono::steady_clock::now();
        session_info.session_mutex_.unlock();

        uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
        try {
          co_await this->write_async(udp_datagram(datagram.address(),
            const_data_buffer(out_message, sizeof(out_message))));
        }
        catch (...) {
        }
      }
      break;
    }
    }
  }
}

<commit_msg>change UDP MTU size<commit_after>/*
Copyright (c) 2017, Vadim Malyshev, lboss75@gmail.com
All rights reserved
*/

#include "stdafx.h"
#include "private/udp_transport.h"
#include "messages/dht_route_messages.h"
#include "private/dht_session.h"
#include "logger.h"
#include "dht_network_client.h"
#include "dht_network_client_p.h"

vds::dht::network::udp_transport::udp_transport(){
}

vds::dht::network::udp_transport::~udp_transport() {
}

void vds::dht::network::udp_transport::start(
  const service_provider * sp,
  const std::shared_ptr<certificate> & node_cert,
  const std::shared_ptr<asymmetric_private_key> & node_key,
  uint16_t port) {
  this->send_thread_ = std::make_shared<thread_apartment>(sp);
  this->sp_ = sp;
  this->this_node_id_ = node_cert->fingerprint(hash::sha256());
  this->node_cert_ = node_cert;
  this->node_key_ = node_key;

  try {
    auto [reader, writer] = this->server_.start(sp, network_address::any_ip6(port));
    this->reader_ = reader;
    this->writer_ = writer;
  }
  catch (...) {
    auto[reader, writer] = this->server_.start(sp, network_address::any_ip4(port));
    this->reader_ = reader;
    this->writer_ = writer;
  }

      this->continue_read().detach();
}

void vds::dht::network::udp_transport::stop() {
  this->server_.stop();
}

vds::async_task<void>
vds::dht::network::udp_transport::write_async( const udp_datagram& datagram) {
  auto result = std::make_shared<vds::async_result<void>>();
  this->send_thread_->schedule([result, this, datagram]() {
    try{
      this->writer_->write_async(datagram).get();
    }
    catch (...){
      result->set_exception(std::current_exception());
      return;
    }
    result->set_value();
  });

  return result->get_future();

  //  std::unique_lock<std::debug_mutex> lock(this->write_mutex_);
  //  while(this->write_in_progress_) {
  //    this->write_cond_.wait(*reinterpret_cast<std::unique_lock<std::mutex> *>(&lock));
  //  }
  //  this->write_in_progress_ = true;
  //#ifdef _DEBUG
  //#ifndef _WIN32
  //  this->owner_id_ = syscall(SYS_gettid);
  //#else
  //  this->owner_id_ = GetCurrentThreadId();
  //#endif
  //#endif//_DEBUG
  //co_await this->writer_->write_async(sp, datagram);
}

vds::async_task<void> vds::dht::network::udp_transport::try_handshake(
                                                                  const std::string& address_str) {

  auto address = network_address::parse(this->server_.address().family(), address_str);

  this->sessions_mutex_.lock();
  auto p = this->sessions_.find(address);
  if (this->sessions_.end() == p) {
    this->sessions_mutex_.unlock();
  }
  else {
    auto & session_info = p->second;
    this->sessions_mutex_.unlock();

    session_info.session_mutex_.lock();
    if (session_info.blocked_) {
      if ((std::chrono::steady_clock::now() - session_info.update_time_) <= std::chrono::minutes(1)) {
        session_info.session_mutex_.unlock();
        co_return;
      }
    }
    else if(session_info.session_) {
      session_info.session_mutex_.unlock();
      co_return;
    }
    session_info.session_mutex_.unlock();
  }

  resizable_data_buffer out_message;
  out_message.add((uint8_t)protocol_message_type_t::HandshakeBroadcast);
  out_message.add(MAGIC_LABEL >> 24);
  out_message.add(MAGIC_LABEL >> 16);
  out_message.add(MAGIC_LABEL >> 8);
  out_message.add(MAGIC_LABEL);
  out_message.add(PROTOCOL_VERSION);

  binary_serializer bs;
  bs << this->node_cert_->der();

  out_message += bs.move_data();

  co_await this->write_async(udp_datagram(
    address,
    out_message.move_data(),
    false));
}

vds::async_task<void> vds::dht::network::udp_transport::on_timer() {
  std::list<std::shared_ptr<dht_session>> sessions;

  this->sessions_mutex_.lock_shared();
  for(auto & p : this->sessions_) {
    if (p.second.session_) {
      sessions.push_back(p.second.session_);
    }
  }
  this->sessions_mutex_.unlock_shared();

  for(auto & s : sessions) {
    co_await s->on_timer(this->shared_from_this());
  }
}

void vds::dht::network::udp_transport::get_session_statistics(session_statistic& session_statistic) {
  session_statistic.output_size_ = this->send_thread_->size();

  std::shared_lock<std::shared_mutex> lock(this->sessions_mutex_);
  for (const auto& p : this->sessions_) {
    const auto & session = p.second;
    if (session.session_) {
      session_statistic.items_.push_back(session.session_->get_statistic());
    }
  }
}

vds::async_task<void> vds::dht::network::udp_transport::continue_read(
  ) {
  for (;;) {
    udp_datagram datagram;
    try {
      datagram = co_await this->reader_->read_async();
    }
    catch(const std::system_error & ex) {
      continue;
    }

    if (this->sp_->get_shutdown_event().is_shuting_down()) {
      co_return;
    }

    this->sessions_mutex_.lock();
    auto & session_info = this->sessions_[datagram.address()];
    this->sessions_mutex_.unlock();

    session_info.session_mutex_.lock();
    if (session_info.blocked_) {
      if ((std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(1)
        && (*datagram.data() == (uint8_t)protocol_message_type_t::Handshake
        || *datagram.data() == (uint8_t)protocol_message_type_t::HandshakeBroadcast
        || *datagram.data() == (uint8_t)protocol_message_type_t::Welcome)) {
        logger::get(this->sp_)->trace(ThisModule, "Unblock session %s", datagram.address().to_string().c_str());
        session_info.blocked_ = false;
      }
      else {
        session_info.session_mutex_.unlock();
        if (*datagram.data() != (uint8_t)protocol_message_type_t::Failed
          && *datagram.data() != (uint8_t)protocol_message_type_t::Handshake
          && *datagram.data() != (uint8_t)protocol_message_type_t::HandshakeBroadcast
          && *datagram.data() != (uint8_t)protocol_message_type_t::Welcome) {
          uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
          try {
            co_await this->write_async(udp_datagram(datagram.address(),
              const_data_buffer(out_message, sizeof(out_message))));
          }
          catch (...) {
          }
        }
        continue;
      }
    }

    switch ((protocol_message_type_t)datagram.data()[0]) {
    case protocol_message_type_t::HandshakeBroadcast:
    case protocol_message_type_t::Handshake: {

      if (session_info.session_) {
        session_info.session_mutex_.unlock();
        continue;
      }

      if (
        (uint8_t)(MAGIC_LABEL >> 24) == datagram.data()[1]
        && (uint8_t)(MAGIC_LABEL >> 16) == datagram.data()[2]
        && (uint8_t)(MAGIC_LABEL >> 8) == datagram.data()[3]
        && (uint8_t)(MAGIC_LABEL) == datagram.data()[4]
        && PROTOCOL_VERSION == datagram.data()[5]) {
        binary_deserializer bd(datagram.data() + 6, datagram.data_size() - 6);
        const_data_buffer partner_node_cert_der;
        bd >> partner_node_cert_der;
        auto partner_node_cert = certificate::parse_der(partner_node_cert_der);
        auto partner_node_id = partner_node_cert.fingerprint(hash::sha256());
        if (partner_node_id == this->this_node_id_) {
          session_info.session_mutex_.unlock();
          break;
        }

        if (!session_info.session_key_ || (std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(10)) {
          session_info.update_time_ = std::chrono::steady_clock::now();
          session_info.session_key_.resize(32);
          crypto_service::rand_bytes(session_info.session_key_.data(), session_info.session_key_.size());
        }

        session_info.session_ = std::make_shared<dht_session>(
          this->sp_,
          datagram.address(),
          this->this_node_id_,
          partner_node_id,
          session_info.session_key_);

        this->sp_->get<logger>()->debug(ThisModule, "Add session %s", datagram.address().to_string().c_str());
        (*this->sp_->get<client>())->add_session(session_info.session_, 0);

        resizable_data_buffer out_message;
        out_message.add(static_cast<uint8_t>(protocol_message_type_t::Welcome));
        out_message.add(MAGIC_LABEL >> 24);
        out_message.add(MAGIC_LABEL >> 16);
        out_message.add(MAGIC_LABEL >> 8);
        out_message.add(MAGIC_LABEL);

        binary_serializer bs;
        bs
          << this->node_cert_->der()
          << partner_node_cert.public_key().encrypt(session_info.session_key_);

        session_info.session_mutex_.unlock();

        out_message += bs.move_data();
        co_await this->write_async(udp_datagram(datagram.address(), out_message.move_data(), false));
        co_await (*this->sp_->get<client>())->send_neighbors(
          message_create<messages::dht_find_node_response>(
            std::list<messages::dht_find_node_response::target_node>({
          messages::dht_find_node_response::target_node(partner_node_id, datagram.address().to_string(), 0) })));

        co_await this->sp_->get<imessage_map>()->on_new_session(
          partner_node_id);
      }
      else {
        session_info.session_mutex_.unlock();
        throw std::runtime_error("Invalid protocol");
      }
      break;
    }
    case protocol_message_type_t::Welcome: {
      if (datagram.data_size() > 5
        && (uint8_t)(MAGIC_LABEL >> 24) == datagram.data()[1]
        && (uint8_t)(MAGIC_LABEL >> 16) == datagram.data()[2]
        && (uint8_t)(MAGIC_LABEL >> 8) == datagram.data()[3]
        && (uint8_t)(MAGIC_LABEL) == datagram.data()[4]) {

        const_data_buffer cert_buffer;
        const_data_buffer key_buffer;
        binary_deserializer bd(datagram.data() + 5, datagram.data_size() - 5);
        bd
          >> cert_buffer
          >> key_buffer;

        auto cert = certificate::parse_der(cert_buffer);
        auto key = this->node_key_->decrypt(key_buffer);

        auto partner_id = cert.fingerprint(hash::sha256());

        //TODO: validate cert
        auto session = std::make_shared<dht_session>(
          this->sp_,
          datagram.address(),
          this->this_node_id_,
          partner_id,
          key);

        session_info.session_ = session;
        session_info.session_mutex_.unlock();

        this->sp_->get<logger>()->debug(ThisModule, "Add session %s", datagram.address().to_string().c_str());
        (*this->sp_->get<client>())->add_session(session, 0);

        co_await this->sp_->get<imessage_map>()->on_new_session(
          partner_id);
      }
      else {
        session_info.session_mutex_.unlock();
        throw std::runtime_error("Invalid protocol");
      }

      break;
    }
    case protocol_message_type_t::Failed: {
      logger::get(this->sp_)->trace(ThisModule, "Block session %s", datagram.address().to_string().c_str());
      (*this->sp_->get<client>())->remove_session(session_info.session_);
      session_info.blocked_ = true;
      session_info.session_.reset();
      session_info.update_time_ = std::chrono::steady_clock::now();
      session_info.session_mutex_.unlock();
      break;
    }
    default: {
      if (session_info.session_) {
        try {
          auto session = session_info.session_;
          session_info.session_mutex_.unlock();

          bool failed = false;
          try {
            co_await session->process_datagram(
              this->shared_from_this(),
              const_data_buffer(datagram.data(), datagram.data_size()));
          }
          catch (const std::exception & ex) {
            logger::get(this->sp_)->debug(ThisModule, "%s at process message from %s",
              ex.what(),
              datagram.address().to_string().c_str());
            failed = true;
          }

          if(failed) {
            session_info.session_mutex_.lock();
            logger::get(this->sp_)->trace(ThisModule, "Block session %s", datagram.address().to_string().c_str());
            (*this->sp_->get<client>())->remove_session(session_info.session_);
            session_info.blocked_ = true;
            session_info.session_.reset();
            session_info.update_time_ = std::chrono::steady_clock::now();
            session_info.session_mutex_.unlock();

            uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
            co_await this->write_async(udp_datagram(datagram.address(),
              const_data_buffer(out_message, sizeof(out_message))));
          }
        }
        catch (...) {
          session_info.session_mutex_.lock();
          logger::get(this->sp_)->trace(ThisModule, "Block session %s", datagram.address().to_string().c_str());
          (*this->sp_->get<client>())->remove_session(session_info.session_);
          session_info.blocked_ = true;
          session_info.session_.reset();
          session_info.update_time_ = std::chrono::steady_clock::now();
          session_info.session_mutex_.unlock();
        }
      }
      else {
        logger::get(this->sp_)->trace(ThisModule, "Block session %s", datagram.address().to_string().c_str());
        (*this->sp_->get<client>())->remove_session(session_info.session_);
        session_info.blocked_ = true;
        session_info.session_.reset();
        session_info.update_time_ = std::chrono::steady_clock::now();
        session_info.session_mutex_.unlock();

        uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
        try {
          co_await this->write_async(udp_datagram(datagram.address(),
            const_data_buffer(out_message, sizeof(out_message))));
        }
        catch (...) {
        }
      }
      break;
    }
    }
  }
}

<|endoftext|>


<commit_before>#include "stdafx.h"
#include "input.h"

#include "asdf_multiplat/main/mouse.h"
#include "asdf_multiplat/main/input_sdl.h"

#include "hexmap/data/hex_util.h"

#include "editor.h"

using namespace std;
using namespace glm;

namespace asdf {
namespace hexmap {
namespace editor
{

    input_handler_t::input_handler_t(editor_t& _editor)
    : editor(_editor)
    {
    }

    glm::vec2 input_handler_t::world_coords(glm::ivec2 screen_coords) const
    {
        return vec2(editor.rendered_map.camera.screen_to_world_coord(vec2(screen_coords)));
    }

    glm::vec2 input_handler_t::get_snap_position(glm::vec2 world_coord) const
    {
        auto nearest_snap = nearest_snap_point(world_coord, editor.snap_mode);
        if(glm::abs(glm::distance(nearest_snap, world_coord)) <= editor.snap_threshold)
            return nearest_snap;

        return world_coord;
    }

    bool input_handler_t::on_mouse_down(mouse_button_event_t& event)
    {
         vec2 mw = world_coords(event.mouse_state.mouse_position);
        ivec2 hx = world_to_hex_coord(mw);
         vec2 snap_pos = get_snap_position(mw);

        switch(editor.current_tool)
        {
            case editor_t::select:
            {
                auto const& sel_objs = editor.object_selection;
                vec2 const& ub = sel_objs.upper_bound;
                vec2 const& lb = sel_objs.lower_bound;

                if(util::CheckBounds(mw.x, mw.y
                                   , lb.x, lb.y
                                   , ub.x, ub.y))
                {
                    editor.drag_type = editor_t::drag_selected_items;
                }
                else
                {
                    editor.drag_type = editor_t::drag_selection_box;
                    editor.start_drag_selection(mw);
                }

                break;
            }

            case editor_t::terrain_paint:
            {
                editor.paint_terrain_start();
                editor.paint_terrain_at_coord(hx);
                return true;
            }

            case editor_t::place_objects:
            {
                editor.place_object(snap_pos);
                return true;
            }

            case editor_t::place_splines:
            {
                if(event.button == mouse_left)
                {
                    editor.spline_click(snap_pos);
                }
                else if(event.button == mouse_right)
                {
                    if(editor.is_placing_spline())
                    {
                        editor.finish_spline();
                    }
                }

                break;
            }

            case editor_t::num_tool_types: break;
        }

        return false;
    }

    bool input_handler_t::on_mouse_up(mouse_button_event_t& event)
    {
        auto mw = world_coords(event.mouse_state.mouse_position);
        auto hx = world_to_hex_coord(mw);

        switch(editor.current_tool)
        {
            case editor_t::select:
            {
                if(!event.mouse_state.is_dragging(mouse_left))
                {
                    if(handle_click_selection(event, mw))
                        event.handled = true;
                    break;
                }

                switch(editor.drag_type)
                {
                    case editor_t::drag_type_none:
                    {

                    }
                    case editor_t::drag_selection_box:
                    {
                        editor.end_drag_selection(mw);
                        break;
                    }
                    case editor_t::drag_selected_items:
                    {
                        break;
                    }
                };

                break;
            }

            case editor_t::terrain_paint:
            {
                editor.paint_terrain_at_coord(hx);
                editor.paint_terrain_end();
                return true;
            }

            case editor_t::place_objects:
            {
                break;
            }

            case editor_t::place_splines:
            {
                break;
            }

            case editor_t::num_tool_types: break;
        }

        editor.drag_type = editor_t::drag_type_none;

        return false;
    }

    bool input_handler_t::on_mouse_move(mouse_motion_event_t& event)
    {
         vec2 mw = world_coords(event.mouse_state.mouse_position);
        ivec2 hx = world_to_hex_coord(mw);
         vec2 snap_pos = get_snap_position(mw);

        switch(editor.current_tool)
        {
            case editor_t::select:
            {
                break;
            }

            case editor_t::terrain_paint:
            {
                editor.brush_pos = hex_to_world_coord(hx);

                break;
            }

            case editor_t::place_objects:
            {
                editor.brush_pos = snap_pos;
                editor.wip_object().position = snap_pos;
                break;
            }

            case editor_t::place_splines:
            {
                if(editor.is_placing_spline())
                {
                    editor.update_WIP_node(snap_pos);
                }
                break;
            }

            case editor_t::num_tool_types: break;
        }

        return false;
    }

    bool input_handler_t::on_mouse_drag(mouse_motion_event_t& event)
    {
        auto mw = world_coords(event.mouse_state.mouse_position);
        auto hx = world_to_hex_coord(mw);

        switch(editor.current_tool)
        {
            case editor_t::select:
            {
                switch(editor.drag_type)
                {
                    case editor_t::drag_type_none:
                    {
                    }
                    case editor_t::drag_selection_box:
                    {
                        editor.update_drag_selection(mw);
                        break;
                    }
                    case editor_t::drag_selected_items:
                    {
                        break;
                    }
                };

                break;
            }

            case editor_t::terrain_paint:
            {
                editor.brush_pos = hex_to_world_coord(hx);

                /// If the user is dragging the mouse
                /// paint hexes along the line they have dragged
                /// otherwise it will only paint one hex per update
                /// which looks terrible if their performance is bad
                if(event.mouse_state.mouse_button_state(mouse_left))
                {
                    vec2 mw_begin = world_coords(event.mouse_state.mouse_prev_position);
                    vec2 mw_end = world_coords(event.mouse_state.mouse_position);

                    editor.paint_terrain_along_line(mw_begin, mw_end);

                    return true;
                }

                break;
            }

            case editor_t::place_objects:
            {
                editor.brush_pos = mw;

                break;
            }

            case editor_t::place_splines:
            {
                if(editor.is_placing_spline())
                {
                    if(event.mouse_state.is_dragging(mouse_left))
                    {
                        editor.update_WIP_control_nodes(mw);
                    }

                    editor.update_WIP_node(mw);
                }
                break;
            }

            case editor_t::num_tool_types: break;
        }

        return false;
    }

    bool input_handler_t::on_mouse_wheel(mouse_wheel_event_t& event)
    {
        ASDF_UNUSED(event);

        return false;
    }

    void input_handler_t::on_key_down(SDL_Keysym keysm)
    {
        auto& key = keysm.sym;

        modifier_keys = modifier_keys_from_sdl2_event(keysm, true);

        bool is_shft = (modifier_keys & KMOD_SHIFT)>0;
        bool is_ctrl = (modifier_keys & KMOD_CTRL)>0;
        bool is_alt  = (modifier_keys & KMOD_ALT)>0;

        bool mod_ctrl_only = modifier_keys == KMOD_LCTRL
                          || modifier_keys == KMOD_RCTRL;

        if(key == SDLK_p)
        {
            LOG("Key Mods: %s %s %s %s"
                , (modifier_keys & KMOD_SHIFT)>0 ? "S" : "-"
                , (modifier_keys & KMOD_CTRL)>0  ? "C" : "-"
                , (modifier_keys & KMOD_ALT)>0   ? "A" : "-"
                , (modifier_keys & KMOD_GUI)>0   ? "^" : "-"
            );
        }

        //modifier key combos first
        if(mod_ctrl_only)
        {
            switch(key)
            {
                case SDLK_n:
                    editor.new_map_action("", editor.map_data.hex_grid.size);
                    return;

                //save on ctrl+s
                case SDLK_s:
                    editor.save_action("test_map.hxm");
                    return;

                //load on ctrl+o
                case SDLK_o:
                    editor.load_action("test_map.hxm");
                    return;

                case SDLK_z:
                    editor.undo();
                    return;

                case SDLK_y:
                    editor.redo();
                    return;

                default: return;
            }
        }
        else if(is_ctrl && is_shft)
        {
            switch(key)
            {
                case SDLK_z:
                    editor.redo();
                    return;
            }
        }
        else if ((keysm.mod & KMOD_ALT) > 0)
        {
            switch(key)
            {
                case SDLK_1: editor.set_tool(editor_t::select);            break;
                case SDLK_2: editor.set_tool(editor_t::terrain_paint);     break;
                case SDLK_3: editor.set_tool(editor_t::place_objects);      break;
                case SDLK_4: editor.set_tool(editor_t::place_splines);      break;
            }
        }
        else
        {
            if(key >= SDLK_0 && key <= SDLK_9)
            {
                ASSERT(uint64_t(key) >= uint64_t(SDLK_0), "");
                uint64_t num_from_key = uint64_t(key) - uint64_t(SDLK_0);

                switch(editor.current_tool)
                {
                    case editor_t::terrain_paint:
                    {
                        // make it 1-indexed such that pressing 1 selects the
                        // 'first'(zero'th) tile
                        editor.set_current_tile_id(num_from_key - 1);
                        break;
                    }

                    case editor_t::place_objects:
                    {
                        editor.set_current_object_id(num_from_key);
                        break;
                    }
                    case editor_t::place_splines:
                    {
                        num_from_key += 11 * (num_from_key == 0);
                        --num_from_key;

                        auto interp_type = static_cast<data::spline_t::interpolation_e>(num_from_key);
                        editor.set_current_spline_interpolation(interp_type);
                        break;
                    }
                }
            }

            switch(key)
            {
                case sdl2_input_map[cancel_action]:
                    editor.cancel_action();
                    break;

                case sdl2_input_map[set_tool_select]:
                    editor.set_tool(editor_t::select);
                    break;
                case sdl2_input_map[set_tool_terrain_paint]:
                    editor.set_tool(editor_t::terrain_paint);
                    break;
                case sdl2_input_map[set_tool_place_object]:
                    editor.set_tool(editor_t::place_objects);
                    break;
                case sdl2_input_map[set_tool_place_spline]:
                    editor.set_tool(editor_t::place_splines);
                    break;

                case SDLK_EQUALS: //plus
                    editor.new_node_style.thickness += 0.2f;
                    LOG("spline thickness: %0.3f", editor.new_node_style.thickness);
                    break;
                case SDLK_MINUS:
                    editor.new_node_style.thickness -= 0.2f;
                    LOG("spline thickness: %0.3f", editor.new_node_style.thickness);
                    break;

                default: return;
            }
        }
    }

    void input_handler_t::on_key_up(SDL_Keysym keysm)
    {
        modifier_keys = modifier_keys_from_sdl2_event(keysm, false);

        // LOG("Key Mods: %s %s %s %s"
        //     , (modifier_keys & KMOD_SHIFT)>0 ? "S" : "-"
        //     , (modifier_keys & KMOD_CTRL)>0  ? "C" : "-"
        //     , (modifier_keys & KMOD_ALT)>0   ? "A" : "-"
        //     , (modifier_keys & KMOD_GUI)>0   ? "^" : "-"
        // );
    }

    bool input_handler_t::handle_click_selection(mouse_button_event_t& event, vec2 const& mw)
    {
        size_t obj_ind = editor.map_data.object_index_at(mw);

        if(obj_ind != nullindex)
        {
            switch(modifier_keys)
            {
                case KMOD_LSHIFT: [[fallthrough]];
                case KMOD_RSHIFT:
                    editor.select_object(obj_ind);
                    break;

                case KMOD_LALT: [[fallthrough]];
                case KMOD_RALT:
                    editor.deselect_object(obj_ind);
                    break;

                case 0:
                    editor.deselect_all();
                    editor.select_object(obj_ind);
                    break;

                default:
                    return false;
            };

            return true;
        }
        else if(modifier_keys == 0)
        {
            editor.deselect_all();
            return true;
        }

        return false;
    }

}
}
}
<commit_msg>include fix. MSVC doesn't canonicalize paths when including headers (ie: two paths to the same file are treated as two separate files)<commit_after>#include "stdafx.h"
#include "input.h"

#include "asdf_multiplat/main/mouse.h"
#include "asdf_multiplat/main/input_sdl.h"

#include "data/hex_util.h"

#include "editor.h"

using namespace std;
using namespace glm;

namespace asdf {
namespace hexmap {
namespace editor
{

    input_handler_t::input_handler_t(editor_t& _editor)
    : editor(_editor)
    {
    }

    glm::vec2 input_handler_t::world_coords(glm::ivec2 screen_coords) const
    {
        return vec2(editor.rendered_map.camera.screen_to_world_coord(vec2(screen_coords)));
    }

    glm::vec2 input_handler_t::get_snap_position(glm::vec2 world_coord) const
    {
        auto nearest_snap = nearest_snap_point(world_coord, editor.snap_mode);
        if(glm::abs(glm::distance(nearest_snap, world_coord)) <= editor.snap_threshold)
            return nearest_snap;

        return world_coord;
    }

    bool input_handler_t::on_mouse_down(mouse_button_event_t& event)
    {
         vec2 mw = world_coords(event.mouse_state.mouse_position);
        ivec2 hx = world_to_hex_coord(mw);
         vec2 snap_pos = get_snap_position(mw);

        switch(editor.current_tool)
        {
            case editor_t::select:
            {
                auto const& sel_objs = editor.object_selection;
                vec2 const& ub = sel_objs.upper_bound;
                vec2 const& lb = sel_objs.lower_bound;

                if(util::CheckBounds(mw.x, mw.y
                                   , lb.x, lb.y
                                   , ub.x, ub.y))
                {
                    editor.drag_type = editor_t::drag_selected_items;
                }
                else
                {
                    editor.drag_type = editor_t::drag_selection_box;
                    editor.start_drag_selection(mw);
                }

                break;
            }

            case editor_t::terrain_paint:
            {
                editor.paint_terrain_start();
                editor.paint_terrain_at_coord(hx);
                return true;
            }

            case editor_t::place_objects:
            {
                editor.place_object(snap_pos);
                return true;
            }

            case editor_t::place_splines:
            {
                if(event.button == mouse_left)
                {
                    editor.spline_click(snap_pos);
                }
                else if(event.button == mouse_right)
                {
                    if(editor.is_placing_spline())
                    {
                        editor.finish_spline();
                    }
                }

                break;
            }

            case editor_t::num_tool_types: break;
        }

        return false;
    }

    bool input_handler_t::on_mouse_up(mouse_button_event_t& event)
    {
        auto mw = world_coords(event.mouse_state.mouse_position);
        auto hx = world_to_hex_coord(mw);

        switch(editor.current_tool)
        {
            case editor_t::select:
            {
                if(!event.mouse_state.is_dragging(mouse_left))
                {
                    if(handle_click_selection(event, mw))
                        event.handled = true;
                    break;
                }

                switch(editor.drag_type)
                {
                    case editor_t::drag_type_none:
                    {

                    }
                    case editor_t::drag_selection_box:
                    {
                        editor.end_drag_selection(mw);
                        break;
                    }
                    case editor_t::drag_selected_items:
                    {
                        break;
                    }
                };

                break;
            }

            case editor_t::terrain_paint:
            {
                editor.paint_terrain_at_coord(hx);
                editor.paint_terrain_end();
                return true;
            }

            case editor_t::place_objects:
            {
                break;
            }

            case editor_t::place_splines:
            {
                break;
            }

            case editor_t::num_tool_types: break;
        }

        editor.drag_type = editor_t::drag_type_none;

        return false;
    }

    bool input_handler_t::on_mouse_move(mouse_motion_event_t& event)
    {
         vec2 mw = world_coords(event.mouse_state.mouse_position);
        ivec2 hx = world_to_hex_coord(mw);
         vec2 snap_pos = get_snap_position(mw);

        switch(editor.current_tool)
        {
            case editor_t::select:
            {
                break;
            }

            case editor_t::terrain_paint:
            {
                editor.brush_pos = hex_to_world_coord(hx);

                break;
            }

            case editor_t::place_objects:
            {
                editor.brush_pos = snap_pos;
                editor.wip_object().position = snap_pos;
                break;
            }

            case editor_t::place_splines:
            {
                if(editor.is_placing_spline())
                {
                    editor.update_WIP_node(snap_pos);
                }
                break;
            }

            case editor_t::num_tool_types: break;
        }

        return false;
    }

    bool input_handler_t::on_mouse_drag(mouse_motion_event_t& event)
    {
        auto mw = world_coords(event.mouse_state.mouse_position);
        auto hx = world_to_hex_coord(mw);

        switch(editor.current_tool)
        {
            case editor_t::select:
            {
                switch(editor.drag_type)
                {
                    case editor_t::drag_type_none:
                    {
                    }
                    case editor_t::drag_selection_box:
                    {
                        editor.update_drag_selection(mw);
                        break;
                    }
                    case editor_t::drag_selected_items:
                    {
                        break;
                    }
                };

                break;
            }

            case editor_t::terrain_paint:
            {
                editor.brush_pos = hex_to_world_coord(hx);

                /// If the user is dragging the mouse
                /// paint hexes along the line they have dragged
                /// otherwise it will only paint one hex per update
                /// which looks terrible if their performance is bad
                if(event.mouse_state.mouse_button_state(mouse_left))
                {
                    vec2 mw_begin = world_coords(event.mouse_state.mouse_prev_position);
                    vec2 mw_end = world_coords(event.mouse_state.mouse_position);

                    editor.paint_terrain_along_line(mw_begin, mw_end);

                    return true;
                }

                break;
            }

            case editor_t::place_objects:
            {
                editor.brush_pos = mw;

                break;
            }

            case editor_t::place_splines:
            {
                if(editor.is_placing_spline())
                {
                    if(event.mouse_state.is_dragging(mouse_left))
                    {
                        editor.update_WIP_control_nodes(mw);
                    }

                    editor.update_WIP_node(mw);
                }
                break;
            }

            case editor_t::num_tool_types: break;
        }

        return false;
    }

    bool input_handler_t::on_mouse_wheel(mouse_wheel_event_t& event)
    {
        ASDF_UNUSED(event);

        return false;
    }

    void input_handler_t::on_key_down(SDL_Keysym keysm)
    {
        auto& key = keysm.sym;

        modifier_keys = modifier_keys_from_sdl2_event(keysm, true);

        bool is_shft = (modifier_keys & KMOD_SHIFT)>0;
        bool is_ctrl = (modifier_keys & KMOD_CTRL)>0;
        bool is_alt  = (modifier_keys & KMOD_ALT)>0;

        bool mod_ctrl_only = modifier_keys == KMOD_LCTRL
                          || modifier_keys == KMOD_RCTRL;

        if(key == SDLK_p)
        {
            LOG("Key Mods: %s %s %s %s"
                , (modifier_keys & KMOD_SHIFT)>0 ? "S" : "-"
                , (modifier_keys & KMOD_CTRL)>0  ? "C" : "-"
                , (modifier_keys & KMOD_ALT)>0   ? "A" : "-"
                , (modifier_keys & KMOD_GUI)>0   ? "^" : "-"
            );
        }

        //modifier key combos first
        if(mod_ctrl_only)
        {
            switch(key)
            {
                case SDLK_n:
                    editor.new_map_action("", editor.map_data.hex_grid.size);
                    return;

                //save on ctrl+s
                case SDLK_s:
                    editor.save_action("test_map.hxm");
                    return;

                //load on ctrl+o
                case SDLK_o:
                    editor.load_action("test_map.hxm");
                    return;

                case SDLK_z:
                    editor.undo();
                    return;

                case SDLK_y:
                    editor.redo();
                    return;

                default: return;
            }
        }
        else if(is_ctrl && is_shft)
        {
            switch(key)
            {
                case SDLK_z:
                    editor.redo();
                    return;
            }
        }
        else if ((keysm.mod & KMOD_ALT) > 0)
        {
            switch(key)
            {
                case SDLK_1: editor.set_tool(editor_t::select);            break;
                case SDLK_2: editor.set_tool(editor_t::terrain_paint);     break;
                case SDLK_3: editor.set_tool(editor_t::place_objects);      break;
                case SDLK_4: editor.set_tool(editor_t::place_splines);      break;
            }
        }
        else
        {
            if(key >= SDLK_0 && key <= SDLK_9)
            {
                ASSERT(uint64_t(key) >= uint64_t(SDLK_0), "");
                uint64_t num_from_key = uint64_t(key) - uint64_t(SDLK_0);

                switch(editor.current_tool)
                {
                    case editor_t::terrain_paint:
                    {
                        // make it 1-indexed such that pressing 1 selects the
                        // 'first'(zero'th) tile
                        editor.set_current_tile_id(num_from_key - 1);
                        break;
                    }

                    case editor_t::place_objects:
                    {
                        editor.set_current_object_id(num_from_key);
                        break;
                    }
                    case editor_t::place_splines:
                    {
                        num_from_key += 11 * (num_from_key == 0);
                        --num_from_key;

                        auto interp_type = static_cast<data::spline_t::interpolation_e>(num_from_key);
                        editor.set_current_spline_interpolation(interp_type);
                        break;
                    }
                }
            }

            switch(key)
            {
                case sdl2_input_map[cancel_action]:
                    editor.cancel_action();
                    break;

                case sdl2_input_map[set_tool_select]:
                    editor.set_tool(editor_t::select);
                    break;
                case sdl2_input_map[set_tool_terrain_paint]:
                    editor.set_tool(editor_t::terrain_paint);
                    break;
                case sdl2_input_map[set_tool_place_object]:
                    editor.set_tool(editor_t::place_objects);
                    break;
                case sdl2_input_map[set_tool_place_spline]:
                    editor.set_tool(editor_t::place_splines);
                    break;

                case SDLK_EQUALS: //plus
                    editor.new_node_style.thickness += 0.2f;
                    LOG("spline thickness: %0.3f", editor.new_node_style.thickness);
                    break;
                case SDLK_MINUS:
                    editor.new_node_style.thickness -= 0.2f;
                    LOG("spline thickness: %0.3f", editor.new_node_style.thickness);
                    break;

                default: return;
            }
        }
    }

    void input_handler_t::on_key_up(SDL_Keysym keysm)
    {
        modifier_keys = modifier_keys_from_sdl2_event(keysm, false);

        // LOG("Key Mods: %s %s %s %s"
        //     , (modifier_keys & KMOD_SHIFT)>0 ? "S" : "-"
        //     , (modifier_keys & KMOD_CTRL)>0  ? "C" : "-"
        //     , (modifier_keys & KMOD_ALT)>0   ? "A" : "-"
        //     , (modifier_keys & KMOD_GUI)>0   ? "^" : "-"
        // );
    }

    bool input_handler_t::handle_click_selection(mouse_button_event_t& event, vec2 const& mw)
    {
        size_t obj_ind = editor.map_data.object_index_at(mw);

        if(obj_ind != nullindex)
        {
            switch(modifier_keys)
            {
                case KMOD_LSHIFT: [[fallthrough]];
                case KMOD_RSHIFT:
                    editor.select_object(obj_ind);
                    break;

                case KMOD_LALT: [[fallthrough]];
                case KMOD_RALT:
                    editor.deselect_object(obj_ind);
                    break;

                case 0:
                    editor.deselect_all();
                    editor.select_object(obj_ind);
                    break;

                default:
                    return false;
            };

            return true;
        }
        else if(modifier_keys == 0)
        {
            editor.deselect_all();
            return true;
        }

        return false;
    }

}
}
}
<|endoftext|>


<commit_before>
#include "Singleton.h"
#include "ObjectC.h"

template class MYDLL_API Singleton<ObjectC>;

#include "Singleton.inl"

template<> ObjectC *Singleton<ObjectC>::instance = NULL;

ObjectC::ObjectC()
{
}

ObjectC::~ObjectC()
{
}

void
ObjectC::print()
{
    std::cout << "ObjectC::print()\n";
}
<commit_msg>modif<commit_after>
#include "Singleton.h"
#include "ObjectC.h"
#include "Singleton.inl"
template<> ObjectC *Singleton<ObjectC>::instance = NULL;

template class MYDLL_API Singleton<ObjectC>;

ObjectC::ObjectC()
{
}

ObjectC::~ObjectC()
{
}

void
ObjectC::print()
{
    std::cout << "ObjectC::print()\n";
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "controller_p.h"

#include "dispatcher.h"
#include "action.h"

#include <QMetaClassInfo>
#include <QStringBuilder>
#include <QDebug>

using namespace Cutelyst;

Controller::Controller(QObject *parent) :
    QObject(parent),
    d_ptr(new ControllerPrivate)
{
}

Controller::~Controller()
{
}

QByteArray Controller::ns() const
{
    Q_D(const Controller);
    return d->ns;
}

Action *Controller::actionFor(const QByteArray &name) const
{
    Q_D(const Controller);
    return d->dispatcher->getAction(name, d->ns);
}

bool Controller::operator==(const char *className)
{
    return !qstrcmp(metaObject()->className(), className);
}

void Controller::Begin(Context *ctx)
{

}

bool Controller::Auto(Context *ctx)
{
    return true;
}

void Controller::End(Context *ctx)
{

}

void Controller::init()
{
    Q_D(Controller);

    QByteArray controlerNS;
    for (int i = 0; i < metaObject()->classInfoCount(); ++i) {
        if (metaObject()->classInfo(i).name() == QLatin1String("Namespace")) {
            controlerNS = metaObject()->classInfo(i).value();
            break;
        }
    }

    if (controlerNS.isNull()) {
        QString className = metaObject()->className();
        bool lastWasUpper = true;

        for (int i = 0; i < className.length(); ++i) {
            if (className.at(i).toLower() == className.at(i)) {
                controlerNS.append(className.at(i));
                lastWasUpper = false;
            } else {
                if (lastWasUpper) {
                    controlerNS.append(className.at(i).toLower());
                } else {
                    controlerNS.append(QLatin1Char('/') % className.at(i).toLower());
                }
                lastWasUpper = true;
            }
        }
    }
    d->ns = controlerNS;
}

void Controller::setupActions(Dispatcher *dispatcher)
{
    Q_D(Controller);

    ActionList beginList;
    beginList = dispatcher->getActions(QByteArrayLiteral("Begin"), d->ns);
    if (!beginList.isEmpty()) {
        d->begin = beginList.last();
        d->actionSteps.append(d->begin);
    }

    d->autoList = dispatcher->getActions(QByteArrayLiteral("Auto"), d->ns);
    d->actionSteps.append(d->autoList);

    ActionList endList;
    endList = dispatcher->getActions(QByteArrayLiteral("End"), d->ns);
    if (!endList.isEmpty()) {
        d->end = endList.last();
    }
}

void Controller::_DISPATCH(Context *ctx)
{
    Q_D(Controller);

    // Dispatch to _BEGIN, _AUTO and _ACTION
    ActionList steps = d->actionSteps;
    steps.append(ctx->action());

    Q_FOREACH (Action *action, steps) {
        if (!action->dispatch(ctx)) {
            break;
        }
    }

    d->end->dispatch(ctx);
}

bool Controller::_BEGIN(Context *ctx)
{
//    qDebug() << Q_FUNC_INFO;
    Q_D(Controller);
    if (d->begin) {
        d->begin->dispatch(ctx);
        return !ctx->error();
    }
    return true;
}

bool Controller::_AUTO(Context *ctx)
{
//    qDebug() << Q_FUNC_INFO;
    Q_D(Controller);
    Q_FOREACH (Action *autoAction, d->autoList) {
        if (!autoAction->dispatch(ctx)) {
            return false;
        }
    }
    return true;
}

bool Controller::_ACTION(Context *ctx)
{
//    qDebug() << Q_FUNC_INFO;
    if (ctx->action()) {
        return ctx->action()->dispatch(ctx);
    }
    return !ctx->error();
}

bool Controller::_END(Context *ctx)
{
//    qDebug() << Q_FUNC_INFO;
    Q_D(Controller);
    if (d->end) {
        d->end->dispatch(ctx);
        return !ctx->error();
    }
    return true;
}
<commit_msg>Avoid detaching and appending to a QList<commit_after>/*
 * Copyright (C) 2013 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "controller_p.h"

#include "dispatcher.h"
#include "action.h"

#include <QMetaClassInfo>
#include <QStringBuilder>
#include <QDebug>

using namespace Cutelyst;

Controller::Controller(QObject *parent) :
    QObject(parent),
    d_ptr(new ControllerPrivate)
{
}

Controller::~Controller()
{
}

QByteArray Controller::ns() const
{
    Q_D(const Controller);
    return d->ns;
}

Action *Controller::actionFor(const QByteArray &name) const
{
    Q_D(const Controller);
    return d->dispatcher->getAction(name, d->ns);
}

bool Controller::operator==(const char *className)
{
    return !qstrcmp(metaObject()->className(), className);
}

void Controller::Begin(Context *ctx)
{

}

bool Controller::Auto(Context *ctx)
{
    return true;
}

void Controller::End(Context *ctx)
{

}

void Controller::init()
{
    Q_D(Controller);

    QByteArray controlerNS;
    for (int i = 0; i < metaObject()->classInfoCount(); ++i) {
        if (metaObject()->classInfo(i).name() == QLatin1String("Namespace")) {
            controlerNS = metaObject()->classInfo(i).value();
            break;
        }
    }

    if (controlerNS.isNull()) {
        QString className = metaObject()->className();
        bool lastWasUpper = true;

        for (int i = 0; i < className.length(); ++i) {
            if (className.at(i).toLower() == className.at(i)) {
                controlerNS.append(className.at(i));
                lastWasUpper = false;
            } else {
                if (lastWasUpper) {
                    controlerNS.append(className.at(i).toLower());
                } else {
                    controlerNS.append(QLatin1Char('/') % className.at(i).toLower());
                }
                lastWasUpper = true;
            }
        }
    }
    d->ns = controlerNS;
}

void Controller::setupActions(Dispatcher *dispatcher)
{
    Q_D(Controller);

    ActionList beginList;
    beginList = dispatcher->getActions(QByteArrayLiteral("Begin"), d->ns);
    if (!beginList.isEmpty()) {
        d->begin = beginList.last();
        d->actionSteps.append(d->begin);
    }

    d->autoList = dispatcher->getActions(QByteArrayLiteral("Auto"), d->ns);
    d->actionSteps.append(d->autoList);

    ActionList endList;
    endList = dispatcher->getActions(QByteArrayLiteral("End"), d->ns);
    if (!endList.isEmpty()) {
        d->end = endList.last();
    }
}

void Controller::_DISPATCH(Context *ctx)
{
    Q_D(Controller);

    bool failedState = false;

    // Dispatch to _BEGIN and _AUTO
    Q_FOREACH (Action *action, d->actionSteps) {
        if (!action->dispatch(ctx)) {
            failedState = true;
            break;
        }
    }

    // Dispatch to _ACTION
    if (!failedState) {
        ctx->action()->dispatch(ctx);
    }

    // Dispatch to _END
    d->end->dispatch(ctx);
}

bool Controller::_BEGIN(Context *ctx)
{
//    qDebug() << Q_FUNC_INFO;
    Q_D(Controller);
    if (d->begin) {
        d->begin->dispatch(ctx);
        return !ctx->error();
    }
    return true;
}

bool Controller::_AUTO(Context *ctx)
{
//    qDebug() << Q_FUNC_INFO;
    Q_D(Controller);
    Q_FOREACH (Action *autoAction, d->autoList) {
        if (!autoAction->dispatch(ctx)) {
            return false;
        }
    }
    return true;
}

bool Controller::_ACTION(Context *ctx)
{
//    qDebug() << Q_FUNC_INFO;
    if (ctx->action()) {
        return ctx->action()->dispatch(ctx);
    }
    return !ctx->error();
}

bool Controller::_END(Context *ctx)
{
//    qDebug() << Q_FUNC_INFO;
    Q_D(Controller);
    if (d->end) {
        d->end->dispatch(ctx);
        return !ctx->error();
    }
    return true;
}
<|endoftext|>


<commit_before>/// \file ROOT/RClusterPool.hxx
/// \ingroup NTuple ROOT7
/// \author Jakob Blomer <jblomer@cern.ch>
/// \date 2020-03-11
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

/*************************************************************************
 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT7_RClusterPool
#define ROOT7_RClusterPool

#include <ROOT/RCluster.hxx>
#include <ROOT/RNTupleUtil.hxx>
#include <ROOT/RPageStorage.hxx> // for ColumnSet_t

#include <condition_variable>
#include <memory>
#include <mutex>
#include <future>
#include <queue>
#include <thread>
#include <set>
#include <vector>

namespace ROOT {
namespace Experimental {
namespace Detail {

class RPageSource;

// clang-format off
/**
\class ROOT::Experimental::Detail::RClusterPool
\ingroup NTuple
\brief Managed a set of clusters containing compressed and packed pages

The cluster pool steers the preloading of (partial) clusters. There is a two-step pipeline: in a first step,
compressed pages are read from clusters into a memory buffer. The second pipeline step decompresses the pages
and pushes them into the page pool. The actual logic of reading and unzipping is implemented by the page source.
The cluster pool only orchestrates the work queues for reading and unzipping. It uses two threads, one for
each pipeline step. The I/O thread for reading waits for data from storage and generates no CPU load. In contrast,
the unzip thread is supposed to submit multi-threaded, CPU heavy work to the application's task scheduler.

The unzipping step of the pipeline therefore behaves differently depending on whether or not implicit multi-threadin
is turned on. If it is turned off, i.e. in a single-threaded environment, the cluster pool will only read the
compressed pages and the page source has to uncompresses pages at a later point when data from the page is requested.
*/
// clang-format on
class RClusterPool {
private:
   /// Maximum number of queued cluster requests for the I/O thread. A single request can span mutliple clusters.
   static constexpr unsigned int kWorkQueueLimit = 4;

   /// Request to load a subset of the columns of a particular cluster.
   /// Work items come in groups and are executed by the page source.
   struct RReadItem {
      std::promise<std::unique_ptr<RCluster>> fPromise;
      DescriptorId_t fClusterId = kInvalidDescriptorId;
      RPageSource::ColumnSet_t fColumns;
   };

   /// Request to decompress and if necessary unpack compressed pages. The unzipped pages
   /// are supposed to be pushed into the page pool by the page source.
   struct RUnzipItem {
      std::unique_ptr<RCluster> fCluster;
      std::promise<std::unique_ptr<RCluster>> fPromise;
   };

   /// Clusters that are currently being processed by the pipeline.  Every in-flight cluster has a corresponding
   /// work item, first a read item and then an unzip item.
   struct RInFlightCluster {
      std::future<std::unique_ptr<RCluster>> fFuture;
      DescriptorId_t fClusterId = kInvalidDescriptorId;
      RPageSource::ColumnSet_t fColumns;
      /// By the time a cluster has been loaded, this cluster might not be necessary anymore. This can happen if
      /// there are jumps in the access pattern (i.e. the access pattern deviates from linear access).
      bool fIsExpired = false;

      bool operator ==(const RInFlightCluster &other) const { return fClusterId == other.fClusterId && fColumns == other.fColumns; }
      bool operator !=(const RInFlightCluster &other) const { return !(*this == other); }
      /// First order by cluster id, then by number of columns, than by the column ids in fColumns
      bool operator <(const RInFlightCluster &other) const;
   };

   /// Every cluster pool is responsible for exactly one page source that triggers loading of the clusters
   /// (GetCluster()) and is used for implementing the I/O and cluster memory allocation (PageSource::LoadCluster()).
   RPageSource &fPageSource;
   /// The number of clusters before the currently active cluster that should stay in the pool if present
   unsigned int fWindowPre;
   /// The number of desired clusters in the pool, including the currently active cluster
   unsigned int fWindowPost;
   /// The cache of clusters around the currently active cluster
   std::vector<std::unique_ptr<RCluster>> fPool;

   /// Protects the shared state between the main thread and the pipeline threads, namely the read and unzip
   /// work queues and the in-flight clusters vector
   std::mutex fLockWorkQueue;
   /// The clusters that were handed off to the I/O thread
   std::vector<RInFlightCluster> fInFlightClusters;
   /// Signals a non-empty I/O work queue
   std::condition_variable fCvHasReadWork;
   /// The communication channel to the I/O thread
   std::queue<RReadItem> fReadQueue;
   /// The lock associated with the fCvHasUnzipWork conditional variable
   std::mutex fLockUnzipQueue;
   /// Signals non-empty unzip work queue
   std::condition_variable fCvHasUnzipWork;
   /// The communication channel between the I/O thread and the unzip thread
   std::queue<RUnzipItem> fUnzipQueue;

   /// The I/O thread calls RPageSource::LoadCluster() asynchronously.  The thread is mostly waiting for the
   /// data to arrive (blocked by the kernel) and therefore can safely run in addition to the application
   /// main threads.
   std::thread fThreadIo;
   /// The unzip thread takes a loaded cluster and passes it to fPageSource->UnzipCluster() on it. If implicit
   /// multi-threading is turned off, the UnzipCluster() call is a no-op. Otherwise, the UnzipCluster() call
   /// schedules the unzipping of pages using the application's task scheduler.
   std::thread fThreadUnzip;

   /// Every cluster id has at most one corresponding RCluster pointer in the pool
   RCluster *FindInPool(DescriptorId_t clusterId) const;
   /// Returns an index of an unused element in fPool; callers of this function (GetCluster() and WaitFor())
   /// make sure that a free slot actually exists
   size_t FindFreeSlot() const;
   /// The I/O thread routine, there is exactly one I/O thread in-flight for every cluster pool
   void ExecReadClusters();
   /// The unzip thread routine which takes a loaded cluster and passes it to fPageSource.UnzipCluster (which
   /// might be a no-op if IMT is off). Marks the cluster as ready to be picked up by the main thread.
   void ExecUnzipClusters();
   /// Returns the given cluster from the pool, which needs to contain at least the columns `columns`.
   /// Executed at the end of GetCluster when all missing data pieces have been sent to the load queue.
   /// Ideally, the function returns without blocking if the cluster is already in the pool.
   RCluster *WaitFor(DescriptorId_t clusterId, const RPageSource::ColumnSet_t &columns);

public:
   static constexpr unsigned int kDefaultPoolSize = 4;
   RClusterPool(RPageSource &pageSource, unsigned int size);
   explicit RClusterPool(RPageSource &pageSource) : RClusterPool(pageSource, kDefaultPoolSize) {}
   RClusterPool(const RClusterPool &other) = delete;
   RClusterPool &operator =(const RClusterPool &other) = delete;
   ~RClusterPool();

   unsigned int GetWindowPre() const { return fWindowPre; }
   unsigned int GetWindowPost() const { return fWindowPost; }

   /// Returns the requested cluster either from the pool or, in case of a cache miss, lets the I/O thread load
   /// the cluster in the pool, blocks until done, and then returns it.  Triggers along the way the background loading
   /// of the following fWindowPost number of clusters.  The returned cluster has at least all the pages of `columns`
   /// and possibly pages of other columns, too.  If implicit multi-threading is turned on, the uncompressed pages
   /// of the returned cluster are already pushed into the page pool associated with the page source upon return.
   /// The cluster remains valid until the next call to GetCluster().
   RCluster *GetCluster(DescriptorId_t clusterId, const RPageSource::ColumnSet_t &columns);
}; // class RClusterPool

} // namespace Detail
} // namespace Experimental
} // namespace ROOT

#endif
<commit_msg>[ntuple] Remove unnecessary constant<commit_after>/// \file ROOT/RClusterPool.hxx
/// \ingroup NTuple ROOT7
/// \author Jakob Blomer <jblomer@cern.ch>
/// \date 2020-03-11
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

/*************************************************************************
 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT7_RClusterPool
#define ROOT7_RClusterPool

#include <ROOT/RCluster.hxx>
#include <ROOT/RNTupleUtil.hxx>
#include <ROOT/RPageStorage.hxx> // for ColumnSet_t

#include <condition_variable>
#include <memory>
#include <mutex>
#include <future>
#include <queue>
#include <thread>
#include <set>
#include <vector>

namespace ROOT {
namespace Experimental {
namespace Detail {

class RPageSource;

// clang-format off
/**
\class ROOT::Experimental::Detail::RClusterPool
\ingroup NTuple
\brief Managed a set of clusters containing compressed and packed pages

The cluster pool steers the preloading of (partial) clusters. There is a two-step pipeline: in a first step,
compressed pages are read from clusters into a memory buffer. The second pipeline step decompresses the pages
and pushes them into the page pool. The actual logic of reading and unzipping is implemented by the page source.
The cluster pool only orchestrates the work queues for reading and unzipping. It uses two threads, one for
each pipeline step. The I/O thread for reading waits for data from storage and generates no CPU load. In contrast,
the unzip thread is supposed to submit multi-threaded, CPU heavy work to the application's task scheduler.

The unzipping step of the pipeline therefore behaves differently depending on whether or not implicit multi-threadin
is turned on. If it is turned off, i.e. in a single-threaded environment, the cluster pool will only read the
compressed pages and the page source has to uncompresses pages at a later point when data from the page is requested.
*/
// clang-format on
class RClusterPool {
private:
   /// Request to load a subset of the columns of a particular cluster.
   /// Work items come in groups and are executed by the page source.
   struct RReadItem {
      std::promise<std::unique_ptr<RCluster>> fPromise;
      DescriptorId_t fClusterId = kInvalidDescriptorId;
      RPageSource::ColumnSet_t fColumns;
   };

   /// Request to decompress and if necessary unpack compressed pages. The unzipped pages
   /// are supposed to be pushed into the page pool by the page source.
   struct RUnzipItem {
      std::unique_ptr<RCluster> fCluster;
      std::promise<std::unique_ptr<RCluster>> fPromise;
   };

   /// Clusters that are currently being processed by the pipeline.  Every in-flight cluster has a corresponding
   /// work item, first a read item and then an unzip item.
   struct RInFlightCluster {
      std::future<std::unique_ptr<RCluster>> fFuture;
      DescriptorId_t fClusterId = kInvalidDescriptorId;
      RPageSource::ColumnSet_t fColumns;
      /// By the time a cluster has been loaded, this cluster might not be necessary anymore. This can happen if
      /// there are jumps in the access pattern (i.e. the access pattern deviates from linear access).
      bool fIsExpired = false;

      bool operator ==(const RInFlightCluster &other) const { return fClusterId == other.fClusterId && fColumns == other.fColumns; }
      bool operator !=(const RInFlightCluster &other) const { return !(*this == other); }
      /// First order by cluster id, then by number of columns, than by the column ids in fColumns
      bool operator <(const RInFlightCluster &other) const;
   };

   /// Every cluster pool is responsible for exactly one page source that triggers loading of the clusters
   /// (GetCluster()) and is used for implementing the I/O and cluster memory allocation (PageSource::LoadCluster()).
   RPageSource &fPageSource;
   /// The number of clusters before the currently active cluster that should stay in the pool if present
   unsigned int fWindowPre;
   /// The number of desired clusters in the pool, including the currently active cluster
   unsigned int fWindowPost;
   /// The cache of clusters around the currently active cluster
   std::vector<std::unique_ptr<RCluster>> fPool;

   /// Protects the shared state between the main thread and the pipeline threads, namely the read and unzip
   /// work queues and the in-flight clusters vector
   std::mutex fLockWorkQueue;
   /// The clusters that were handed off to the I/O thread
   std::vector<RInFlightCluster> fInFlightClusters;
   /// Signals a non-empty I/O work queue
   std::condition_variable fCvHasReadWork;
   /// The communication channel to the I/O thread
   std::queue<RReadItem> fReadQueue;
   /// The lock associated with the fCvHasUnzipWork conditional variable
   std::mutex fLockUnzipQueue;
   /// Signals non-empty unzip work queue
   std::condition_variable fCvHasUnzipWork;
   /// The communication channel between the I/O thread and the unzip thread
   std::queue<RUnzipItem> fUnzipQueue;

   /// The I/O thread calls RPageSource::LoadCluster() asynchronously.  The thread is mostly waiting for the
   /// data to arrive (blocked by the kernel) and therefore can safely run in addition to the application
   /// main threads.
   std::thread fThreadIo;
   /// The unzip thread takes a loaded cluster and passes it to fPageSource->UnzipCluster() on it. If implicit
   /// multi-threading is turned off, the UnzipCluster() call is a no-op. Otherwise, the UnzipCluster() call
   /// schedules the unzipping of pages using the application's task scheduler.
   std::thread fThreadUnzip;

   /// Every cluster id has at most one corresponding RCluster pointer in the pool
   RCluster *FindInPool(DescriptorId_t clusterId) const;
   /// Returns an index of an unused element in fPool; callers of this function (GetCluster() and WaitFor())
   /// make sure that a free slot actually exists
   size_t FindFreeSlot() const;
   /// The I/O thread routine, there is exactly one I/O thread in-flight for every cluster pool
   void ExecReadClusters();
   /// The unzip thread routine which takes a loaded cluster and passes it to fPageSource.UnzipCluster (which
   /// might be a no-op if IMT is off). Marks the cluster as ready to be picked up by the main thread.
   void ExecUnzipClusters();
   /// Returns the given cluster from the pool, which needs to contain at least the columns `columns`.
   /// Executed at the end of GetCluster when all missing data pieces have been sent to the load queue.
   /// Ideally, the function returns without blocking if the cluster is already in the pool.
   RCluster *WaitFor(DescriptorId_t clusterId, const RPageSource::ColumnSet_t &columns);

public:
   static constexpr unsigned int kDefaultPoolSize = 4;
   RClusterPool(RPageSource &pageSource, unsigned int size);
   explicit RClusterPool(RPageSource &pageSource) : RClusterPool(pageSource, kDefaultPoolSize) {}
   RClusterPool(const RClusterPool &other) = delete;
   RClusterPool &operator =(const RClusterPool &other) = delete;
   ~RClusterPool();

   unsigned int GetWindowPre() const { return fWindowPre; }
   unsigned int GetWindowPost() const { return fWindowPost; }

   /// Returns the requested cluster either from the pool or, in case of a cache miss, lets the I/O thread load
   /// the cluster in the pool, blocks until done, and then returns it.  Triggers along the way the background loading
   /// of the following fWindowPost number of clusters.  The returned cluster has at least all the pages of `columns`
   /// and possibly pages of other columns, too.  If implicit multi-threading is turned on, the uncompressed pages
   /// of the returned cluster are already pushed into the page pool associated with the page source upon return.
   /// The cluster remains valid until the next call to GetCluster().
   RCluster *GetCluster(DescriptorId_t clusterId, const RPageSource::ColumnSet_t &columns);
}; // class RClusterPool

} // namespace Detail
} // namespace Experimental
} // namespace ROOT

#endif
<|endoftext|>