<commit_before>/*
 Copyright (c) 2019 Anon authors, see AUTHORS file.

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

#include <epc.h>
#include <netdb.h>
#include "dns_lookup.h"

endpoint_cluster::endpoint_cluster(const char *host, int port,
                                   bool do_tls,
                                   const tls_context *tls_ctx,
                                   int max_conn_per_ep,
                                   int lookup_frequency_in_seconds)
    : host_(host),
      port_(port),
      do_tls_(do_tls),
      tls_ctx_(tls_ctx),
      max_conn_per_ep_(max_conn_per_ep),
      lookup_frequency_in_seconds_(lookup_frequency_in_seconds),
      last_lookup_time_((struct timespec){}),
      round_robin_index_(0),
      looking_up_endpoints_(false),
      max_io_block_time_(k_default_io_block_time),
      retries_enabled_(true)
{
}

void endpoint_cluster::update_endpoints()
{
  auto addrs = dns_lookup::get_addrinfo(host_.c_str(), port_);

  fiber_lock l(mtx_);
  if (addrs.first != 0 || addrs.second.size() == 0)
  {
    // if there are no current endpoints that are already known
    // for this host name, then this error object will be thrown
    // in the fiber has initiated this call to update_endpoints.
    // In that case, the fact that it is an instance of fiber_io_error
    // will cause the exponential backoff logic to kick in and
    // re-attempt the operation - optimistically hoping that whatever
    // is causing the error will resolve itself
    lookup_err_ = std::unique_ptr<fiber_io_error>(new fiber_io_error(Log::fmt(
        [&](std::ostream &msg) { msg << "dns lookup failed for: " << host_ << ", error: " << (addrs.first < 0 ? gai_strerror(addrs.first) : error_string(addrs.first)); })));
  }
  else
  {
    // build a map which is the union of all currently
    // known endpoints plus the ones that were just
    // returned in the call to get_adddrinfo.
    auto now = cur_time();
    std::map<sockaddr_in6, std::shared_ptr<endpoint>> endpoints;
    for (auto &ep : endpoints_)
      endpoints.insert(std::make_pair(ep->addr_, ep));
    for (auto &addr : addrs.second)
    {
      auto it = endpoints.find(addr);
      if (it == endpoints.end())
        endpoints.insert(std::make_pair(addr, std::make_shared<endpoint>(addr)));
      else
        it->second->last_lookup_time_ = now;
    }

    // we use a simple policy rule that we are willing to
    // keep attempting to use any ip address that was returned
    // by getaddrinfo up to 10 times whatever the
    // dns lookup_frequency_in_seconds_ is.  If any cached
    // open sockets become unusable they will be deleted.
    // If new connection attempts fail to one of these
    // "slighly old" endpoints we will delete that endpoint
    // from our list.
    auto oldest = now - lookup_frequency_in_seconds_ * 10;
    for (auto it = endpoints.begin(); it != endpoints.end();)
    {
      if (it->second->last_lookup_time_ < oldest)
      {
#ifdef ANON_LOG_DNS_LOOKUP
        anon_log("aging out endpoint " << it->second->addr_ << ", because it was " << to_seconds(now - it->second->last_lookup_time_) << " seconds old");
#endif
        it = endpoints.erase(it);
      }
      else
        ++it;
    }
    endpoints_.resize(endpoints.size());
    int indx = 0;
#ifdef ANON_LOG_DNS_LOOKUP
    anon_log("host: " << host_);
#endif
    for (auto &p : endpoints)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      auto age = to_seconds(now - p.second->last_lookup_time_);
      if (age < 0.0)
        age = 0.0;
      anon_log(" using " << p.second->addr_ << ", lookup age: " << age << " seconds");
#endif
      endpoints_[indx++] = p.second;
    }
  }

  last_lookup_time_ = cur_time();
  looking_up_endpoints_ = false;
  cond_.notify_all();
}

// This is called either by erase_if_empty ep has only one open
// socket (erase_if_empty is called if there is a problem with
// that socket) - or if we get a connection error trying to
// open a new socket for this endpoint.  We delete the endpoint
// from our list to force dns to run again next time we try
// to get a functional pipe
void endpoint_cluster::erase(const std::shared_ptr<endpoint> &ep)
{
  fiber_lock l(mtx_);
  auto it = endpoints_.begin();
  while (it != endpoints_.end())
  {
    if (*it == ep)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("endpoint_cluster::erase emptying");
#endif
      endpoints_.erase(it);
      return;
    }
    it++;
  }
}

// This is called when there is some error that has occurred
// on a socket related to this endpoint.  If it turns out that
// that socket was the only one in existence for this endpoint
// then remove the matching entry in the endpoints_ vector.
// This causes that endpoint to get deleted (when the other
// shared pointers to it are destructed) and means that the
// endpoint_cluster itself will not attempt to use this endpoint
// again without first going back through dns resolution.
// This cleans up cases where the ip address itself is no
// longer good and dns lookup is correctly no longer reporting
// that ip address.  We have a policy of continuing to attempt
// to use that ip address for some period after dns lookup has
// returned it.
void endpoint_cluster::erase_if_empty(const std::shared_ptr<endpoint> &ep)
{
  {
    fiber_lock l(ep->mtx_);
    if (ep->socks_.size() != 0 || ep->outstanding_requests_ != 1)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("endpoint_cluster::erase_if_empty, not emptying, ep->socks_.size(): " << ep->socks_.size() << ", ep->outstanding_requests_: " << ep->outstanding_requests_);
#endif
      return;
    }
  }

  erase(ep);
}

void endpoint_cluster::delete_cached_endpoints()
{
  fiber_lock l(mtx_);
  endpoints_.resize(0);
}

namespace
{

class cleanup
{
public:
  cleanup(const std::weak_ptr<endpoint_cluster::endpoint> &wep,
          const std::shared_ptr<endpoint_cluster::endpoint::sock> &sock,
          const std::weak_ptr<endpoint_cluster>& wcp)
      : wep(wep),
        sock(sock),
        cache(false),
        exception_thrown(true)
#ifdef ANON_LOG_DNS_LOOKUP
        ,addr(wep.lock()->addr_)
#endif
  {
  }

  ~cleanup()
  {
    auto ep = wep.lock();
    if (ep)
    {
      fiber_lock l(ep->mtx_);
      --ep->outstanding_requests_;
      if (exception_thrown) {
        auto cp = wcp.lock();
        if (cp)
          cp->erase(ep);
      }
      if (cache)
      {
        sock->idle_start_time = cur_time();
        ep->socks_.push(sock);
      }
      ep->error_ |= exception_thrown;
      ep->cond_.notify_all();
    }
    else
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("epc appears that endpoint for " << addr << " was deleted prior to callback returning");
#endif
    }
  }

  std::weak_ptr<endpoint_cluster::endpoint> wep;
  std::shared_ptr<endpoint_cluster::endpoint::sock> sock;
  std::weak_ptr<endpoint_cluster> wcp;
  bool cache;
  bool exception_thrown;

#ifdef ANON_LOG_DNS_LOOKUP
  const struct sockaddr_in6 addr;
#endif
};

class eraser
{
public:
  eraser(endpoint_cluster *epc,
         const std::shared_ptr<endpoint_cluster::endpoint> &ep)
      : epc(epc),
        ep(ep),
        success(false)
  {
  }

  ~eraser()
  {
    if (!success)
      epc->erase(ep);
  }

  endpoint_cluster *epc;
  std::shared_ptr<endpoint_cluster::endpoint> ep;
  bool success;
};

} // namespace

void endpoint_cluster::do_with_connected_pipe(const std::function<bool(const pipe_t *pipe)> &f)
{
  // if there are currently no available endpoints, or if it has been too long since we have
  // last looked up endpoints, then restart the lookup process.  If there are no endpoints
  // then wait until the lookup is complete, otherwise continue to use the endpoints we
  // already know about and let the lookup complete asynchronously.  If there was an error
  // attempting the lookup throw that error if there are no current endpoints, otherwise
  // ignore the error
  std::shared_ptr<endpoint> ep;
  std::weak_ptr<endpoint> wep;
  {
    fiber_lock l(mtx_);
    if (endpoints_.size() == 0 || to_seconds(cur_time() - last_lookup_time_) > lookup_frequency_in_seconds_)
    {
      if (!looking_up_endpoints_)
      {
        looking_up_endpoints_ = true;
        lookup_err_.reset();
        std::weak_ptr<endpoint_cluster> wp = shared_from_this();
        auto stack_size = 16 * 1024;
        fiber::run_in_fiber(
            [wp] {
              auto ths = wp.lock();
              if (ths)
              {
                ths->update_endpoints();
              }
            },
            stack_size, "epc, update_endpoints");
      }
      while (endpoints_.size() == 0)
      {
        cond_.wait(l);
        if (lookup_err_)
          throw *lookup_err_;
      }
    }
    ep = endpoints_[round_robin_index_++ % endpoints_.size()];
    wep = ep;
  }

  std::shared_ptr<endpoint::sock> sock;
  {
    fiber_lock l(ep->mtx_);

    while (ep->outstanding_requests_ >= max_conn_per_ep_)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("waiting to use endpoint because it has " << ep->outstanding_requests_ << " current connections, maximum allowed: " << max_conn_per_ep_);
#endif
      ep->cond_.wait(l);
    }
    if (ep->error_)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("endpoint for " << ep->addr_ << " had previous error (and has been removed from endpoint list) - trying again");
#endif
      l.unlock();
      ep.reset();
      do_with_connected_pipe(f);
      return;
    }
    ++ep->outstanding_requests_;

    while (!sock && ep->socks_.size() != 0)
    {
      auto s = ep->socks_.front();
      ep->socks_.pop();
      if (cur_time() < s->idle_start_time + k_max_idle_time)
        sock = s;
#ifdef ANON_LOG_DNS_LOOKUP
      else
        anon_log("releasing socket (fd=" << s->pipe_->get_fd() << ", from " << ep->addr_ << ") because it has been idle for " << cur_time() - s->idle_start_time << " seconds");
#endif
    }

    if (sock)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("epc reused connection (fd=" << sock->pipe_->get_fd() << ", idle_time=" << cur_time() - sock->idle_start_time << ") to " << ep->addr_);
#endif
    }
    else
    {
      l.unlock();
      eraser era(this, ep);
      auto conn = tcp_client::connect((struct sockaddr *)&ep->addr_, ep->addr_.sin6_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
      era.success = true;
      if (conn.first != 0)
      {
        erase(ep);
        anon_throw(fiber_io_error, "tcp connect failed for " << ep->addr_ << ", error: " << error_string(conn.first));
      }
      conn.second->limit_io_block_time(max_io_block_time_);

      std::unique_ptr<pipe_t> pipe;
      if (do_tls_)
        pipe = std::unique_ptr<pipe_t>(new tls_pipe(std::move(conn.second),
                                                    true, // client (not server)
                                                    true, // verify_peer
                                                    true, // doSNI
                                                    host_.c_str(),
                                                    *tls_ctx_));
      else
        pipe = std::unique_ptr<pipe_t>(conn.second.release());
      sock = std::shared_ptr<endpoint::sock>(new endpoint::sock(std::move(pipe)));
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("epc established new connection (fd=" << sock->pipe_->get_fd() << ") to " << ep->addr_);
#endif
    }
  }

  // we let go of the endpoint itself and only hold
  // the weak pointer to it across the call.  This
  // lets it timeout and get deleted more smoothly.
  cleanup cu(wep, sock, shared_from_this());
  ep.reset();
  cu.cache = f(sock->pipe_.get());
  cu.exception_thrown = false;
}
<commit_msg>more logging<commit_after>/*
 Copyright (c) 2019 Anon authors, see AUTHORS file.

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
*/

#include <epc.h>
#include <netdb.h>
#include "dns_lookup.h"

endpoint_cluster::endpoint_cluster(const char *host, int port,
                                   bool do_tls,
                                   const tls_context *tls_ctx,
                                   int max_conn_per_ep,
                                   int lookup_frequency_in_seconds)
    : host_(host),
      port_(port),
      do_tls_(do_tls),
      tls_ctx_(tls_ctx),
      max_conn_per_ep_(max_conn_per_ep),
      lookup_frequency_in_seconds_(lookup_frequency_in_seconds),
      last_lookup_time_((struct timespec){}),
      round_robin_index_(0),
      looking_up_endpoints_(false),
      max_io_block_time_(k_default_io_block_time),
      retries_enabled_(true)
{
}

void endpoint_cluster::update_endpoints()
{
  auto addrs = dns_lookup::get_addrinfo(host_.c_str(), port_);

  fiber_lock l(mtx_);
  if (addrs.first != 0 || addrs.second.size() == 0)
  {
    // if there are no current endpoints that are already known
    // for this host name, then this error object will be thrown
    // in the fiber has initiated this call to update_endpoints.
    // In that case, the fact that it is an instance of fiber_io_error
    // will cause the exponential backoff logic to kick in and
    // re-attempt the operation - optimistically hoping that whatever
    // is causing the error will resolve itself
    lookup_err_ = std::unique_ptr<fiber_io_error>(new fiber_io_error(Log::fmt(
        [&](std::ostream &msg) { msg << "dns lookup failed for: " << host_ << ", error: " << (addrs.first < 0 ? gai_strerror(addrs.first) : error_string(addrs.first)); })));
  }
  else
  {
    // build a map which is the union of all currently
    // known endpoints plus the ones that were just
    // returned in the call to get_adddrinfo.
    auto now = cur_time();
    std::map<sockaddr_in6, std::shared_ptr<endpoint>> endpoints;
    for (auto &ep : endpoints_)
      endpoints.insert(std::make_pair(ep->addr_, ep));
    for (auto &addr : addrs.second)
    {
      auto it = endpoints.find(addr);
      if (it == endpoints.end())
        endpoints.insert(std::make_pair(addr, std::make_shared<endpoint>(addr)));
      else
        it->second->last_lookup_time_ = now;
    }

    // we use a simple policy rule that we are willing to
    // keep attempting to use any ip address that was returned
    // by getaddrinfo up to 10 times whatever the
    // dns lookup_frequency_in_seconds_ is.  If any cached
    // open sockets become unusable they will be deleted.
    // If new connection attempts fail to one of these
    // "slighly old" endpoints we will delete that endpoint
    // from our list.
    auto oldest = now - lookup_frequency_in_seconds_ * 10;
    for (auto it = endpoints.begin(); it != endpoints.end();)
    {
      if (it->second->last_lookup_time_ < oldest)
      {
#ifdef ANON_LOG_DNS_LOOKUP
        anon_log("aging out endpoint " << it->second->addr_ << ", because it was " << to_seconds(now - it->second->last_lookup_time_) << " seconds old");
#endif
        it = endpoints.erase(it);
      }
      else
        ++it;
    }
    endpoints_.resize(endpoints.size());
    int indx = 0;
#ifdef ANON_LOG_DNS_LOOKUP
    anon_log("host: " << host_);
#endif
    for (auto &p : endpoints)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      auto age = to_seconds(now - p.second->last_lookup_time_);
      if (age < 0.0)
        age = 0.0;
      anon_log(" using " << p.second->addr_ << ", lookup age: " << age << " seconds");
#endif
      endpoints_[indx++] = p.second;
    }
  }

  last_lookup_time_ = cur_time();
  looking_up_endpoints_ = false;
  cond_.notify_all();
}

// This is called either by erase_if_empty ep has only one open
// socket (erase_if_empty is called if there is a problem with
// that socket) - or if we get a connection error trying to
// open a new socket for this endpoint.  We delete the endpoint
// from our list to force dns to run again next time we try
// to get a functional pipe
void endpoint_cluster::erase(const std::shared_ptr<endpoint> &ep)
{
  fiber_lock l(mtx_);
  auto it = endpoints_.begin();
  while (it != endpoints_.end())
  {
    if (*it == ep)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("endpoint_cluster::erase emptying");
#endif
      endpoints_.erase(it);
      return;
    }
    it++;
  }
#ifdef ANON_LOG_DNS_LOOKUP
  anon_log("failed to erase endpoint " << ep->addr_);
#endif
}

// This is called when there is some error that has occurred
// on a socket related to this endpoint.  If it turns out that
// that socket was the only one in existence for this endpoint
// then remove the matching entry in the endpoints_ vector.
// This causes that endpoint to get deleted (when the other
// shared pointers to it are destructed) and means that the
// endpoint_cluster itself will not attempt to use this endpoint
// again without first going back through dns resolution.
// This cleans up cases where the ip address itself is no
// longer good and dns lookup is correctly no longer reporting
// that ip address.  We have a policy of continuing to attempt
// to use that ip address for some period after dns lookup has
// returned it.
void endpoint_cluster::erase_if_empty(const std::shared_ptr<endpoint> &ep)
{
  {
    fiber_lock l(ep->mtx_);
    if (ep->socks_.size() != 0 || ep->outstanding_requests_ != 1)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("endpoint_cluster::erase_if_empty, not emptying, ep->socks_.size(): " << ep->socks_.size() << ", ep->outstanding_requests_: " << ep->outstanding_requests_);
#endif
      return;
    }
  }

  erase(ep);
}

void endpoint_cluster::delete_cached_endpoints()
{
  fiber_lock l(mtx_);
  endpoints_.resize(0);
}

namespace
{

class cleanup
{
public:
  cleanup(const std::weak_ptr<endpoint_cluster::endpoint> &wep,
          const std::shared_ptr<endpoint_cluster::endpoint::sock> &sock,
          const std::weak_ptr<endpoint_cluster>& wcp)
      : wep(wep),
        sock(sock),
        cache(false),
        exception_thrown(true)
#ifdef ANON_LOG_DNS_LOOKUP
        ,addr(wep.lock()->addr_)
#endif
  {
  }

  ~cleanup()
  {
    auto ep = wep.lock();
    if (ep)
    {
      fiber_lock l(ep->mtx_);
      --ep->outstanding_requests_;
      if (exception_thrown) {
#ifdef ANON_LOG_DNS_LOOKUP
        anon_log("cleanup::~cleanup - exception thrown");
#endif
        auto cp = wcp.lock();
        if (cp) {
#ifdef ANON_LOG_DNS_LOOKUP
        anon_log("cleanup::~cleanup - exception thrown, erasing " << ep->addr_);
#endif
          cp->erase(ep);
        }
      }
      if (cache)
      {
        sock->idle_start_time = cur_time();
        ep->socks_.push(sock);
      }
      ep->error_ |= exception_thrown;
      ep->cond_.notify_all();
    }
    else
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("epc appears that endpoint for " << addr << " was deleted prior to callback returning");
#endif
    }
  }

  std::weak_ptr<endpoint_cluster::endpoint> wep;
  std::shared_ptr<endpoint_cluster::endpoint::sock> sock;
  std::weak_ptr<endpoint_cluster> wcp;
  bool cache;
  bool exception_thrown;

#ifdef ANON_LOG_DNS_LOOKUP
  const struct sockaddr_in6 addr;
#endif
};

class eraser
{
public:
  eraser(endpoint_cluster *epc,
         const std::shared_ptr<endpoint_cluster::endpoint> &ep)
      : epc(epc),
        ep(ep),
        success(false)
  {
  }

  ~eraser()
  {
    if (!success)
      epc->erase(ep);
  }

  endpoint_cluster *epc;
  std::shared_ptr<endpoint_cluster::endpoint> ep;
  bool success;
};

} // namespace

void endpoint_cluster::do_with_connected_pipe(const std::function<bool(const pipe_t *pipe)> &f)
{
  // if there are currently no available endpoints, or if it has been too long since we have
  // last looked up endpoints, then restart the lookup process.  If there are no endpoints
  // then wait until the lookup is complete, otherwise continue to use the endpoints we
  // already know about and let the lookup complete asynchronously.  If there was an error
  // attempting the lookup throw that error if there are no current endpoints, otherwise
  // ignore the error
  std::shared_ptr<endpoint> ep;
  std::weak_ptr<endpoint> wep;
  {
    fiber_lock l(mtx_);
    if (endpoints_.size() == 0 || to_seconds(cur_time() - last_lookup_time_) > lookup_frequency_in_seconds_)
    {
      if (!looking_up_endpoints_)
      {
        looking_up_endpoints_ = true;
        lookup_err_.reset();
        std::weak_ptr<endpoint_cluster> wp = shared_from_this();
        auto stack_size = 16 * 1024;
        fiber::run_in_fiber(
            [wp] {
              auto ths = wp.lock();
              if (ths)
              {
                ths->update_endpoints();
              }
            },
            stack_size, "epc, update_endpoints");
      }
      while (endpoints_.size() == 0)
      {
        cond_.wait(l);
        if (lookup_err_)
          throw *lookup_err_;
      }
    }
    ep = endpoints_[round_robin_index_++ % endpoints_.size()];
    wep = ep;
  }

  std::shared_ptr<endpoint::sock> sock;
  {
    fiber_lock l(ep->mtx_);

    while (ep->outstanding_requests_ >= max_conn_per_ep_)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("waiting to use endpoint because it has " << ep->outstanding_requests_ << " current connections, maximum allowed: " << max_conn_per_ep_);
#endif
      ep->cond_.wait(l);
    }
    if (ep->error_)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("endpoint for " << ep->addr_ << " had previous error (and has been removed from endpoint list) - trying again");
#endif
      l.unlock();
      ep.reset();
      do_with_connected_pipe(f);
      return;
    }
    ++ep->outstanding_requests_;

    while (!sock && ep->socks_.size() != 0)
    {
      auto s = ep->socks_.front();
      ep->socks_.pop();
      if (cur_time() < s->idle_start_time + k_max_idle_time)
        sock = s;
#ifdef ANON_LOG_DNS_LOOKUP
      else
        anon_log("releasing socket (fd=" << s->pipe_->get_fd() << ", from " << ep->addr_ << ") because it has been idle for " << cur_time() - s->idle_start_time << " seconds");
#endif
    }

    if (sock)
    {
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("epc reused connection (fd=" << sock->pipe_->get_fd() << ", idle_time=" << cur_time() - sock->idle_start_time << ") to " << ep->addr_);
#endif
    }
    else
    {
      l.unlock();
      eraser era(this, ep);
      auto conn = tcp_client::connect((struct sockaddr *)&ep->addr_, ep->addr_.sin6_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
      era.success = true;
      if (conn.first != 0)
      {
        erase(ep);
        anon_throw(fiber_io_error, "tcp connect failed for " << ep->addr_ << ", error: " << error_string(conn.first));
      }
      conn.second->limit_io_block_time(max_io_block_time_);

      std::unique_ptr<pipe_t> pipe;
      if (do_tls_)
        pipe = std::unique_ptr<pipe_t>(new tls_pipe(std::move(conn.second),
                                                    true, // client (not server)
                                                    true, // verify_peer
                                                    true, // doSNI
                                                    host_.c_str(),
                                                    *tls_ctx_));
      else
        pipe = std::unique_ptr<pipe_t>(conn.second.release());
      sock = std::shared_ptr<endpoint::sock>(new endpoint::sock(std::move(pipe)));
#ifdef ANON_LOG_DNS_LOOKUP
      anon_log("epc established new connection (fd=" << sock->pipe_->get_fd() << ") to " << ep->addr_);
#endif
    }
  }

  // we let go of the endpoint itself and only hold
  // the weak pointer to it across the call.  This
  // lets it timeout and get deleted more smoothly.
  cleanup cu(wep, sock, shared_from_this());
  ep.reset();
  cu.cache = f(sock->pipe_.get());
  cu.exception_thrown = false;
}
<|endoftext|>


<commit_before><commit_msg>fixes #0003000: View property Angular Deflection can freeze FC<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 *          Nathan Binkert
 */

#include <iostream>
#include <string>
#include <sstream>

#include "base/cprintf.hh"
#include "base/loader/symtab.hh"
#include "base/misc.hh"
#include "base/output.hh"
#include "cpu/base.hh"
#include "cpu/cpuevent.hh"
#include "cpu/thread_context.hh"
#include "cpu/profile.hh"
#include "sim/sim_exit.hh"
#include "sim/param.hh"
#include "sim/process.hh"
#include "sim/sim_events.hh"
#include "sim/system.hh"

#include "base/trace.hh"

// Hack
#include "sim/stat_control.hh"

using namespace std;

vector<BaseCPU *> BaseCPU::cpuList;

// This variable reflects the max number of threads in any CPU.  Be
// careful to only use it once all the CPUs that you care about have
// been initialized
int maxThreadsPerCPU = 1;

CPUProgressEvent::CPUProgressEvent(EventQueue *q, Tick ival,
                                   BaseCPU *_cpu)
    : Event(q, Event::Stat_Event_Pri), interval(ival),
      lastNumInst(0), cpu(_cpu)
{
    if (interval)
        schedule(curTick + interval);
}

void
CPUProgressEvent::process()
{
    Counter temp = cpu->totalInstructions();
#ifndef NDEBUG
    double ipc = double(temp - lastNumInst) / (interval / cpu->cycles(1));

    DPRINTFN("%s progress event, instructions committed: %lli, IPC: %0.8d\n",
             cpu->name(), temp - lastNumInst, ipc);
    ipc = 0.0;
#else
    cprintf("%lli: %s progress event, instructions committed: %lli\n",
            curTick, cpu->name(), temp - lastNumInst);
#endif
    lastNumInst = temp;
    schedule(curTick + interval);
}

const char *
CPUProgressEvent::description()
{
    return "CPU Progress event";
}

#if FULL_SYSTEM
BaseCPU::BaseCPU(Params *p)
    : MemObject(p->name), clock(p->clock), instCnt(0),
      params(p), number_of_threads(p->numberOfThreads), system(p->system),
      phase(p->phase)
#else
BaseCPU::BaseCPU(Params *p)
    : MemObject(p->name), clock(p->clock), params(p),
      number_of_threads(p->numberOfThreads), system(p->system),
      phase(p->phase)
#endif
{
//    currentTick = curTick;
    DPRINTF(FullCPU, "BaseCPU: Creating object, mem address %#x.\n", this);

    // add self to global list of CPUs
    cpuList.push_back(this);

    DPRINTF(FullCPU, "BaseCPU: CPU added to cpuList, mem address %#x.\n",
            this);

    if (number_of_threads > maxThreadsPerCPU)
        maxThreadsPerCPU = number_of_threads;

    // allocate per-thread instruction-based event queues
    comInstEventQueue = new EventQueue *[number_of_threads];
    for (int i = 0; i < number_of_threads; ++i)
        comInstEventQueue[i] = new EventQueue("instruction-based event queue");

    //
    // set up instruction-count-based termination events, if any
    //
    if (p->max_insts_any_thread != 0)
        for (int i = 0; i < number_of_threads; ++i)
            schedExitSimLoop("a thread reached the max instruction count",
                             p->max_insts_any_thread, 0,
                             comInstEventQueue[i]);

    if (p->max_insts_all_threads != 0) {
        // allocate & initialize shared downcounter: each event will
        // decrement this when triggered; simulation will terminate
        // when counter reaches 0
        int *counter = new int;
        *counter = number_of_threads;
        for (int i = 0; i < number_of_threads; ++i)
            new CountedExitEvent(comInstEventQueue[i],
                "all threads reached the max instruction count",
                p->max_insts_all_threads, *counter);
    }

    // allocate per-thread load-based event queues
    comLoadEventQueue = new EventQueue *[number_of_threads];
    for (int i = 0; i < number_of_threads; ++i)
        comLoadEventQueue[i] = new EventQueue("load-based event queue");

    //
    // set up instruction-count-based termination events, if any
    //
    if (p->max_loads_any_thread != 0)
        for (int i = 0; i < number_of_threads; ++i)
            schedExitSimLoop("a thread reached the max load count",
                             p->max_loads_any_thread, 0,
                             comLoadEventQueue[i]);

    if (p->max_loads_all_threads != 0) {
        // allocate & initialize shared downcounter: each event will
        // decrement this when triggered; simulation will terminate
        // when counter reaches 0
        int *counter = new int;
        *counter = number_of_threads;
        for (int i = 0; i < number_of_threads; ++i)
            new CountedExitEvent(comLoadEventQueue[i],
                "all threads reached the max load count",
                p->max_loads_all_threads, *counter);
    }

    functionTracingEnabled = false;
    if (p->functionTrace) {
        functionTraceStream = simout.find(csprintf("ftrace.%s", name()));
        currentFunctionStart = currentFunctionEnd = 0;
        functionEntryTick = p->functionTraceStart;

        if (p->functionTraceStart == 0) {
            functionTracingEnabled = true;
        } else {
            Event *e =
                new EventWrapper<BaseCPU, &BaseCPU::enableFunctionTrace>(this,
                                                                         true);
            e->schedule(p->functionTraceStart);
        }
    }
#if FULL_SYSTEM
    profileEvent = NULL;
    if (params->profile)
        profileEvent = new ProfileEvent(this, params->profile);
#endif
}

BaseCPU::Params::Params()
{
#if FULL_SYSTEM
    profile = false;
#endif
    checker = NULL;
}

void
BaseCPU::enableFunctionTrace()
{
    functionTracingEnabled = true;
}

BaseCPU::~BaseCPU()
{
}

void
BaseCPU::init()
{
    if (!params->deferRegistration)
        registerThreadContexts();
}

void
BaseCPU::startup()
{
#if FULL_SYSTEM
    if (!params->deferRegistration && profileEvent)
        profileEvent->schedule(curTick);
#endif

    if (params->progress_interval) {
        new CPUProgressEvent(&mainEventQueue, params->progress_interval,
                             this);
    }
}

void
BaseCPU::regStats()
{
    using namespace Stats;

    numCycles
        .name(name() + ".numCycles")
        .desc("number of cpu cycles simulated")
        ;

    int size = threadContexts.size();
    if (size > 1) {
        for (int i = 0; i < size; ++i) {
            stringstream namestr;
            ccprintf(namestr, "%s.ctx%d", name(), i);
            threadContexts[i]->regStats(namestr.str());
        }
    } else if (size == 1)
        threadContexts[0]->regStats(name());

#if FULL_SYSTEM
#endif
}

Tick
BaseCPU::nextCycle()
{
    Tick next_tick = curTick - phase + clock - 1;
    next_tick -= (next_tick % clock);
    next_tick += phase;
    return next_tick;
}

Tick
BaseCPU::nextCycle(Tick begin_tick)
{
    Tick next_tick = begin_tick;
    next_tick -= (next_tick % clock);
    next_tick += phase;

    while (next_tick < curTick)
        next_tick += clock;

    assert(next_tick >= curTick);
    return next_tick;
}

void
BaseCPU::registerThreadContexts()
{
    for (int i = 0; i < threadContexts.size(); ++i) {
        ThreadContext *tc = threadContexts[i];

#if FULL_SYSTEM
        int id = params->cpu_id;
        if (id != -1)
            id += i;

        tc->setCpuId(system->registerThreadContext(tc, id));
#else
        tc->setCpuId(tc->getProcessPtr()->registerThreadContext(tc));
#endif
    }
}

int
BaseCPU::findContext(ThreadContext *tc)
{
    for (int i = 0; i < threadContexts.size(); ++i) {
        if (tc == threadContexts[i])
            return i;
    }
    return 0;
}

void
BaseCPU::switchOut()
{
//    panic("This CPU doesn't support sampling!");
#if FULL_SYSTEM
    if (profileEvent && profileEvent->scheduled())
        profileEvent->deschedule();
#endif
}

void
BaseCPU::takeOverFrom(BaseCPU *oldCPU)
{
    assert(threadContexts.size() == oldCPU->threadContexts.size());

    for (int i = 0; i < threadContexts.size(); ++i) {
        ThreadContext *newTC = threadContexts[i];
        ThreadContext *oldTC = oldCPU->threadContexts[i];

        newTC->takeOverFrom(oldTC);

        CpuEvent::replaceThreadContext(oldTC, newTC);

        assert(newTC->readCpuId() == oldTC->readCpuId());
#if FULL_SYSTEM
        system->replaceThreadContext(newTC, newTC->readCpuId());
#else
        assert(newTC->getProcessPtr() == oldTC->getProcessPtr());
        newTC->getProcessPtr()->replaceThreadContext(newTC, newTC->readCpuId());
#endif

//    TheISA::compareXCs(oldXC, newXC);
    }

#if FULL_SYSTEM
    interrupts = oldCPU->interrupts;

    for (int i = 0; i < threadContexts.size(); ++i)
        threadContexts[i]->profileClear();

    // The Sampler must take care of this!
//    if (profileEvent)
//        profileEvent->schedule(curTick);
#endif
}

#if FULL_SYSTEM
BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, int _interval)
    : Event(&mainEventQueue), cpu(_cpu), interval(_interval)
{ }

void
BaseCPU::ProfileEvent::process()
{
    for (int i = 0, size = cpu->threadContexts.size(); i < size; ++i) {
        ThreadContext *tc = cpu->threadContexts[i];
        tc->profileSample();
    }

    schedule(curTick + interval);
}

void
BaseCPU::post_interrupt(int int_type)
{
    interrupts.post(int_type);
}

void
BaseCPU::post_interrupt(int int_num, int index)
{
    interrupts.post(int_num, index);
}

void
BaseCPU::clear_interrupt(int int_num, int index)
{
    interrupts.clear(int_num, index);
}

void
BaseCPU::clear_interrupts()
{
    interrupts.clear_all();
}

void
BaseCPU::serialize(std::ostream &os)
{
    SERIALIZE_SCALAR(instCnt);
    interrupts.serialize(os);
}

void
BaseCPU::unserialize(Checkpoint *cp, const std::string &section)
{
    UNSERIALIZE_SCALAR(instCnt);
    interrupts.unserialize(cp, section);
}

#endif // FULL_SYSTEM

void
BaseCPU::traceFunctionsInternal(Addr pc)
{
    if (!debugSymbolTable)
        return;

    // if pc enters different function, print new function symbol and
    // update saved range.  Otherwise do nothing.
    if (pc < currentFunctionStart || pc >= currentFunctionEnd) {
        string sym_str;
        bool found = debugSymbolTable->findNearestSymbol(pc, sym_str,
                                                         currentFunctionStart,
                                                         currentFunctionEnd);

        if (!found) {
            // no symbol found: use addr as label
            sym_str = csprintf("0x%x", pc);
            currentFunctionStart = pc;
            currentFunctionEnd = pc + 1;
        }

        ccprintf(*functionTraceStream, " (%d)\n%d: %s",
                 curTick - functionEntryTick, curTick, sym_str);
        functionEntryTick = curTick;
    }
}

DEFINE_SIM_OBJECT_CLASS_NAME("BaseCPU", BaseCPU)
<commit_msg>Set progress_interval in terms of CPU cycles.<commit_after>/*
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Steve Reinhardt
 *          Nathan Binkert
 */

#include <iostream>
#include <string>
#include <sstream>

#include "base/cprintf.hh"
#include "base/loader/symtab.hh"
#include "base/misc.hh"
#include "base/output.hh"
#include "cpu/base.hh"
#include "cpu/cpuevent.hh"
#include "cpu/thread_context.hh"
#include "cpu/profile.hh"
#include "sim/sim_exit.hh"
#include "sim/param.hh"
#include "sim/process.hh"
#include "sim/sim_events.hh"
#include "sim/system.hh"

#include "base/trace.hh"

// Hack
#include "sim/stat_control.hh"

using namespace std;

vector<BaseCPU *> BaseCPU::cpuList;

// This variable reflects the max number of threads in any CPU.  Be
// careful to only use it once all the CPUs that you care about have
// been initialized
int maxThreadsPerCPU = 1;

CPUProgressEvent::CPUProgressEvent(EventQueue *q, Tick ival,
                                   BaseCPU *_cpu)
    : Event(q, Event::Stat_Event_Pri), interval(ival),
      lastNumInst(0), cpu(_cpu)
{
    if (interval)
        schedule(curTick + interval);
}

void
CPUProgressEvent::process()
{
    Counter temp = cpu->totalInstructions();
#ifndef NDEBUG
    double ipc = double(temp - lastNumInst) / (interval / cpu->cycles(1));

    DPRINTFN("%s progress event, instructions committed: %lli, IPC: %0.8d\n",
             cpu->name(), temp - lastNumInst, ipc);
    ipc = 0.0;
#else
    cprintf("%lli: %s progress event, instructions committed: %lli\n",
            curTick, cpu->name(), temp - lastNumInst);
#endif
    lastNumInst = temp;
    schedule(curTick + interval);
}

const char *
CPUProgressEvent::description()
{
    return "CPU Progress event";
}

#if FULL_SYSTEM
BaseCPU::BaseCPU(Params *p)
    : MemObject(p->name), clock(p->clock), instCnt(0),
      params(p), number_of_threads(p->numberOfThreads), system(p->system),
      phase(p->phase)
#else
BaseCPU::BaseCPU(Params *p)
    : MemObject(p->name), clock(p->clock), params(p),
      number_of_threads(p->numberOfThreads), system(p->system),
      phase(p->phase)
#endif
{
//    currentTick = curTick;
    DPRINTF(FullCPU, "BaseCPU: Creating object, mem address %#x.\n", this);

    // add self to global list of CPUs
    cpuList.push_back(this);

    DPRINTF(FullCPU, "BaseCPU: CPU added to cpuList, mem address %#x.\n",
            this);

    if (number_of_threads > maxThreadsPerCPU)
        maxThreadsPerCPU = number_of_threads;

    // allocate per-thread instruction-based event queues
    comInstEventQueue = new EventQueue *[number_of_threads];
    for (int i = 0; i < number_of_threads; ++i)
        comInstEventQueue[i] = new EventQueue("instruction-based event queue");

    //
    // set up instruction-count-based termination events, if any
    //
    if (p->max_insts_any_thread != 0)
        for (int i = 0; i < number_of_threads; ++i)
            schedExitSimLoop("a thread reached the max instruction count",
                             p->max_insts_any_thread, 0,
                             comInstEventQueue[i]);

    if (p->max_insts_all_threads != 0) {
        // allocate & initialize shared downcounter: each event will
        // decrement this when triggered; simulation will terminate
        // when counter reaches 0
        int *counter = new int;
        *counter = number_of_threads;
        for (int i = 0; i < number_of_threads; ++i)
            new CountedExitEvent(comInstEventQueue[i],
                "all threads reached the max instruction count",
                p->max_insts_all_threads, *counter);
    }

    // allocate per-thread load-based event queues
    comLoadEventQueue = new EventQueue *[number_of_threads];
    for (int i = 0; i < number_of_threads; ++i)
        comLoadEventQueue[i] = new EventQueue("load-based event queue");

    //
    // set up instruction-count-based termination events, if any
    //
    if (p->max_loads_any_thread != 0)
        for (int i = 0; i < number_of_threads; ++i)
            schedExitSimLoop("a thread reached the max load count",
                             p->max_loads_any_thread, 0,
                             comLoadEventQueue[i]);

    if (p->max_loads_all_threads != 0) {
        // allocate & initialize shared downcounter: each event will
        // decrement this when triggered; simulation will terminate
        // when counter reaches 0
        int *counter = new int;
        *counter = number_of_threads;
        for (int i = 0; i < number_of_threads; ++i)
            new CountedExitEvent(comLoadEventQueue[i],
                "all threads reached the max load count",
                p->max_loads_all_threads, *counter);
    }

    functionTracingEnabled = false;
    if (p->functionTrace) {
        functionTraceStream = simout.find(csprintf("ftrace.%s", name()));
        currentFunctionStart = currentFunctionEnd = 0;
        functionEntryTick = p->functionTraceStart;

        if (p->functionTraceStart == 0) {
            functionTracingEnabled = true;
        } else {
            Event *e =
                new EventWrapper<BaseCPU, &BaseCPU::enableFunctionTrace>(this,
                                                                         true);
            e->schedule(p->functionTraceStart);
        }
    }
#if FULL_SYSTEM
    profileEvent = NULL;
    if (params->profile)
        profileEvent = new ProfileEvent(this, params->profile);
#endif
}

BaseCPU::Params::Params()
{
#if FULL_SYSTEM
    profile = false;
#endif
    checker = NULL;
}

void
BaseCPU::enableFunctionTrace()
{
    functionTracingEnabled = true;
}

BaseCPU::~BaseCPU()
{
}

void
BaseCPU::init()
{
    if (!params->deferRegistration)
        registerThreadContexts();
}

void
BaseCPU::startup()
{
#if FULL_SYSTEM
    if (!params->deferRegistration && profileEvent)
        profileEvent->schedule(curTick);
#endif

    if (params->progress_interval) {
        new CPUProgressEvent(&mainEventQueue,
                             cycles(params->progress_interval),
                             this);
    }
}

void
BaseCPU::regStats()
{
    using namespace Stats;

    numCycles
        .name(name() + ".numCycles")
        .desc("number of cpu cycles simulated")
        ;

    int size = threadContexts.size();
    if (size > 1) {
        for (int i = 0; i < size; ++i) {
            stringstream namestr;
            ccprintf(namestr, "%s.ctx%d", name(), i);
            threadContexts[i]->regStats(namestr.str());
        }
    } else if (size == 1)
        threadContexts[0]->regStats(name());

#if FULL_SYSTEM
#endif
}

Tick
BaseCPU::nextCycle()
{
    Tick next_tick = curTick - phase + clock - 1;
    next_tick -= (next_tick % clock);
    next_tick += phase;
    return next_tick;
}

Tick
BaseCPU::nextCycle(Tick begin_tick)
{
    Tick next_tick = begin_tick;
    next_tick -= (next_tick % clock);
    next_tick += phase;

    while (next_tick < curTick)
        next_tick += clock;

    assert(next_tick >= curTick);
    return next_tick;
}

void
BaseCPU::registerThreadContexts()
{
    for (int i = 0; i < threadContexts.size(); ++i) {
        ThreadContext *tc = threadContexts[i];

#if FULL_SYSTEM
        int id = params->cpu_id;
        if (id != -1)
            id += i;

        tc->setCpuId(system->registerThreadContext(tc, id));
#else
        tc->setCpuId(tc->getProcessPtr()->registerThreadContext(tc));
#endif
    }
}

int
BaseCPU::findContext(ThreadContext *tc)
{
    for (int i = 0; i < threadContexts.size(); ++i) {
        if (tc == threadContexts[i])
            return i;
    }
    return 0;
}

void
BaseCPU::switchOut()
{
//    panic("This CPU doesn't support sampling!");
#if FULL_SYSTEM
    if (profileEvent && profileEvent->scheduled())
        profileEvent->deschedule();
#endif
}

void
BaseCPU::takeOverFrom(BaseCPU *oldCPU)
{
    assert(threadContexts.size() == oldCPU->threadContexts.size());

    for (int i = 0; i < threadContexts.size(); ++i) {
        ThreadContext *newTC = threadContexts[i];
        ThreadContext *oldTC = oldCPU->threadContexts[i];

        newTC->takeOverFrom(oldTC);

        CpuEvent::replaceThreadContext(oldTC, newTC);

        assert(newTC->readCpuId() == oldTC->readCpuId());
#if FULL_SYSTEM
        system->replaceThreadContext(newTC, newTC->readCpuId());
#else
        assert(newTC->getProcessPtr() == oldTC->getProcessPtr());
        newTC->getProcessPtr()->replaceThreadContext(newTC, newTC->readCpuId());
#endif

//    TheISA::compareXCs(oldXC, newXC);
    }

#if FULL_SYSTEM
    interrupts = oldCPU->interrupts;

    for (int i = 0; i < threadContexts.size(); ++i)
        threadContexts[i]->profileClear();

    // The Sampler must take care of this!
//    if (profileEvent)
//        profileEvent->schedule(curTick);
#endif
}

#if FULL_SYSTEM
BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, int _interval)
    : Event(&mainEventQueue), cpu(_cpu), interval(_interval)
{ }

void
BaseCPU::ProfileEvent::process()
{
    for (int i = 0, size = cpu->threadContexts.size(); i < size; ++i) {
        ThreadContext *tc = cpu->threadContexts[i];
        tc->profileSample();
    }

    schedule(curTick + interval);
}

void
BaseCPU::post_interrupt(int int_type)
{
    interrupts.post(int_type);
}

void
BaseCPU::post_interrupt(int int_num, int index)
{
    interrupts.post(int_num, index);
}

void
BaseCPU::clear_interrupt(int int_num, int index)
{
    interrupts.clear(int_num, index);
}

void
BaseCPU::clear_interrupts()
{
    interrupts.clear_all();
}

void
BaseCPU::serialize(std::ostream &os)
{
    SERIALIZE_SCALAR(instCnt);
    interrupts.serialize(os);
}

void
BaseCPU::unserialize(Checkpoint *cp, const std::string &section)
{
    UNSERIALIZE_SCALAR(instCnt);
    interrupts.unserialize(cp, section);
}

#endif // FULL_SYSTEM

void
BaseCPU::traceFunctionsInternal(Addr pc)
{
    if (!debugSymbolTable)
        return;

    // if pc enters different function, print new function symbol and
    // update saved range.  Otherwise do nothing.
    if (pc < currentFunctionStart || pc >= currentFunctionEnd) {
        string sym_str;
        bool found = debugSymbolTable->findNearestSymbol(pc, sym_str,
                                                         currentFunctionStart,
                                                         currentFunctionEnd);

        if (!found) {
            // no symbol found: use addr as label
            sym_str = csprintf("0x%x", pc);
            currentFunctionStart = pc;
            currentFunctionEnd = pc + 1;
        }

        ccprintf(*functionTraceStream, " (%d)\n%d: %s",
                 curTick - functionEntryTick, curTick, sym_str);
        functionEntryTick = curTick;
    }
}

DEFINE_SIM_OBJECT_CLASS_NAME("BaseCPU", BaseCPU)
<|endoftext|>


<commit_before>#include <chrono>

#include "Decoder_NO.hpp"

using namespace aff3ct::module;

template <typename B, typename R>
Decoder_NO<B,R>
::Decoder_NO(const int K, const int N, const int n_frames, const std::string name)
: Decoder_SISO<B,R>(K, N, n_frames, 1, name)
{
	if (N != K)
		throw std::invalid_argument("aff3ct::module::Decoder_NO: \"K\" and \"N\" have to be equal.");
}

template <typename B, typename R>
Decoder_NO<B,R>
::~Decoder_NO()
{
}

template <typename B, typename R>
void Decoder_NO<B,R>
::_hard_decode(const R *Y_N, B *V_K)
{
	auto t_store = std::chrono::steady_clock::now(); // --------------------------------------------------------- STORE
	// take the hard decision
	auto vec_loop_size = (this->K / mipp::nElReg<R>()) * mipp::nElReg<R>();
	for (auto i = 0; i < vec_loop_size; i += mipp::nElReg<R>())
	{
		const auto r_Y_N = mipp::Reg<R>(&Y_N[i]);
		const auto r_s = mipp::cast<R,B>(r_Y_N.sign()) >> (sizeof(B) * 8 - 1); // s[i] = Y_N[i] < 0;
		r_s.store(&V_K[i]);
	}
	for (auto i = vec_loop_size; i < this->K; i++)
		V_K[i] = Y_N[i] < 0;
	auto d_store = std::chrono::steady_clock::now() - t_store;

	this->d_store_total += d_store;
}

template <typename B, typename R>
void Decoder_NO<B,R>
::_soft_decode(const R *sys, const R *par, R *ext)
{
	std::copy(sys, sys + this->K, ext);
}

template <typename B, typename R>
void Decoder_NO<B,R>
::_soft_decode(const R *Y_N1, R *Y_N2)
{
	std::copy(Y_N1, Y_N1 + this->N, Y_N2);
}

// ==================================================================================== explicit template instantiation
#include "Tools/types.h"
#ifdef MULTI_PREC
template class aff3ct::module::Decoder_NO<B_8,Q_8>;
template class aff3ct::module::Decoder_NO<B_16,Q_16>;
template class aff3ct::module::Decoder_NO<B_32,Q_32>;
template class aff3ct::module::Decoder_NO<B_64,Q_64>;
#else
template class aff3ct::module::Decoder_NO<B,R>;
#endif
// ==================================================================================== explicit template instantiation
<commit_msg>Fix bad template.<commit_after>#include <chrono>

#include "Decoder_NO.hpp"

using namespace aff3ct::module;

template <typename B, typename R>
Decoder_NO<B,R>
::Decoder_NO(const int K, const int N, const int n_frames, const std::string name)
: Decoder_SISO<B,R>(K, N, n_frames, 1, name)
{
	if (N != K)
		throw std::invalid_argument("aff3ct::module::Decoder_NO: \"K\" and \"N\" have to be equal.");
}

template <typename B, typename R>
Decoder_NO<B,R>
::~Decoder_NO()
{
}

template <typename B, typename R>
void Decoder_NO<B,R>
::_hard_decode(const R *Y_N, B *V_K)
{
	auto t_store = std::chrono::steady_clock::now(); // --------------------------------------------------------- STORE
	// take the hard decision
	auto vec_loop_size = (this->K / mipp::nElReg<R>()) * mipp::nElReg<R>();
	for (auto i = 0; i < vec_loop_size; i += mipp::nElReg<R>())
	{
		const auto r_Y_N = mipp::Reg<R>(&Y_N[i]);
		const auto r_s = mipp::cast<R,B>(r_Y_N.sign()) >> (sizeof(B) * 8 - 1); // s[i] = Y_N[i] < 0;
		r_s.store(&V_K[i]);
	}
	for (auto i = vec_loop_size; i < this->K; i++)
		V_K[i] = Y_N[i] < 0;
	auto d_store = std::chrono::steady_clock::now() - t_store;

	this->d_store_total += d_store;
}

template <typename B, typename R>
void Decoder_NO<B,R>
::_soft_decode(const R *sys, const R *par, R *ext)
{
	std::copy(sys, sys + this->K, ext);
}

template <typename B, typename R>
void Decoder_NO<B,R>
::_soft_decode(const R *Y_N1, R *Y_N2)
{
	std::copy(Y_N1, Y_N1 + this->N, Y_N2);
}

// ==================================================================================== explicit template instantiation
#include "Tools/types.h"
#ifdef MULTI_PREC
template class aff3ct::module::Decoder_NO<B_8,Q_8>;
template class aff3ct::module::Decoder_NO<B_16,Q_16>;
template class aff3ct::module::Decoder_NO<B_32,Q_32>;
template class aff3ct::module::Decoder_NO<B_64,Q_64>;
#else
template class aff3ct::module::Decoder_NO<B,Q>;
#endif
// ==================================================================================== explicit template instantiation
<|endoftext|>


<commit_before>#include <QTimer>
#include "afisynclogger.h"
#include "console.h"

Console::Console(QObject* parent):
    QObject(parent)
{
    process_ = new QProcess();
    connect(process_, SIGNAL(readyRead()), this, SLOT(printOutput()));
}

Console::~Console()
{
    process_->kill();
    delete process_;
}

bool Console::runCmd(const QString& cmd) const
{
    LOG << "Running command: " << cmd.toStdString().c_str();

    process_->start(cmd);
    process_->waitForFinished(-1);

    return process_->exitCode() == 0;
}

QProcess* Console::runCmdAsync(const QString& cmd)
{
    LOG << "Running command: " << cmd.toStdString().c_str();

    process_->start(cmd);
    return process_;
}

void Console::printOutput()
{
    LOG << process_->readAll().toStdString().c_str();
}

void Console::terminate()
{
    if (process_->state() == QProcess::ProcessState::Running)
    {
        LOG << "Terminating process " << process_->program();
        process_->terminate();
    }
}
<commit_msg>Cleaned console.cpp includes<commit_after>#include "afisynclogger.h"
#include "console.h"

Console::Console(QObject* parent):
    QObject(parent)
{
    process_ = new QProcess();
    connect(process_, SIGNAL(readyRead()), this, SLOT(printOutput()));
}

Console::~Console()
{
    process_->kill();
    delete process_;
}

bool Console::runCmd(const QString& cmd) const
{
    LOG << "Running command: " << cmd.toStdString().c_str();

    process_->start(cmd);
    process_->waitForFinished(-1);

    return process_->exitCode() == 0;
}

QProcess* Console::runCmdAsync(const QString& cmd)
{
    LOG << "Running command: " << cmd.toStdString().c_str();

    process_->start(cmd);
    return process_;
}

void Console::printOutput()
{
    LOG << process_->readAll().toStdString().c_str();
}

void Console::terminate()
{
    if (process_->state() == QProcess::ProcessState::Running)
    {
        LOG << "Terminating process " << process_->program();
        process_->terminate();
    }
}
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2003 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifdef _WIN32
#pragma warning( 4: 4786)
#endif

#include "CursesUI.h"

// add this UI to the map
UIAdder CursesUI::uiAdder("curses", &CursesUI::creator);

CursesUI::CursesUI(const std::map<PlayerId, std::string>& p, PlayerId m) :
  players(p), me(m), maxHistory(20), currentHistory(0) {

  // initialize ncurses
  initscr();
  nonl();
  cbreak();
  noecho();

  // create main output window
  mainWin = newwin(LINES - 5, 0, 0, 0);
  wsetscrreg(mainWin, 0, LINES - 2);
  scrollok(mainWin, TRUE);
  wrefresh(mainWin);

  // create target window
  targetWin = newwin(1, 0, LINES - 2, 0);
  wattron(targetWin, A_UNDERLINE);
  targetIter = players.begin();
  updateTargetWin();

  // create command window
  cmdWin = newwin(0, 0, LINES - 1, 0);
  keypad(cmdWin, TRUE);
  nodelay(cmdWin, TRUE);
  updateCmdWin();

  // register commands for tab completion
  comp.registerWord("/lagstats");
  comp.registerWord("/idlestats");
  comp.registerWord("/flaghistory");
  comp.registerWord("/password ");
  comp.registerWord("/report ");
  comp.registerWord("/shutdownserver");
  comp.registerWord("/superkill");
  comp.registerWord("/gameover");
  comp.registerWord("/flag ");
  comp.registerWord("reset");
  comp.registerWord("up");
  comp.registerWord("show");
  comp.registerWord("/kick ");
  comp.registerWord("/playerlist");
  comp.registerWord("/ban ");
  comp.registerWord("/banlist");
  comp.registerWord("/countdown");
  comp.registerWord("/lagwarn ");
  comp.registerWord("/quit");
  comp.registerWord("/unban ");
  comp.registerWord("/lagwarn ");
  comp.registerWord("/help");
  comp.registerWord("/identify ");
  comp.registerWord("/register ");
  comp.registerWord("/ghost ");
  comp.registerWord("/deregister");
  comp.registerWord("/setpass ");
  comp.registerWord("/groupperms");
  comp.registerWord("/setgroup ");
  comp.registerWord("/removegroup ");
  comp.registerWord("/reload");
  comp.registerWord("/poll ");
  comp.registerWord("ban");
  comp.registerWord("kick");
  comp.registerWord("/vote");
  comp.registerWord("/veto");
  comp.registerWord("/set");
  comp.registerWord("/reset");
}

CursesUI::~CursesUI() {
  endwin();
}

void CursesUI::outputMessage(const std::string& msg) {
  waddstr(mainWin, (msg + "\n").c_str());
  wrefresh(mainWin);
}

bool CursesUI::checkCommand(std::string& str) {
  wrefresh(cmdWin);
  str = "";
  int i;
  int c = wgetch(cmdWin);
  switch (c) {
  case ERR:
    return false;

    // clear command (21 is Ctrl-U)
  case 21:
    cmd = "";
    updateCmdWin();
    currentHistory = history.size();

    // delete last character
  case KEY_BACKSPACE:
  case KEY_DC:
  case 127:
    cmd = cmd.substr(0, cmd.size() - 1);
    updateCmdWin();
    return false;

    // send command
  case '\n': // works with PDCurses
  case 13:   // works with ncurses
    if (history.size() == maxHistory)
      history.erase(history.begin());
    history.push_back(cmd);
    str = cmd;
    cmd = "";
    currentHistory = history.size();
    updateCmdWin();
    return true;

    // scroll main window - doesn't work
  case KEY_NPAGE:
    wscrl(mainWin, 1);
    return false;
  case KEY_PPAGE:
    wscrl(mainWin, -1);
    return false;

    // change target
  case KEY_LEFT:
    if (targetIter == players.begin())
      for (unsigned int i = 0; i < players.size() - 1; i++)
	++targetIter;
    else
      targetIter--;
    updateTargetWin();
    return false;
  case KEY_RIGHT:
    targetIter++;
    if (targetIter == players.end())
      targetIter = players.begin();
    updateTargetWin();
    return false;

    // command history
  case KEY_UP:
    if (currentHistory == 0 || history.size() == 0)
      return false;
    --currentHistory;
    cmd = history[currentHistory];
    updateCmdWin();
    return false;
  case KEY_DOWN:
    if (currentHistory < history.size())
      ++currentHistory;
    if (currentHistory == history.size())
      cmd = "";
    else
      cmd = history[currentHistory];
    updateCmdWin();
    return false;

    // kick target
  case KEY_F(5):
    if (targetIter != players.end() && targetIter->first != me) {
      cmd = "/kick ";
      cmd += targetIter->second;
      targetIter = players.find(me);
      updateCmdWin();
      updateTargetWin();
    }
    return false;

    // tab - autocomplete
  case '\t':
    i = cmd.find_last_of(" \t");
    cmd = cmd.substr(0, i+1) + comp.complete(cmd.substr(i+1));
    updateCmdWin();
    return false;

  default:
    if (c < 32 || c > 127 || cmd.size() >= CMDLENGTH)
      return false;
    cmd += char(c);
    updateCmdWin();
    return false;
  }
}

void CursesUI::addedPlayer(PlayerId p) {
  comp.registerWord(players.find(p)->second);
}

void CursesUI::removingPlayer(PlayerId p) {
  if (targetIter->first == p) {
    targetIter = players.find(me);
    updateTargetWin();
  }
  comp.unregisterWord(players.find(p)->second);
}

PlayerId CursesUI::getTarget() const {
  if (targetIter->first == me)
    return AllPlayers;
  return targetIter->first;
}

void CursesUI::updateTargetWin() {
  wclear(targetWin);
  wmove(targetWin, 0, 0);
  wmove(targetWin, 1, 1);
  std::string tmp = "Send to ";
  tmp = tmp + (targetIter == players.end() || targetIter->first == me ?
	       "all" : targetIter->second) + ":";
  waddstr(targetWin, tmp.c_str());
  wrefresh(targetWin);
}

void CursesUI::updateCmdWin() {
  wclear(cmdWin);
  wmove(cmdWin, 1, 1);
  waddstr(cmdWin, cmd.c_str());
  wmove(cmdWin, 1, 1 + cmd.size());
  wrefresh(cmdWin);
}

BZAdminUI* CursesUI::creator(const std::map<PlayerId, std::string>& players,
			     PlayerId me) {
  return new CursesUI(players, me);
}

// Local variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>Sorted the registerWord() calls in semi-alphabetical order after the argument to make it easier to see if a word has been registered or not<commit_after>/* bzflag
 * Copyright (c) 1993 - 2003 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifdef _WIN32
#pragma warning( 4: 4786)
#endif

#include "CursesUI.h"

// add this UI to the map
UIAdder CursesUI::uiAdder("curses", &CursesUI::creator);

CursesUI::CursesUI(const std::map<PlayerId, std::string>& p, PlayerId m) :
  players(p), me(m), maxHistory(20), currentHistory(0) {

  // initialize ncurses
  initscr();
  nonl();
  cbreak();
  noecho();

  // create main output window
  mainWin = newwin(LINES - 5, 0, 0, 0);
  wsetscrreg(mainWin, 0, LINES - 2);
  scrollok(mainWin, TRUE);
  wrefresh(mainWin);

  // create target window
  targetWin = newwin(1, 0, LINES - 2, 0);
  wattron(targetWin, A_UNDERLINE);
  targetIter = players.begin();
  updateTargetWin();

  // create command window
  cmdWin = newwin(0, 0, LINES - 1, 0);
  keypad(cmdWin, TRUE);
  nodelay(cmdWin, TRUE);
  updateCmdWin();

  // register commands for tab completion
  comp.registerWord("/ban ");
  comp.registerWord("/banlist");
  comp.registerWord("/countdown");
  comp.registerWord("/deregister");
  comp.registerWord("/flag ");
  comp.registerWord("reset");
  comp.registerWord("up");
  comp.registerWord("show");
  comp.registerWord("/flaghistory");
  comp.registerWord("/gameover");
  comp.registerWord("/ghost ");
  comp.registerWord("/groupperms");
  comp.registerWord("/help");
  comp.registerWord("/identify ");
  comp.registerWord("/idlestats");
  comp.registerWord("/kick ");
  comp.registerWord("/lagstats");
  comp.registerWord("/lagwarn ");
  comp.registerWord("/password ");
  comp.registerWord("/playerlist");
  comp.registerWord("/poll ");
  comp.registerWord("ban");
  comp.registerWord("kick");
  comp.registerWord("/quit");
  comp.registerWord("/register ");
  comp.registerWord("/reload");
  comp.registerWord("/removegroup ");
  comp.registerWord("/report ");
  comp.registerWord("/reset");
  comp.registerWord("/set");
  comp.registerWord("/setgroup ");
  comp.registerWord("/setpass ");
  comp.registerWord("/shutdownserver");
  comp.registerWord("/superkill");
  comp.registerWord("/unban ");
  comp.registerWord("/veto");
  comp.registerWord("/vote");
}

CursesUI::~CursesUI() {
  endwin();
}

void CursesUI::outputMessage(const std::string& msg) {
  waddstr(mainWin, (msg + "\n").c_str());
  wrefresh(mainWin);
}

bool CursesUI::checkCommand(std::string& str) {
  wrefresh(cmdWin);
  str = "";
  int i;
  int c = wgetch(cmdWin);
  switch (c) {
  case ERR:
    return false;

    // clear command (21 is Ctrl-U)
  case 21:
    cmd = "";
    updateCmdWin();
    currentHistory = history.size();

    // delete last character
  case KEY_BACKSPACE:
  case KEY_DC:
  case 127:
    cmd = cmd.substr(0, cmd.size() - 1);
    updateCmdWin();
    return false;

    // send command
  case '\n': // works with PDCurses
  case 13:   // works with ncurses
    if (history.size() == maxHistory)
      history.erase(history.begin());
    history.push_back(cmd);
    str = cmd;
    cmd = "";
    currentHistory = history.size();
    updateCmdWin();
    return true;

    // scroll main window - doesn't work
  case KEY_NPAGE:
    wscrl(mainWin, 1);
    return false;
  case KEY_PPAGE:
    wscrl(mainWin, -1);
    return false;

    // change target
  case KEY_LEFT:
    if (targetIter == players.begin())
      for (unsigned int i = 0; i < players.size() - 1; i++)
	++targetIter;
    else
      targetIter--;
    updateTargetWin();
    return false;
  case KEY_RIGHT:
    targetIter++;
    if (targetIter == players.end())
      targetIter = players.begin();
    updateTargetWin();
    return false;

    // command history
  case KEY_UP:
    if (currentHistory == 0 || history.size() == 0)
      return false;
    --currentHistory;
    cmd = history[currentHistory];
    updateCmdWin();
    return false;
  case KEY_DOWN:
    if (currentHistory < history.size())
      ++currentHistory;
    if (currentHistory == history.size())
      cmd = "";
    else
      cmd = history[currentHistory];
    updateCmdWin();
    return false;

    // kick target
  case KEY_F(5):
    if (targetIter != players.end() && targetIter->first != me) {
      cmd = "/kick ";
      cmd += targetIter->second;
      targetIter = players.find(me);
      updateCmdWin();
      updateTargetWin();
    }
    return false;

    // tab - autocomplete
  case '\t':
    i = cmd.find_last_of(" \t");
    cmd = cmd.substr(0, i+1) + comp.complete(cmd.substr(i+1));
    updateCmdWin();
    return false;

  default:
    if (c < 32 || c > 127 || cmd.size() >= CMDLENGTH)
      return false;
    cmd += char(c);
    updateCmdWin();
    return false;
  }
}

void CursesUI::addedPlayer(PlayerId p) {
  comp.registerWord(players.find(p)->second);
}

void CursesUI::removingPlayer(PlayerId p) {
  if (targetIter->first == p) {
    targetIter = players.find(me);
    updateTargetWin();
  }
  comp.unregisterWord(players.find(p)->second);
}

PlayerId CursesUI::getTarget() const {
  if (targetIter->first == me)
    return AllPlayers;
  return targetIter->first;
}

void CursesUI::updateTargetWin() {
  wclear(targetWin);
  wmove(targetWin, 0, 0);
  wmove(targetWin, 1, 1);
  std::string tmp = "Send to ";
  tmp = tmp + (targetIter == players.end() || targetIter->first == me ?
	       "all" : targetIter->second) + ":";
  waddstr(targetWin, tmp.c_str());
  wrefresh(targetWin);
}

void CursesUI::updateCmdWin() {
  wclear(cmdWin);
  wmove(cmdWin, 1, 1);
  waddstr(cmdWin, cmd.c_str());
  wmove(cmdWin, 1, 1 + cmd.size());
  wrefresh(cmdWin);
}

BZAdminUI* CursesUI::creator(const std::map<PlayerId, std::string>& players,
			     PlayerId me) {
  return new CursesUI(players, me);
}

// Local variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010  Belledonne Communications SARL.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "agent.hh"
#include "etchosts.hh"

class ForwardModule : public Module {
	public:
		ForwardModule(Agent *ag);
		virtual void onRequest(SipEvent *ev);
		virtual void onResponse(SipEvent *ev);
	private:
		static ModuleInfo<ForwardModule> sInfo;
};

ModuleInfo<ForwardModule> ForwardModule::sInfo("Forward",
   "This module executes the basic routing task of SIP requests and pass them to the transport layer. "
	"It must always be enabled.");

ForwardModule::ForwardModule(Agent *ag) : Module(ag){
}

void ForwardModule::onRequest(SipEvent *ev){
	size_t msg_size;
	char *buf;
	url_t* dest=NULL;
	sip_t *sip=ev->mSip;
	msg_t *msg=ev->mMsg;

	switch(sip->sip_request->rq_method){
		case sip_method_invite:
			LOGD("This is an invite");
			break;
		case sip_method_register:
			LOGD("This is a register");
			//rewrite the request uri to the domain
			//this assume the domain is also the proxy
			sip->sip_request->rq_url->url_host=sip->sip_to->a_url->url_host;
			sip->sip_request->rq_url->url_port=sip->sip_to->a_url->url_port;
		case sip_method_ack:
		default:
			break;
	}
	dest=sip->sip_request->rq_url;
	// removes top route header if it maches us
	if (sip->sip_route!=NULL){
		if (getAgent()->isUs(sip->sip_route->r_url)){
			sip_route_remove(msg,sip);
		}
		if (sip->sip_route!=NULL){
			/*forward to this route*/
			dest=sip->sip_route->r_url;
		}
	}

	char contact_route_param[64];
	// now need to check if request uri has special param inserted by contact-route-inserter module
	if (url_param(dest->url_params,getAgent()->getUniqueId().c_str(),contact_route_param,sizeof(contact_route_param))) {
		//first remove param
		dest->url_params = url_strip_param_string(su_strdup(ev->getHome(),dest->url_params),getAgent()->getUniqueId().c_str());
		//test and remove maddr param
		if (url_has_param(dest,"maddr")) {
			dest->url_params = url_strip_param_string(su_strdup(ev->getHome(),dest->url_params),"maddr");
		}
		//second change dest to
		char* hostport_separator = strchr(contact_route_param, ':');
		dest->url_host=su_strndup(ev->getHome(), contact_route_param, (hostport_separator-contact_route_param) );
		dest->url_port=su_strdup(ev->getHome(), hostport_separator+1);
	}

	std::string ip;
	if (EtcHostsResolver::get()->resolve(dest->url_host,&ip)){
		LOGD("Found %s in /etc/hosts",dest->url_host);
		/* duplication dest because we don't want to modify the message with our name resolution result*/
		dest=url_hdup(ev->getHome(),dest);
		dest->url_host=ip.c_str();
	}

	/* workaround bad sip uris with two @ that results in host part being "something@somewhere" */
	if (strchr(dest->url_host,'@')!=0){
		nta_msg_treply (getSofiaAgent(),msg,400,"Bad request",SIPTAG_SERVER_STR(getAgent()->getServerString()),TAG_END());
		return;
	}

	if (!getAgent()->isUs(dest)) {
		buf=msg_as_string(ev->getHome(), msg, NULL, 0,&msg_size);
		LOGD("About to forward request to %s:\n%s",url_as_string(ev->getHome(),dest),buf);
		nta_msg_tsend (getSofiaAgent(),msg,(url_string_t*)dest,TAG_END());
	}else{
		LOGD("This message has final destination this proxy, discarded...");
		nta_msg_discard(getSofiaAgent(),msg);
	}
}

void ForwardModule::onResponse(SipEvent *ev){
	char *buf;
	size_t msg_size;

	buf=msg_as_string(ev->getHome(), ev->mMsg, NULL, 0,&msg_size);
	LOGD("About to forward response:\n%s",buf);

	nta_msg_tsend(getSofiaAgent(),ev->mMsg,(url_string_t*)NULL,TAG_END());
}
<commit_msg>implement a static routing option in module::Forward<commit_after>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010  Belledonne Communications SARL.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "agent.hh"
#include "etchosts.hh"

class ForwardModule : public Module {
	public:
		ForwardModule(Agent *ag);
		virtual void onDeclare(ConfigStruct * module_config);
		virtual void onLoad(Agent *agent, const ConfigStruct *root);
		virtual void onRequest(SipEvent *ev);
		virtual void onResponse(SipEvent *ev);
		url_t* overrideDest(SipEvent *ev, url_t* dest);
		~ForwardModule();
	private:
		su_home_t mHome;
		sip_route_t *mOutRoute;
		bool mRewriteReqUri;
		static ModuleInfo<ForwardModule> sInfo;
};

ModuleInfo<ForwardModule> ForwardModule::sInfo("Forward",
   "This module executes the basic routing task of SIP requests and pass them to the transport layer. "
	"It must always be enabled.");

ForwardModule::ForwardModule(Agent *ag) : Module(ag){
	su_home_init(&mHome);
	mOutRoute=NULL;
}

ForwardModule::~ForwardModule(){
	su_home_deinit(&mHome);
}

void ForwardModule::onDeclare(ConfigStruct * module_config){
	ConfigItemDescriptor items[]={
			{	String	,	"route"	, 	"A sip uri where to send all requests",	""	},
			{	Boolean	,	"rewrite-req-uri"	,	"Rewrite request-uri's host and port according to above route", "false"	},
			config_item_end
	};
	module_config->addChildrenValues(items);
}

void ForwardModule::onLoad(Agent *agent, const ConfigStruct *module_config){
	std::string route=module_config->get<ConfigString>("route")->read();
	mRewriteReqUri=module_config->get<ConfigBoolean>("rewrite-req-uri")->read();
	if (route.size()>0){
		mOutRoute=sip_route_create(&mHome,(url_t*)route.c_str(),NULL);
		if (mOutRoute==NULL){
			LOGF("Bad route parameter '%s' in configuration of Forward module",route.c_str());
		}
	}
}

url_t* ForwardModule::overrideDest(SipEvent *ev, url_t *dest){
	if (mOutRoute){
		dest=mOutRoute->r_url;
		if (mRewriteReqUri){
			ev->mSip->sip_request->rq_url->url_host=mOutRoute->r_url->url_host;
			ev->mSip->sip_request->rq_url->url_port=mOutRoute->r_url->url_port;
		}
	}
	return dest;
}

void ForwardModule::onRequest(SipEvent *ev){
	size_t msg_size;
	char *buf;
	url_t* dest=NULL;
	sip_t *sip=ev->mSip;
	msg_t *msg=ev->mMsg;

	switch(sip->sip_request->rq_method){
		case sip_method_invite:
			LOGD("This is an invite");
			break;
		case sip_method_register:
			LOGD("This is a register");
			//rewrite the request uri to the domain
			//this assume the domain is also the proxy
			sip->sip_request->rq_url->url_host=sip->sip_to->a_url->url_host;
			sip->sip_request->rq_url->url_port=sip->sip_to->a_url->url_port;
		case sip_method_ack:
		default:
			break;
	}
	dest=sip->sip_request->rq_url;
	// removes top route header if it maches us
	if (sip->sip_route!=NULL){
		if (getAgent()->isUs(sip->sip_route->r_url)){
			sip_route_remove(msg,sip);
		}
		if (sip->sip_route!=NULL){
			/*forward to this route*/
			dest=sip->sip_route->r_url;
		}
	}

	char contact_route_param[64];
	// now need to check if request uri has special param inserted by contact-route-inserter module
	if (url_param(dest->url_params,getAgent()->getUniqueId().c_str(),contact_route_param,sizeof(contact_route_param))) {
		//first remove param
		dest->url_params = url_strip_param_string(su_strdup(ev->getHome(),dest->url_params),getAgent()->getUniqueId().c_str());
		//test and remove maddr param
		if (url_has_param(dest,"maddr")) {
			dest->url_params = url_strip_param_string(su_strdup(ev->getHome(),dest->url_params),"maddr");
		}
		//second change dest to
		char* hostport_separator = strchr(contact_route_param, ':');
		dest->url_host=su_strndup(ev->getHome(), contact_route_param, (hostport_separator-contact_route_param) );
		dest->url_port=su_strdup(ev->getHome(), hostport_separator+1);
	}

	/* workaround bad sip uris with two @ that results in host part being "something@somewhere" */
	if (strchr(dest->url_host,'@')!=0){
		nta_msg_treply (getSofiaAgent(),msg,400,"Bad request",SIPTAG_SERVER_STR(getAgent()->getServerString()),TAG_END());
		return;
	}

	dest=overrideDest(ev,dest);

	std::string ip;
	if (EtcHostsResolver::get()->resolve(dest->url_host,&ip)){
		LOGD("Found %s in /etc/hosts",dest->url_host);
		/* duplication dest because we don't want to modify the message with our name resolution result*/
		dest=url_hdup(ev->getHome(),dest);
		dest->url_host=ip.c_str();
	}

	if (!getAgent()->isUs(dest)) {
		buf=msg_as_string(ev->getHome(), msg, NULL, 0,&msg_size);
		LOGD("About to forward request to %s:\n%s",url_as_string(ev->getHome(),dest),buf);
		nta_msg_tsend (getSofiaAgent(),msg,(url_string_t*)dest,TAG_END());
	}else{
		LOGD("This message has final destination this proxy, discarded...");
		nta_msg_discard(getSofiaAgent(),msg);
	}
}

void ForwardModule::onResponse(SipEvent *ev){
	char *buf;
	size_t msg_size;

	buf=msg_as_string(ev->getHome(), ev->mMsg, NULL, 0,&msg_size);
	LOGD("About to forward response:\n%s",buf);

	nta_msg_tsend(getSofiaAgent(),ev->mMsg,(url_string_t*)NULL,TAG_END());
}
<|endoftext|>


<commit_before>/**
 * @file   measurement_systen_node.cpp
 * @author Izabella Thais Oliveira Gomes
 * @date   23/09/2016
 *
 * @attention Copyright (C) 2014 UnBall Robot Soccer Team
 *
 * @brief Rotate robot position datas acording to camera position in the vision and simulator and publishes that.
 *
*/

#include <vector>
#include <cmath>

#include <ros/ros.h>

#include <unball/VisionMessage.h>
#include <unball/SimulatorMessage.h>
#include <unball/MeasurementSystemMessage.h>
#include <opencv2/opencv.hpp>

const float field_x_length = 1.50;
const float field_y_length = 1.30;

const float camera_x_length = 640;
const float camera_y_length = 480;

void receiveVisionMessage(const unball::VisionMessage::ConstPtr &msg_s);
void rotateAxis();
void convertPixelsToMeters();
void receiveSimulatorMessage(const unball::SimulatorMessage::ConstPtr &msg_v);
void publishMeasurementSystemMessage(ros::Publisher &publisher);

unball::MeasurementSystemMessage message;

ros::Publisher publisher;

int main(int argc, char **argv)
{
    ros::init(argc, argv, "measurement_system_node");

    ros::NodeHandle n;
    ros::Rate loop_rate(10); // Hz

    ros::Subscriber sub = n.subscribe("vision_topic", 1, receiveVisionMessage);
    publisher = n.advertise<unball::MeasurementSystemMessage>("measurement_system_topic", 1);

    while (ros::ok())
    {
        ros::spinOnce();
        loop_rate.sleep();
    }

    return 0;
}

void receiveVisionMessage(const unball::VisionMessage::ConstPtr &msg_v)
{
    std::vector<float> x(6), y(6), th(6);
    std::vector<float> ball_location(2);

    ROS_INFO("\n\n[MeasurementNode]:ReceiveVisionMessage - Receiving vision message");

    for (int robot_index = 0; robot_index < 6; robot_index++)
    {
        ROS_INFO("%d x: %f\t y: %f\t th: %f", robot_index, msg_v->x[robot_index], msg_v->y[robot_index],
            msg_v->th[robot_index]);
        message.x[robot_index] = msg_v->x[robot_index];
        message.y[robot_index] = msg_v->y[robot_index];
        message.th[robot_index] = msg_v->th[robot_index];
    }
    message.ball_x = msg_v->ball_x;
    message.ball_y = msg_v->ball_y;
    convertPixelsToMeters();

    ROS_INFO("\n\n[MeasurementNode]:ReceiveVisionMessage - Sending measurement system message");

    for (int robot_index = 0; robot_index < 6; robot_index++)
    {
        ROS_INFO("%d x: %f\t y: %f\t th: %f", robot_index, message.x[robot_index], message.y[robot_index],
            message.th[robot_index]);
    }
    ROS_INFO("Ball: x: %f, y: %f", message.ball_x, message.ball_y);

    publisher.publish(message);
}

void convertPixelsToMeters(){
    auto x_conversion = field_x_length / camera_x_length;
    auto y_conversion = (field_y_length / camera_y_length) * -1;
    for (int i = 0; i < 6; ++i)
    {
        message.x[i] -= camera_x_length / 2;
        message.y[i] -= camera_y_length / 2;
        message.x[i] *= x_conversion;
        message.y[i] *= y_conversion;
    }
    message.ball_x -= camera_x_length / 2;
    message.ball_y -= camera_y_length / 2;
    message.ball_x *= x_conversion;
    message.ball_y *= y_conversion;
}
<commit_msg>UNTESTED: Measurement system repeating last message in case the vision loses track of the robot.<commit_after>/**
 * @file   measurement_systen_node.cpp
 * @author Izabella Thais Oliveira Gomes
 * @date   23/09/2016
 *
 * @attention Copyright (C) 2014 UnBall Robot Soccer Team
 *
 * @brief Rotate robot position datas acording to camera position in the vision and simulator and publishes that.
 *
*/

#include <vector>
#include <cmath>

#include <ros/ros.h>

#include <unball/VisionMessage.h>
#include <unball/SimulatorMessage.h>
#include <unball/MeasurementSystemMessage.h>
#include <opencv2/opencv.hpp>

const float field_x_length = 1.50;
const float field_y_length = 1.30;

const float camera_x_length = 640;
const float camera_y_length = 480;

void receiveVisionMessage(const unball::VisionMessage::ConstPtr &msg_s);
void rotateAxis();
void convertPixelsToMeters();
void receiveSimulatorMessage(const unball::SimulatorMessage::ConstPtr &msg_v);
void publishMeasurementSystemMessage(ros::Publisher &publisher);

unball::MeasurementSystemMessage message;

ros::Publisher publisher;

int main(int argc, char **argv)
{
    ros::init(argc, argv, "measurement_system_node");

    ros::NodeHandle n;
    ros::Rate loop_rate(10); // Hz

    ros::Subscriber sub = n.subscribe("vision_topic", 1, receiveVisionMessage);
    publisher = n.advertise<unball::MeasurementSystemMessage>("measurement_system_topic", 1);

    while (ros::ok())
    {
        ros::spinOnce();
        loop_rate.sleep();
    }

    return 0;
}

void receiveVisionMessage(const unball::VisionMessage::ConstPtr &msg_v)
{
    std::vector<float> x(6), y(6), th(6);
    std::vector<float> ball_location(2);

    ROS_INFO("\n\n[MeasurementNode]:ReceiveVisionMessage - Receiving vision message");

    for (int robot_index = 0; robot_index < 6; robot_index++)
    {
        ROS_INFO("%d x: %f\t y: %f\t th: %f", robot_index, msg_v->x[robot_index], msg_v->y[robot_index],
            msg_v->th[robot_index]);
        if (not (msg_v->x[robot_index] == -1 and msg_v->y[robot_index] == -1))
        {
            message.x[robot_index] = msg_v->x[robot_index];
            message.y[robot_index] = msg_v->y[robot_index];
            message.th[robot_index] = msg_v->th[robot_index];
        }
    }
    message.ball_x = msg_v->ball_x;
    message.ball_y = msg_v->ball_y;
    convertPixelsToMeters();

    ROS_INFO("\n\n[MeasurementNode]:ReceiveVisionMessage - Sending measurement system message");

    for (int robot_index = 0; robot_index < 6; robot_index++)
    {
        ROS_INFO("%d x: %f\t y: %f\t th: %f", robot_index, message.x[robot_index], message.y[robot_index],
            message.th[robot_index]);
    }
    ROS_INFO("Ball: x: %f, y: %f", message.ball_x, message.ball_y);

    publisher.publish(message);
}

void convertPixelsToMeters(){
    auto x_conversion = field_x_length / camera_x_length;
    auto y_conversion = (field_y_length / camera_y_length) * -1;
    for (int i = 0; i < 6; ++i)
    {
        message.x[i] -= camera_x_length / 2;
        message.y[i] -= camera_y_length / 2;
        message.x[i] *= x_conversion;
        message.y[i] *= y_conversion;
    }
    message.ball_x -= camera_x_length / 2;
    message.ball_y -= camera_y_length / 2;
    message.ball_x *= x_conversion;
    message.ball_y *= y_conversion;
}
<|endoftext|>


<commit_before>//
//
//

#include "Sys.h"
#include "Debug.h"

#include "KeyEvent.h"
#include "Widget.h"
#include "WidgetCallback.h"

Widget::Widget(Widget *parent)
{
	parent_=parent;
	if (parent_) parent_->children.push_back(this);
	x_=xfocus_=0;
	y_=yfocus_=0;
	width_=20;
	height_=4;
	focus_=false;
	acceptsFocus_=true;
	dirty_=false;
}

Widget::~Widget()
{
	for (unsigned int i=0;i<children.size();i++)
		delete children.at(i);
	for (unsigned int i=0;i<callbacks.size();i++)
		delete callbacks[i];
}

bool Widget::keyEvent(KeyEvent &ke)
{
	// return value indicates acceptance of the event

	Dout(dc::trace,"Widget::keyEvent()");

	if (!contains(ke.x(),ke.y()))
	{
		setFocusWidget(false);
		return false;
	}

	Widget *currentFocusWidget=NULL;
	for (unsigned int i=0;i<children.size();i++)
	{
		if (children.at(i)->isFocusWidget())
		{

			currentFocusWidget=children.at(i);
			Dout(dc::trace,"Widget::keyEvent() current focus widget at " << currentFocusWidget->x() << " " << currentFocusWidget->y());
			//break; // FIXME to help with debugging!
		}
	}

	if (currentFocusWidget) // check the focus widget first (should always be one)
	{
		if (currentFocusWidget->keyEvent(ke))
			return true;
	}

	for (unsigned int i=0;i<children.size();i++) // check all children next
	{
		if (children.at(i)->keyEvent(ke))
			return true;
	}

	// key movement may require a change of focus child widget

	if (ke.event() & KeyEvent::KeyRelease )
	{
		if (ke.event() & KeyEvent::KeyLeft)
		{
			if (xfocus_ > 0) xfocus_--; // this just moves us around by default

			if (children.size() > 0)
			{
				if (currentFocusWidget)
				{
					Widget *prevWidget;
					if ((prevWidget = focusWidgetBefore(currentFocusWidget)))
						prevWidget->setFocusWidget(true);
					else
						currentFocusWidget->setFocusWidget(true);
				}
			}
		}
		else if (ke.event() & KeyEvent::KeyRight)
		{
			if (xfocus_ < 19) xfocus_++;

			if (children.size() > 0)
			{
				if (currentFocusWidget)
				{
					Widget *nextWidget;
					if ((nextWidget = focusWidgetAfter(currentFocusWidget)))
						nextWidget->setFocusWidget(true);
					else
						currentFocusWidget->setFocusWidget(true);
				}
			}
		}
		else if (ke.event() & KeyEvent::KeyUp)
		{
			if (yfocus_ > 0) yfocus_--;

			if (children.size() > 0)
			{
				if (currentFocusWidget)
				{
					Widget *prevWidget;
					if ((prevWidget = focusWidgetBefore(currentFocusWidget)))
						prevWidget->setFocusWidget(true);
					else
						currentFocusWidget->setFocusWidget(true);
				}
			}
		}
		else if (ke.event() & KeyEvent::KeyDown)
		{
			if (yfocus_< 19) yfocus_++;

			if (children.size() > 0)
			{
				if (currentFocusWidget)
				{
					Widget *nextWidget;
					if ((nextWidget = focusWidgetAfter(currentFocusWidget)))
						nextWidget->setFocusWidget(true);
					else
						currentFocusWidget->setFocusWidget(true);
				}
			}
		}
	}

	return true;
}

void Widget::paint(std::vector<std::string> &display)
{
	// We paint into the string ...
	unsigned int nlines=children.size();
	if (nlines > 4) nlines=4;
	for (unsigned int i=0;i<nlines;i++)
	{
		children.at(i)->paint(display);
	}
}

void  Widget::focus(int *fx,int *fy)
{
	if (children.empty())
	{
		*fx=xfocus_;
		*fy=yfocus_;
		return;
	}

	// check if anyone claims focus now
	for (unsigned int i=0;i<children.size();i++)
	{
		if (children.at(i)->isFocusWidget())
		{

			children.at(i)->focus(fx,fy);
			Dout(dc::trace,"Widget::focus() claimed by child at " << *fx << " " << *fy);
			return;
		}
	}

	// no winners so pick the first one
	std::vector<Widget *> ordered;
	orderChildren(ordered);
	for (unsigned int i=0;i<ordered.size();i++)
	{
		if (ordered.at(i)->acceptsFocus())
		{
			ordered.at(i)->focus(fx,fy);
			ordered.at(i)->setFocusWidget(true);
		}
	}

}

void Widget::setGeometry(int x,int y,int w,int h)
{
	x_=xfocus_=x;
	y_=yfocus_=y;
	if (w > 0) width_=w;
	if (h > 0) height_=h;
}

bool Widget::contains(int xp,int yp)
{
	return ((xp >= x_) && (xp <=x_+width_ -1) && (yp>=y_) && (yp <=y_+height_-1));
}

bool Widget::dirty()
{
	bool ret=dirty_;
	for (unsigned int i=0;i<children.size();i++)
		ret = ret || children.at(i)->dirty();
	return ret;
}

//
//
//

void Widget::setDirty(bool d)
{
	dirty_ = d;
}

void Widget::addCallback(Functor *cb)
{
	callbacks.push_back(cb);
}
//
// FIXME assumes one widget per line
//

Widget *Widget::focusWidgetBefore(Widget *w)
{
	int wx = w->x();
	int wy = w->y();
	int wht = w->height();
	Dout(dc::trace," Widget::focusWidgetBefore() " <<wx << " " << wy << " " << wht);

	std::vector<Widget *> ordered;
	orderChildren(ordered);
	for ( int i=0;i<(int) ordered.size();i++)
	{
		Widget *c =ordered.at(i);
		if (c==w)
		{
			Dout(dc::trace," Widget::focusWidgetBefore() match index = " << i);
			int indx;
			if ((i-1) >= 0)
				indx=i-1;
			else //(i-1 < 0)
				indx=ordered.size()-1; //.go to last
			Dout(dc::trace," Widget::focusWidgetBefore() focus at " << ordered.at(indx)->x() << " " << ordered.at(indx)->y());
			return ordered.at(indx);
		}
	}
	return NULL;
}

Widget *Widget::focusWidgetAfter(Widget *w)
{
	int wx = w->x();
	int wy = w->y();
	int wht = w->height();
	Dout(dc::trace," Widget::focusWidgetAfter() " <<wx << " " << wy << " " << wht);

	std::vector<Widget *> ordered;
	orderChildren(ordered);

	for (int i=0;i<(int)ordered.size();i++)
	{
		Widget *c =ordered.at(i);
		if (c==w)
		{
			int indx;
			if (i+1 <(int) ordered.size())
				indx=i+1;
			else //i+1 >= ordered.size()
				indx=0; // go to first
			Dout(dc::trace," Widget::focusWidgetBefore() focus at " << ordered.at(indx)->x() << " " << ordered.at(indx)->y());
			return ordered.at(indx);
		}
	}

	return NULL;
}

void Widget::orderChildren(std::vector<Widget *> & ordered)
{
	// spatially sorts children so that next focus widget can be determined

	// for each line build a list of widgets on that line
	// order that list by 'x' and then copy to the ordered list

	std::vector<Widget *> tmp;
	Dout(dc::trace,"Widget::orderChildren() children before =" << children.size());
	for (int j=0;j<=3;j++)
	{
		tmp.clear();
		for (unsigned int w=0;w<children.size();w++)
		{
			if (children.at(w)->y() == j && children.at(w)->acceptsFocus())
				tmp.push_back(children.at(w));
		}
		// order that list by 'x' and then copy to the ordered list

		for (int i=0;i<20;i++) // small search space so brute force
		{
			for (unsigned int w=0;w<tmp.size();w++)
			{
				if (tmp.at(w)->x()==i)
					ordered.push_back(tmp.at(w));
			}
		}

	}

	Dout(dc::trace,"Widget::orderChildren() children after =" << ordered.size());
}

<commit_msg>Crufty bug. Originally limited to 1 widget per line so only 4 widgets were painted. This limitation no longer exists.<commit_after>//
//
//

#include "Sys.h"
#include "Debug.h"

#include "KeyEvent.h"
#include "Widget.h"
#include "WidgetCallback.h"

Widget::Widget(Widget *parent)
{
	parent_=parent;
	if (parent_) parent_->children.push_back(this);
	x_=xfocus_=0;
	y_=yfocus_=0;
	width_=20;
	height_=4;
	focus_=false;
	acceptsFocus_=true;
	dirty_=false;
}

Widget::~Widget()
{
	for (unsigned int i=0;i<children.size();i++)
		delete children.at(i);
	for (unsigned int i=0;i<callbacks.size();i++)
		delete callbacks[i];
}

bool Widget::keyEvent(KeyEvent &ke)
{
	// return value indicates acceptance of the event

	Dout(dc::trace,"Widget::keyEvent()");

	if (!contains(ke.x(),ke.y()))
	{
		setFocusWidget(false);
		return false;
	}

	Widget *currentFocusWidget=NULL;
	for (unsigned int i=0;i<children.size();i++)
	{
		if (children.at(i)->isFocusWidget())
		{

			currentFocusWidget=children.at(i);
			Dout(dc::trace,"Widget::keyEvent() current focus widget at " << currentFocusWidget->x() << " " << currentFocusWidget->y());
			//break; // FIXME to help with debugging!
		}
	}

	if (currentFocusWidget) // check the focus widget first (should always be one)
	{
		if (currentFocusWidget->keyEvent(ke))
			return true;
	}

	for (unsigned int i=0;i<children.size();i++) // check all children next
	{
		if (children.at(i)->keyEvent(ke))
			return true;
	}

	// key movement may require a change of focus child widget

	if (ke.event() & KeyEvent::KeyRelease )
	{
		if (ke.event() & KeyEvent::KeyLeft)
		{
			if (xfocus_ > 0) xfocus_--; // this just moves us around by default

			if (children.size() > 0)
			{
				if (currentFocusWidget)
				{
					Widget *prevWidget;
					if ((prevWidget = focusWidgetBefore(currentFocusWidget)))
						prevWidget->setFocusWidget(true);
					else
						currentFocusWidget->setFocusWidget(true);
				}
			}
		}
		else if (ke.event() & KeyEvent::KeyRight)
		{
			if (xfocus_ < 19) xfocus_++;

			if (children.size() > 0)
			{
				if (currentFocusWidget)
				{
					Widget *nextWidget;
					if ((nextWidget = focusWidgetAfter(currentFocusWidget)))
						nextWidget->setFocusWidget(true);
					else
						currentFocusWidget->setFocusWidget(true);
				}
			}
		}
		else if (ke.event() & KeyEvent::KeyUp)
		{
			if (yfocus_ > 0) yfocus_--;

			if (children.size() > 0)
			{
				if (currentFocusWidget)
				{
					Widget *prevWidget;
					if ((prevWidget = focusWidgetBefore(currentFocusWidget)))
						prevWidget->setFocusWidget(true);
					else
						currentFocusWidget->setFocusWidget(true);
				}
			}
		}
		else if (ke.event() & KeyEvent::KeyDown)
		{
			if (yfocus_< 19) yfocus_++;

			if (children.size() > 0)
			{
				if (currentFocusWidget)
				{
					Widget *nextWidget;
					if ((nextWidget = focusWidgetAfter(currentFocusWidget)))
						nextWidget->setFocusWidget(true);
					else
						currentFocusWidget->setFocusWidget(true);
				}
			}
		}
	}

	return true;
}

void Widget::paint(std::vector<std::string> &display)
{
	// We paint into the string ...
	//unsigned int nlines=children.size();
	//if (nlines > 4) nlines=4;
	for (unsigned int i=0;i<children.size();i++)
	{
		children.at(i)->paint(display);
	}
}

void  Widget::focus(int *fx,int *fy)
{
	if (children.empty())
	{
		*fx=xfocus_;
		*fy=yfocus_;
		return;
	}

	// check if anyone claims focus now
	for (unsigned int i=0;i<children.size();i++)
	{
		if (children.at(i)->isFocusWidget())
		{

			children.at(i)->focus(fx,fy);
			Dout(dc::trace,"Widget::focus() claimed by child at " << *fx << " " << *fy);
			return;
		}
	}

	// no winners so pick the first one
	std::vector<Widget *> ordered;
	orderChildren(ordered);
	for (unsigned int i=0;i<ordered.size();i++)
	{
		if (ordered.at(i)->acceptsFocus())
		{
			ordered.at(i)->focus(fx,fy);
			ordered.at(i)->setFocusWidget(true);
		}
	}

}

void Widget::setGeometry(int x,int y,int w,int h)
{
	x_=xfocus_=x;
	y_=yfocus_=y;
	if (w > 0) width_=w;
	if (h > 0) height_=h;
}

bool Widget::contains(int xp,int yp)
{
	return ((xp >= x_) && (xp <=x_+width_ -1) && (yp>=y_) && (yp <=y_+height_-1));
}

bool Widget::dirty()
{
	bool ret=dirty_;
	for (unsigned int i=0;i<children.size();i++)
		ret = ret || children.at(i)->dirty();
	return ret;
}

//
//
//

void Widget::setDirty(bool d)
{
	dirty_ = d;
}

void Widget::addCallback(Functor *cb)
{
	callbacks.push_back(cb);
}
//
// FIXME assumes one widget per line
//

Widget *Widget::focusWidgetBefore(Widget *w)
{
	int wx = w->x();
	int wy = w->y();
	int wht = w->height();
	Dout(dc::trace," Widget::focusWidgetBefore() " <<wx << " " << wy << " " << wht);

	std::vector<Widget *> ordered;
	orderChildren(ordered);
	for ( int i=0;i<(int) ordered.size();i++)
	{
		Widget *c =ordered.at(i);
		if (c==w)
		{
			Dout(dc::trace," Widget::focusWidgetBefore() match index = " << i);
			int indx;
			if ((i-1) >= 0)
				indx=i-1;
			else //(i-1 < 0)
				indx=ordered.size()-1; //.go to last
			Dout(dc::trace," Widget::focusWidgetBefore() focus at " << ordered.at(indx)->x() << " " << ordered.at(indx)->y());
			return ordered.at(indx);
		}
	}
	return NULL;
}

Widget *Widget::focusWidgetAfter(Widget *w)
{
	int wx = w->x();
	int wy = w->y();
	int wht = w->height();
	Dout(dc::trace," Widget::focusWidgetAfter() " <<wx << " " << wy << " " << wht);

	std::vector<Widget *> ordered;
	orderChildren(ordered);

	for (int i=0;i<(int)ordered.size();i++)
	{
		Widget *c =ordered.at(i);
		if (c==w)
		{
			int indx;
			if (i+1 <(int) ordered.size())
				indx=i+1;
			else //i+1 >= ordered.size()
				indx=0; // go to first
			Dout(dc::trace," Widget::focusWidgetBefore() focus at " << ordered.at(indx)->x() << " " << ordered.at(indx)->y());
			return ordered.at(indx);
		}
	}

	return NULL;
}

void Widget::orderChildren(std::vector<Widget *> & ordered)
{
	// spatially sorts children so that next focus widget can be determined

	// for each line build a list of widgets on that line
	// order that list by 'x' and then copy to the ordered list

	std::vector<Widget *> tmp;
	Dout(dc::trace,"Widget::orderChildren() children before =" << children.size());
	for (int j=0;j<=3;j++)
	{
		tmp.clear();
		for (unsigned int w=0;w<children.size();w++)
		{
			if (children.at(w)->y() == j && children.at(w)->acceptsFocus())
				tmp.push_back(children.at(w));
		}
		// order that list by 'x' and then copy to the ordered list

		for (int i=0;i<20;i++) // small search space so brute force
		{
			for (unsigned int w=0;w<tmp.size();w++)
			{
				if (tmp.at(w)->x()==i)
					ordered.push_back(tmp.at(w));
			}
		}

	}

	Dout(dc::trace,"Widget::orderChildren() children after =" << ordered.size());
}

<|endoftext|>