<commit_before>// This file is part of the dune-gdt project:
//   https://github.com/dune-community/dune-gdt
// Copyright 2010-2016 dune-gdt developers and contributors. All rights reserved.
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
// Authors:
//   Felix Schindler (2016)
//   Tobias Leibner  (2016)

#ifndef DUNE_GDT_TIMESTEPPER_IMPLICIT_RUNGEKUTTA_PARALLEL_HH
#define DUNE_GDT_TIMESTEPPER_IMPLICIT_RUNGEKUTTA_PARALLEL_HH

#include <utility>

#include <dune/xt/common/exceptions.hh>
#include <dune/xt/common/memory.hh>
#include <dune/xt/common/string.hh>

#include <dune/xt/la/container.hh>
#include <dune/xt/la/container/pattern.hh>

#include <dune/gdt/assembler/codim0-matrix-datahandle.hh>
#include <dune/gdt/operators/interfaces.hh>

#include "interface.hh"
#include "explicit-rungekutta.hh"

namespace Dune {
namespace GDT {

/** \brief Time stepper using diagonally implicit Runge Kutta methods
 *
 * Timestepper using diagonally implicit Runge Kutta methods to solve equations of the form u_t = r * L(u, t) where u is
 * a
 * discrete function, L an operator acting on u and r a scalar factor (e.g. -1).
 * The specific Runge Kutta method can be chosen as the third template argument. If your desired Runge Kutta method is
 * not contained in DiagonallyImplicitRungeKuttaMethods, choose TimeStepperMethods::diagonally_implicit_other and supply
 * a
 * DynamicMatrix< RangeFieldType > A and vectors (DynamicVector< RangeFieldType >) b and c in the constructor. Here, A,
 * b and c form the butcher tableau (see https://en.wikipedia.org/wiki/List_of_Runge%E2%80%93Kutta_methods, A is
 * composed of the coefficients a_{ij}, b of b_j and c of c_j). The default is a backward euler method.
 *
 * Notation (the factor r is omitted for notational simplicity): For an s-stage method,
 * \mathbf{u}^{n+1} = \mathbf{u}^n + dt \sum_{i=0}^{s-1} b_i \mathbf{k}_i
 * \mathbf{k}_i = L(\mathbf{u}_i, t_i)
 * t_i = t^n + dt c_i
 * \mathbf{u}_i = \mathbf{u}^n + dt \sum_{j=0}^{s-1} a_{ij} \mathbf{k}_j,
 * To solve for the \mathbf{u}_i, a Newton method with Armijo backtracking line search is used at each stage (if
 * a_{ii} \neq 0). The Newton update for each stage takes the form
 * N(\mathbf{u}_l) \mathbf{d}_l := (I - dt a_{ii} J_L(\mathbf{u}_l, t_i)) \mathbf{d}_l = -\mathbf{u}_l
 * + \mathbf{u}_{expl, i} + dt a_{ii} L(\mathbf{u}_l, t_i) =: -\mathbf{res}(\mathbf{u}_l),
 * where \mathbf{u}_l is the current iterate,
 * \mathbf{u}_{expl, i} = \mathbf{u}^n + dt \sum_{j=0}^{j-1} b_j \mathbf{k}_j
 * the explicit part of \mathbf{u}_i, J_L the jacobian of the operator L with respect to \mathbf{u} and
 * \mathbf{u}_{l+1} = \mathbf{u}_l + \alpha \mathbf{d}_l. The factor \alpha is found by an Armijo backtracking
 * line search such that
 * \mathbf{res}(\mathbf{u}_l + \alpha * \mathbf{d}_l) < \mathbf{res}(\mathbf{u}^n) + \beta D\mathbf{res} \alpha
 * \mathbf{d}_l,
 * where D\mathbf{res} = -N is the jacobian of the residual.
 *
 * \tparam OperatorImp Type of operator L
 * \tparam DiscreteFunctionImp Type of initial values
 */
template <class OperatorImp,
          class DiscreteFunctionImp,
          class TimeFieldImp = double,
          TimeStepperMethods method = TimeStepperMethods::implicit_euler>
class DiagonallyImplicitCodim0RungeKuttaTimeStepper : public TimeStepperInterface<DiscreteFunctionImp, TimeFieldImp>
{
  typedef TimeStepperInterface<DiscreteFunctionImp, TimeFieldImp> BaseType;
  typedef typename internal::ButcherArrayProvider<typename BaseType::RangeFieldType, TimeFieldImp, method>
      ButcherArrayProviderType;

public:
  using typename BaseType::DiscreteFunctionType;
  using typename BaseType::TimeFieldType;
  using typename BaseType::DomainFieldType;
  using typename BaseType::RangeFieldType;
  using typename BaseType::SolutionType;
  using typename BaseType::DataHandleType;

  typedef OperatorImp OperatorType;
  typedef typename Dune::DynamicMatrix<RangeFieldType> MatrixType;
  typedef typename Dune::DynamicVector<RangeFieldType> VectorType;
  typedef typename DiscreteFunctionType::SpaceType::CommunicatorType CommunicatorType;
  typedef typename Dune::DynamicVector<TimeFieldType> TimeVectorType;

  using BaseType::current_solution;
  using BaseType::current_time;

  /**
   * \brief Constructor for RungeKutta time stepper
   * \param op Operator L
   * \param initial_values Discrete function containing initial values for u at time t_0.
   * \param r Scalar factor (see above, default is 1)
   * \param t_0 Initial time (default is 0)
   * \param solver_type String to choose solver_type, for possible values see SolverType::types().
   * Default is the empty string, which chooses the default solver type.
   * \param A Coefficient matrix (only provide if you use TimeStepperMethods::diagonally_implicit_other)
   * \param b Coefficient vector (only provide if you use TimeStepperMethods::diagonally_implicit_other)
   * \param c Coefficients for time steps (only provide if you use TimeStepperMethods::diagonally_implicit_other)
   */
  DiagonallyImplicitCodim0RungeKuttaTimeStepper(const OperatorType& op,
                                                const DiscreteFunctionType& initial_values,
                                                const RangeFieldType r = 1.0,
                                                const TimeFieldImp t_0 = 0.0,
                                                const TimeFieldType beta = 1e-4,
                                                const std::string solver_type = "",
                                                const MatrixType& A = ButcherArrayProviderType::A(),
                                                const VectorType& b = ButcherArrayProviderType::b(),
                                                const TimeVectorType& c = ButcherArrayProviderType::c())
    : BaseType(t_0, initial_values)
    , op_(op)
    , r_(r)
    , u_i_(BaseType::current_solution())
    , u_i_expl_(u_i_)
    , d_(u_i_)
    , N_times_d_(u_i_)
    , res_(u_i_)
    , new_res_(u_i_)
    , u_i_plus_alpha_d_(u_i_)
    , beta_(beta)
    , newton_matrices_(u_i_.space().grid_view().size(0),
                       MatrixType(u_i_.space().mapper().maxNumDofs(), u_i_.space().mapper().maxNumDofs(), 0.))
    , A_(A)
    , b_(b)
    , c_(c)
    , num_stages_(A_.rows())
  {
    assert(A_.rows() == A_.cols() && "A has to be a square matrix");
    assert(b_.size() == A_.rows());
    assert(c_.size() == A_.rows());
    bool lower_triangular = true;
    bool diagonally_implicit = true;
    for (size_t ii = 0; ii < A_.rows(); ++ii) {
      for (size_t jj = ii; jj < A_.cols(); ++jj) {
        if (Dune::XT::Common::FloatCmp::ne(A_[ii][jj], 0.0))
          lower_triangular = false;
        if (jj > ii && Dune::XT::Common::FloatCmp::ne(A_[ii][jj], 0.0))
          diagonally_implicit = false;
      }
    }
    if (lower_triangular)
      DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong,
                 "Matrix A is strictly lower triangular, use ExplicitRungeKuttaTimeStepper!");
    if (!diagonally_implicit)
      DUNE_THROW(Dune::NotImplemented, "Matrix A has entries above the diagonal, this is not supported yet!");
    // store as many discrete functions as needed for intermediate stages
    for (size_t ii = 0; ii < num_stages_; ++ii) {
      stages_k_.emplace_back(u_i_);
    }
  } // constructor

  RangeFieldType calculate_l2_norm(const DiscreteFunctionType& disc_func)
  {
    RangeFieldType norm = 0;
    const auto& vector = disc_func.vector();
    const auto& mapper = disc_func.space().mapper();
    for (const auto& entity : Dune::elements(disc_func.space().grid_view(), Dune::Partitions::interiorBorder))
      for (const auto& index : mapper.globalIndices(entity))
        norm += std::pow(vector[index], 2);
    disc_func.space().grid_view().comm().sum(norm);
    norm = std::sqrt(norm);
    return norm;
  }

  virtual TimeFieldType step(const TimeFieldType dt, const TimeFieldType max_dt) override final
  {
    const TimeFieldType actual_dt = std::min(dt, max_dt);
    auto& t = current_time();
    auto& u_n = current_solution();
    // calculate stages
    for (size_t ii = 0; ii < num_stages_; ++ii) {
      // calculate u_expl
      if (ii == 0)
        u_i_expl_.vector() = u_n.vector();
      else
        u_i_expl_.vector() += stages_k_[ii - 1].vector() * (actual_dt * r_ * (A_[ii][ii - 1]));
      DataHandleType stages_k_ii_handle(stages_k_[ii]);
      // choose explicit or implicit treatment
      if (XT::Common::FloatCmp::eq(A_[ii][ii], 0.0)) {
        // stage is explicit
        stages_k_[ii].vector() *= RangeFieldType(0);
        op_.apply(
            u_i_expl_, stages_k_[ii], XT::Common::Parameter({{"t", {t + actual_dt * c_[ii]}}, {"dt", {actual_dt}}}));
        stages_k_[ii].space().grid_view().template communicate<DataHandleType>(
            stages_k_ii_handle, Dune::InteriorBorder_All_Interface, Dune::ForwardCommunication);
      } else {
        // stage is implicit, Newton method with Armijo backtracking
        // initial guess for u_i: u_i_expl_ + dt a_{ii} k_{i-1}
        u_i_.vector() =
            (ii == 0) ? u_n.vector() : u_i_expl_.vector() + stages_k_[ii - 1].vector() * (actual_dt * r_ * A_[ii][ii]);
        // calculate residual with current iterate
        stages_k_[ii].vector() *= RangeFieldType(0);
        op_.apply(u_i_, stages_k_[ii], XT::Common::Parameter({{"t", {t + actual_dt * c_[ii]}}, {"dt", {actual_dt}}}));
        stages_k_[ii].space().grid_view().template communicate<DataHandleType>(
            stages_k_ii_handle, Dune::InteriorBorder_All_Interface, Dune::ForwardCommunication);
        res_.vector() = u_i_expl_.vector() - u_i_.vector() + stages_k_[ii].vector() * (actual_dt * r_ * A_[ii][ii]);
        // calculate norm of residual
        RangeFieldType current_error = calculate_l2_norm(res_);
        RangeFieldType first_error = current_error;
        const double abs_tol = 1e-12;
        const double rel_reduction = 1e-8;
        const auto& grid_view = u_i_.space().grid_view().size(0);
        const auto& mapper = u_i_.space().mapper();
        const auto& index_set = grid_view.indexSet();

        //        std::vector<VectorType> entitywise_res(grid_view, VectorType(mapper.maxNumDofs(), mapper.maxNumDofs(),
        //        0.));
        //        for (const auto& entity : Dune::elements(grid_view)) {
        //          const auto& entity_index = index_set.index(entity);
        //          const auto& global_indices = mapper.globalIndices(entity);
        //          for (size_t ii = 0; ii < global_indices.size(); ++ii)
        //            entitywise_res[entity_index][ii] = res_.vector()[global_indices[ii]];
        //        }
        // Newton loop
        while (current_error > abs_tol && current_error > first_error * rel_reduction) {
          // assemble Newton matrix N = I - dt * a_{ii} * J_L
          op_.assemble_newton_matrix(
              newton_matrices_,
              u_i_,
              XT::Common::Parameter({{"t", {t + actual_dt * c_[ii]}}, {"factor", {-actual_dt * r_ * A_[ii][ii]}}}));
          // solve N d = res
          d_.vector() *= 0;
          op_.solve(newton_matrices_, res_, d_);

          // Armijo backtracking line search
          RangeFieldType alpha = 2;
          RangeFieldType error_threshold = abs_tol;
          while (current_error > error_threshold) {
            // decrease alpha and calculate current u_i
            alpha /= 2;
            // calculate error threshold with current alpha
            N_times_d_.vector() *= 0;
            op_.mv(newton_matrices_, d_, N_times_d_);
            N_times_d_.vector() = res_.vector() - (N_times_d_.vector() * alpha * beta_);
            error_threshold = calculate_l2_norm(N_times_d_);
            // calculate residual with current alpha
            u_i_plus_alpha_d_.vector() = u_i_.vector() + d_.vector() * alpha;
            stages_k_[ii].vector() *= RangeFieldType(0);
            op_.apply(u_i_plus_alpha_d_,
                      stages_k_[ii],
                      XT::Common::Parameter({{"t", {t + actual_dt * c_[ii]}}, {"dt", {actual_dt}}}));
            stages_k_[ii].space().grid_view().template communicate<DataHandleType>(
                stages_k_ii_handle, Dune::InteriorBorder_All_Interface, Dune::ForwardCommunication);
            new_res_.vector() = u_i_expl_.vector() - u_i_plus_alpha_d_.vector()
                                + stages_k_[ii].vector() * (actual_dt * r_ * A_[ii][ii]);
            current_error = calculate_l2_norm(new_res_);
          } // Armijo backtracking
          u_i_.vector() = u_i_plus_alpha_d_.vector();
          res_.vector() = new_res_.vector();
        } // Newton
      } // if (explicit) else (implicit)
    } // loop over stages

    // calculate value of u at next time step
    for (size_t ii = 0; ii < num_stages_; ++ii)
      u_n.vector() += stages_k_[ii].vector() * (r_ * actual_dt * b_[ii]);

    // augment time
    t += actual_dt;

    return dt;
  } // ... step(...)

private:
  const OperatorType& op_;
  const RangeFieldType r_;
  DiscreteFunctionType u_i_;
  DiscreteFunctionType u_i_expl_;
  DiscreteFunctionType d_;
  DiscreteFunctionType N_times_d_;
  DiscreteFunctionType res_;
  DiscreteFunctionType new_res_;
  DiscreteFunctionType u_i_plus_alpha_d_;
  const TimeFieldType beta_;
  std::vector<MatrixType> newton_matrices_;
  const MatrixType A_;
  const VectorType b_;
  const VectorType c_;
  std::vector<DiscreteFunctionType> stages_k_;
  const size_t num_stages_;
};

} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_TIMESTEPPER_IMPLICIT_RUNGEKUTTA_PARALLEL_HH
<commit_msg>[timestepper] remove unused file<commit_after><|endoftext|>


<commit_before>/*
Copyright (c) 2013 Daniele Bartolini, Michele Rossi
Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto

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 <GLES2/gl2.h>

#include "Assert.h"
#include "GLContext.h"
#include "Log.h"

namespace crown
{

static ANativeWindow* awindow = NULL;

//-----------------------------------------------------------------------------
void set_android_window(ANativeWindow* window)
{
    CE_ASSERT_NOT_NULL(window);

	awindow = window;
}

//-----------------------------------------------------------------------------
GLContext::GLContext() :
	num_configs(0)
{
}

//-----------------------------------------------------------------------------
void GLContext::create_context()
{
	EGLint format;

	// Screen format rgbx8888 with no alpha channel,
	// maybe it is wrong but is for testing
	EGLint attrib_list[]= { EGL_RED_SIZE,        8,
                            EGL_GREEN_SIZE,      8,
                            EGL_BLUE_SIZE,       8,
                            EGL_DEPTH_SIZE,      24,
                            EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
                            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                            EGL_NONE};

    EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };

    display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

    eglInitialize(display, NULL, NULL);

    eglBindAPI(EGL_OPENGL_ES_API);

    eglChooseConfig(display, attrib_list, &config, 1, &num_configs);

	eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format);

	ANativeWindow_setBuffersGeometry(awindow, 0, 0, format);

    context = eglCreateContext(display, config, EGL_NO_CONTEXT, attributes);

	surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)awindow, NULL);

    eglMakeCurrent(display, surface, surface, context);

    Log::i("EGL context created");
}

//-----------------------------------------------------------------------------
void GLContext::destroy_context()
{
 	eglDestroyContext(display, context);
 	eglDestroySurface(display, surface);
 	eglTerminate(display);

    Log::i("EGL context destroyed");
}

//-----------------------------------------------------------------------------
void GLContext::swap_buffers()
{
   	eglSwapBuffers(display, surface);
}

}<commit_msg>Wrap egl calls aroung EGL_CHECK for improved error detection<commit_after>/*
Copyright (c) 2013 Daniele Bartolini, Michele Rossi
Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto

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 <GLES2/gl2.h>

#include "Config.h"
#include "Assert.h"
#include "GLContext.h"
#include "Log.h"

namespace crown
{

static ANativeWindow* awindow;

//-----------------------------------------------------------------------------
static const char* egl_error_to_string(EGLint error)
{
    switch (error)
    {
        case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
        case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
        case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
        case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
        case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
        case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
        case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
        case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
        case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
        case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
        case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
        case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
        case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
        case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
        default: return "UNKNOWN_EGL_ERROR";
    }
}

//-----------------------------------------------------------------------------
#if defined(CROWN_DEBUG) || defined(CROWN_DEVELOPMENT)
    #define EGL_CHECK(function)\
        function;\
        do { EGLint error; CE_ASSERT((error = eglGetError()) == EGL_SUCCESS,\
                "EGL error: %s", egl_error_to_string(error)); } while (0)
#else
    #define EGL_CHECK(function)\
        function;
#endif

//-----------------------------------------------------------------------------
void set_android_window(ANativeWindow* window)
{
    CE_ASSERT_NOT_NULL(window);

	awindow = window;
}

//-----------------------------------------------------------------------------
GLContext::GLContext() :
	num_configs(0)
{
}

//-----------------------------------------------------------------------------
void GLContext::create_context()
{
	// Screen format rgbx8888 with no alpha channel,
	// maybe it is wrong but is for testing
	EGLint attrib_list[]= { EGL_RED_SIZE,        8,
                            EGL_GREEN_SIZE,      8,
                            EGL_BLUE_SIZE,       8,
                            EGL_DEPTH_SIZE,      24,
                            EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
                            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                            EGL_NONE};

    EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };

    display = EGL_CHECK(eglGetDisplay(EGL_DEFAULT_DISPLAY));

    EGL_CHECK(eglInitialize(display, NULL, NULL));

    EGL_CHECK(eglBindAPI(EGL_OPENGL_ES_API));

    EGL_CHECK(eglChooseConfig(display, attrib_list, &config, 1, &num_configs));

    EGLint format;
	EGL_CHECK(eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format));

	ANativeWindow_setBuffersGeometry(awindow, 0, 0, format);

    context = EGL_CHECK(eglCreateContext(display, config, EGL_NO_CONTEXT, attributes));

	surface = EGL_CHECK(eglCreateWindowSurface(display, config, (EGLNativeWindowType)awindow, NULL));

    EGL_CHECK(eglMakeCurrent(display, surface, surface, context));

    Log::i("EGL context created");
}

//-----------------------------------------------------------------------------
void GLContext::destroy_context()
{
    EGL_CHECK(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
 	EGL_CHECK(eglDestroyContext(display, context));
 	EGL_CHECK(eglDestroySurface(display, surface));
 	EGL_CHECK(eglTerminate(display));

    Log::i("EGL context destroyed");
}

//-----------------------------------------------------------------------------
void GLContext::swap_buffers()
{
   	EGL_CHECK(eglSwapBuffers(display, surface));
}

}
<|endoftext|>


<commit_before>#include "ros/ros.h"
#include "std_msgs/String.h"

#include <sstream>
#include "math.h"
#include <vector>
#include "../EventTriggerUtility.h"
#include "../PerformTaskConstants.h"
#include "elderly_care_simulation/EventTrigger.h"
using namespace elderly_care_simulation;
#include "elderly_care_simulation/PerformTask.h"
using namespace elderly_care_simulation;
#include "../EventNode.h"
#include <unistd.h> // sleep
#include "../EscortRobot.h"

#include "gtest/gtest.h"

EscortRobot doctor;

// Publishers and subcribers
ros::Publisher residentEventPublisher;
ros::Subscriber residentEventSubscriber;

void eventTriggeredCallback(elderly_care_simulation::EventTrigger msg) {
    doctor.eventTriggered(msg);
}

/**
 * This test suite tests the functionality of the EscortRobot class.
 * The EscortRobot ROS node under test is DoctorRobot.
 */
class EscortRobotTest : public ::testing::Test {
    protected:
        // You can remove any or all of the following functions if its body
        // is empty.

        EscortRobotTest() {
            // You can put any initialisation here.
        }

        virtual ~EscortRobotTest() {
            // You can do clean-up work that doesn't throw exceptions here.
        }

        // If the constructor and destructor are not enough for setting up
        // and cleaning up each test, you can define the following methods:

        virtual void SetUp() {
            // Code here will be called immediately after the constructor (right
            // before each test).
        }

        virtual void TearDown() {
            // Code here will be called immediately after each test (right
            // before the destructor).
        }
};

/**
 * Any calls to the doctor that are not VERY_ILL events should be ignored.
 */
TEST_F(EscortRobotTest, ignoreIrrelvantEvents) {

    // Sleep to allow the DoctorRobot to start
    // ros::Rate loop_rate(2);
    // loop_rate.sleep();

    ASSERT_TRUE(true);
}

int main(int argc, char **argv) {

    ros::init(argc, argv, "TestEscortRobot");
    ros::NodeHandle nodeHandle;

    geometry_msgs::Point base;
    base.x = 3.0f;
    base.y = 3.0f;

    geometry_msgs::Point hospital;
    hospital.x = 4.0f;
    hospital.y = 4.0f;

    doctor = EscortRobot(EVENT_TRIGGER_EVENT_TYPE_VERY_ILL, base, hospital);

    // Advertise and subscribe to topics
    residentEventPublisher = nodeHandle.advertise<elderly_care_simulation::EventTrigger>("resident_event",1000, true);
    residentEventSubscriber = nodeHandle.subscribe<elderly_care_simulation::EventTrigger>("resident_trigger",1000, eventTriggeredCallback);

    // Run tests to see if we received messages as expected
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}<commit_msg>Tested that the doctor will ignore non-VERY_ILL events. [#56]<commit_after>#include "ros/ros.h"
#include "std_msgs/String.h"

#include <sstream>
#include "math.h"
#include <vector>
#include "../EventTriggerUtility.h"
#include "../PerformTaskConstants.h"
#include "elderly_care_simulation/EventTrigger.h"
using namespace elderly_care_simulation;
#include "../EventNode.h"
#include <unistd.h> // sleep

#define private public
#include "../EscortRobot.h"

#include "gtest/gtest.h"

EscortRobot doctor;

// Publishers and subcribers
ros::Publisher residentEventPublisher;
ros::Subscriber residentEventSubscriber;

void eventTriggeredCallback(elderly_care_simulation::EventTrigger msg) {
    doctor.eventTriggered(msg);
}

/**
 * This test suite tests the functionality of the EscortRobot class.
 * The EscortRobot ROS node under test is DoctorRobot.
 */
class EscortRobotTest : public ::testing::Test {
    protected:
        // You can remove any or all of the following functions if its body
        // is empty.

        EscortRobotTest() {
            // You can put any initialisation here.
        }

        virtual ~EscortRobotTest() {
            // You can do clean-up work that doesn't throw exceptions here.
        }

        // If the constructor and destructor are not enough for setting up
        // and cleaning up each test, you can define the following methods:

        virtual void SetUp() {
            // Instantiate a new doctor for each test
            geometry_msgs::Point base;
            base.x = 3.0f;
            base.y = 3.0f;

            geometry_msgs::Point hospital;
            hospital.x = 4.0f;
            hospital.y = 4.0f;

            doctor = EscortRobot(EVENT_TRIGGER_EVENT_TYPE_VERY_ILL, base, hospital);
        }

        virtual void TearDown() {
            // Code here will be called immediately after each test (right
            // before the destructor).
        }
};

EventTrigger eventTriggerForEventType(int eventType) {
    EventTrigger msg;
    msg.msg_type = EVENT_TRIGGER_MSG_TYPE_REQUEST;
    msg.event_type = eventType;
    msg.event_priority = EVENT_TRIGGER_PRIORITY_HIGH;
    msg.event_weight = getEventWeight(eventType);
    msg.result = EVENT_TRIGGER_RESULT_UNDEFINED;
    return msg;
}

/**
 * Any calls to the doctor that are not VERY_ILL events should be ignored.
 */
TEST_F(EscortRobotTest, ignoreIrrelvantEvents) {

    ASSERT_EQ(EscortRobot::AT_BASE, doctor.currentLocationState);
    ASSERT_FALSE(doctor.performingTask);

    residentEventPublisher.publish(eventTriggerForEventType(EVENT_TRIGGER_EVENT_TYPE_MEDICATION));

    // The doctor should not change its state
    ASSERT_EQ(EscortRobot::AT_BASE, doctor.currentLocationState);
    ASSERT_FALSE(doctor.performingTask);
}

int main(int argc, char **argv) {

    ros::init(argc, argv, "TestEscortRobot");
    ros::NodeHandle nodeHandle;

    // Advertise and subscribe to topics
    residentEventPublisher = nodeHandle.advertise<elderly_care_simulation::EventTrigger>("resident_event",1000, true);
    residentEventSubscriber = nodeHandle.subscribe<elderly_care_simulation::EventTrigger>("resident_trigger",1000, eventTriggeredCallback);

    // Run tests to see if we received messages as expected
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}<|endoftext|>


<commit_before>/**
 * An example program utilizing most/all calls from the CUDA
 * Runtime API module:
 *
 *   Device Management
 *
 * but does not include the API calls relating to IPC (inter-
 * process communication) - sharing pointers to device memory
 * and events among operating system process. That should be
 * covered by a different program.
 *
 */
#include "cuda/api/pci_id_impl.hpp"
#include "cuda/api/device_count.hpp"
#include "cuda/api/device.hpp"
#include "cuda/api/error.hpp"

#include <cuda_runtime_api.h>

#include <iostream>
#include <string>
#include <cstdlib>
#include <cassert>

[[noreturn]] void die_(const std::string& message)
{
	std::cerr << message << "\n";
	exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
	if (cuda::device::count() == 0) {
		die_("No CUDA devices on this system");
	}

	// TODO: cudaChooseDevice

	// Being very cavalier about our command-line arguments here...
	cuda::device::id_t device_id =  (argc > 1) ?
		std::stoi(argv[1]) : cuda::device::default_device_id;

	if (cuda::device::count() <= device_id) {
		die_("No CUDA device with ID " + std::to_string(device_id));
	}

	auto device = cuda::device::get(device_id);

	std::cout << "Using CUDA device " << device.name() << " (having device ID " << device.id() << ")\n";

	if (device.id() != device_id) {
		die_("The device's reported ID and the ID for which we created the device differ: "
			+ std::to_string(device.id()) + " !=" +  std::to_string(device_id));
	}

	if (device.id() != device.memory().device_id()) {
		die_("The device's reported ID and the device's memory object's reported device ID differ: "
			+ std::to_string(device.id()) + " !=" +  std::to_string(device.memory().device_id()));
	}

	// Attributes and properties
	// ---------------------------

	auto max_registers_per_block = device.get_attribute(cudaDevAttrMaxRegistersPerBlock);
	std::cout
		<< "Maximum number of registers per block on this device: "
		<< max_registers_per_block << "\n";
	assert(device.properties().regsPerBlock == max_registers_per_block);

	// PCI bus IDs
	// --------------------

	auto pci_id = device.pci_id();
	std::string pci_id_str(pci_id);

	cuda::outstanding_error::ensure_none(cuda::do_clear_errors);

	auto re_obtained_device = cuda::device::get(pci_id_str);
	assert(re_obtained_device == device);

	// Memory - total and available
	// -----------------------------------

	auto device_global_mem = device.memory();
	auto total_memory = device_global_mem.amount_total();
	auto free_memory = device_global_mem.amount_total();

	std::cout
		<< "Device " << std::to_string(device.id()) << " reports it has:\n"
		<< free_memory << " Bytes free out of " << total_memory << " Bytes total global memory.\n";

	assert(free_memory < total_memory);

	// Specific attributes and properties with their own API calls:
	// L1/shared mem (CacheConfig), shared memory bank size (SharedMemConfig)
	// and stream priority range
	// ----------------------------------------------------------------

	std::string cache_preference_names[] = {
		"No preference",
		"Equal L1 and shared memory",
		"Prefer shared memory over L1",
		"Prefer L1 over shared memory",
	};

	auto cache_preference = device.cache_preference();
	std::cout << "The cache preference for device " << device.id() << " is: "
		<< cache_preference_names[(unsigned) cache_preference] << ".\n";

	auto new_cache_preference =
		cache_preference == cuda::multiprocessor_cache_preference_t::prefer_l1_over_shared_memory ?
		cuda::multiprocessor_cache_preference_t::prefer_shared_memory_over_l1 :
		cuda::multiprocessor_cache_preference_t::prefer_l1_over_shared_memory;
	device.set_cache_preference(new_cache_preference);
	cache_preference = device.cache_preference();
	assert(cache_preference == new_cache_preference);

	auto shared_mem_bank_size = device.shared_memory_bank_size();
	shared_mem_bank_size =
		(shared_mem_bank_size == cudaSharedMemBankSizeFourByte) ?
			cudaSharedMemBankSizeEightByte : cudaSharedMemBankSizeFourByte;
	device.set_shared_memory_bank_size(shared_mem_bank_size);
	auto stream_priority_range = device.stream_priority_range();
	std::cout << "Streams on device " << device.id() << " have priorities between "
		<< stream_priority_range.first << " and " <<
		(stream_priority_range.second == cuda::stream::unbounded_priority ? "(unbounded)" :
		std::to_string(stream_priority_range.second)) << ".\n";
	assert(
		stream_priority_range.second == cuda::stream::unbounded_priority ||
		stream_priority_range.first <= stream_priority_range.second
		);

	// Resource limits
	// --------------------

	auto printf_fifo_size = device.get_resource_limit(cudaLimitPrintfFifoSize);
	std::cout << "The printf FIFO size for device " << device_id << " is " << printf_fifo_size << ".\n";
	decltype(printf_fifo_size) new_printf_fifo_size =
		(printf_fifo_size <= 1024) ?  2 * printf_fifo_size : printf_fifo_size - 512;
	device.set_resource_limit(cudaLimitPrintfFifoSize, new_printf_fifo_size);
	printf_fifo_size = device.get_resource_limit(cudaLimitPrintfFifoSize);
	assert(printf_fifo_size == new_printf_fifo_size);

	// Flags - yes, yet another kind of attribute/property
	// ----------------------------------------------------

	std::cout << "Device " << device.id() << " uses a"
		<< (device.synch_scheduling_policy() ? " synchronous" : "n asynchronous")
		<< " scheduling policy.\n";
	std::cout << "Device " << device.id() << " is set to "
		<< (device.keeping_larger_local_mem_after_resize() ? "keeps" : "discards")
		<< " shared memory allocation after launch.\n";
	std::cout << "Device " << device.id()
		<< " is set " << (device.can_map_host_memory() ? "to allow" : "not to allow")
		<< " pinned mapped memory.\n";
	// TODO: Change the settings as well obtaining them

	// Peer-to-peer
	// --------------------

	auto device_count = cuda::device::count();
	if (device_count > 1) {
		// This makes assumptions about the valid IDs and their use
		auto peer_id = (argc > 2) ?
			std::stoi(argv[2]): (device.id() + 1) % cuda::device::count();
		auto peer_device = cuda::device::get(peer_id);
		if (device.can_access(peer_device)) {
			auto atomics_supported_over_link = cuda::device::peer_to_peer::get_attribute(
				cudaDevP2PAttrNativeAtomicSupported, device_id, peer_id);
			std::cout
				<< "Native atomics are " << (atomics_supported_over_link ? "" : "not ")
				<< "supported over the link from device " << device_id
				<< " to device " << peer_id << ".\n";
			device.disable_access_to(peer_device);
			// TODO: Try some device-to-device access here, expect an exception
			device.enable_access_to(peer_device);
			// TODO: Try some device-to-device access here
		}
	}

	// Current device manipulation
	// ----------------------------

	if (device_count > 1) {
		auto device_0 = cuda::device::get(0);
		auto device_1 = cuda::device::get(1);
		cuda::device::current::set(device_0.id());
		assert(cuda::device::current::get() == device_0);
		assert(cuda::device::current::get_id() == device_0.id());
		cuda::device::current::set(device_1.id());
		assert(cuda::device::current::get() == device_1);
		assert(cuda::device::current::get_id() == device_1.id());
	}

	try {
		cuda::device::current::set(device_count);
		die_("Should not have been able to set the current device to "
			+ std::to_string(device_count) + " since that's the device count, and "
			+ "the maximum valid ID should be " + std::to_string(device_count - 1)
			+ " (one less)");
	}
	catch(cuda::runtime_error& e) {
		assert(e.code() == cuda::status::invalid_device);
		// We expected to get this exception, just clear it
		cuda::outstanding_error::clear();
	}

	// Synchronize and reset
	// --------------------

	device.synchronize();
	device.reset();

	std::cout << "\nSUCCESS\n";
	return EXIT_SUCCESS;
}
<commit_msg>Device management example: The free memory could theoretically be equal to the total memory; making the assertion reflect that.<commit_after>/**
 * An example program utilizing most/all calls from the CUDA
 * Runtime API module:
 *
 *   Device Management
 *
 * but does not include the API calls relating to IPC (inter-
 * process communication) - sharing pointers to device memory
 * and events among operating system process. That should be
 * covered by a different program.
 *
 */
#include "cuda/api/pci_id_impl.hpp"
#include "cuda/api/device_count.hpp"
#include "cuda/api/device.hpp"
#include "cuda/api/error.hpp"

#include <cuda_runtime_api.h>

#include <iostream>
#include <string>
#include <cstdlib>
#include <cassert>

[[noreturn]] void die_(const std::string& message)
{
	std::cerr << message << "\n";
	exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
	if (cuda::device::count() == 0) {
		die_("No CUDA devices on this system");
	}

	// TODO: cudaChooseDevice

	// Being very cavalier about our command-line arguments here...
	cuda::device::id_t device_id =  (argc > 1) ?
		std::stoi(argv[1]) : cuda::device::default_device_id;

	if (cuda::device::count() <= device_id) {
		die_("No CUDA device with ID " + std::to_string(device_id));
	}

	auto device = cuda::device::get(device_id);

	std::cout << "Using CUDA device " << device.name() << " (having device ID " << device.id() << ")\n";

	if (device.id() != device_id) {
		die_("The device's reported ID and the ID for which we created the device differ: "
			+ std::to_string(device.id()) + " !=" +  std::to_string(device_id));
	}

	if (device.id() != device.memory().device_id()) {
		die_("The device's reported ID and the device's memory object's reported device ID differ: "
			+ std::to_string(device.id()) + " !=" +  std::to_string(device.memory().device_id()));
	}

	// Attributes and properties
	// ---------------------------

	auto max_registers_per_block = device.get_attribute(cudaDevAttrMaxRegistersPerBlock);
	std::cout
		<< "Maximum number of registers per block on this device: "
		<< max_registers_per_block << "\n";
	assert(device.properties().regsPerBlock == max_registers_per_block);

	// PCI bus IDs
	// --------------------

	auto pci_id = device.pci_id();
	std::string pci_id_str(pci_id);

	cuda::outstanding_error::ensure_none(cuda::do_clear_errors);

	auto re_obtained_device = cuda::device::get(pci_id_str);
	assert(re_obtained_device == device);

	// Memory - total and available
	// -----------------------------------

	auto device_global_mem = device.memory();
	auto total_memory = device_global_mem.amount_total();
	auto free_memory = device_global_mem.amount_total();

	std::cout
		<< "Device " << std::to_string(device.id()) << " reports it has:\n"
		<< free_memory << " Bytes free out of " << total_memory << " Bytes total global memory.\n";

	assert(free_memory <= total_memory);

	// Specific attributes and properties with their own API calls:
	// L1/shared mem (CacheConfig), shared memory bank size (SharedMemConfig)
	// and stream priority range
	// ----------------------------------------------------------------

	std::string cache_preference_names[] = {
		"No preference",
		"Equal L1 and shared memory",
		"Prefer shared memory over L1",
		"Prefer L1 over shared memory",
	};

	auto cache_preference = device.cache_preference();
	std::cout << "The cache preference for device " << device.id() << " is: "
		<< cache_preference_names[(unsigned) cache_preference] << ".\n";

	auto new_cache_preference =
		cache_preference == cuda::multiprocessor_cache_preference_t::prefer_l1_over_shared_memory ?
		cuda::multiprocessor_cache_preference_t::prefer_shared_memory_over_l1 :
		cuda::multiprocessor_cache_preference_t::prefer_l1_over_shared_memory;
	device.set_cache_preference(new_cache_preference);
	cache_preference = device.cache_preference();
	assert(cache_preference == new_cache_preference);

	auto shared_mem_bank_size = device.shared_memory_bank_size();
	shared_mem_bank_size =
		(shared_mem_bank_size == cudaSharedMemBankSizeFourByte) ?
			cudaSharedMemBankSizeEightByte : cudaSharedMemBankSizeFourByte;
	device.set_shared_memory_bank_size(shared_mem_bank_size);
	auto stream_priority_range = device.stream_priority_range();
	std::cout << "Streams on device " << device.id() << " have priorities between "
		<< stream_priority_range.first << " and " <<
		(stream_priority_range.second == cuda::stream::unbounded_priority ? "(unbounded)" :
		std::to_string(stream_priority_range.second)) << ".\n";
	assert(
		stream_priority_range.second == cuda::stream::unbounded_priority ||
		stream_priority_range.first <= stream_priority_range.second
		);

	// Resource limits
	// --------------------

	auto printf_fifo_size = device.get_resource_limit(cudaLimitPrintfFifoSize);
	std::cout << "The printf FIFO size for device " << device_id << " is " << printf_fifo_size << ".\n";
	decltype(printf_fifo_size) new_printf_fifo_size =
		(printf_fifo_size <= 1024) ?  2 * printf_fifo_size : printf_fifo_size - 512;
	device.set_resource_limit(cudaLimitPrintfFifoSize, new_printf_fifo_size);
	printf_fifo_size = device.get_resource_limit(cudaLimitPrintfFifoSize);
	assert(printf_fifo_size == new_printf_fifo_size);

	// Flags - yes, yet another kind of attribute/property
	// ----------------------------------------------------

	std::cout << "Device " << device.id() << " uses a"
		<< (device.synch_scheduling_policy() ? " synchronous" : "n asynchronous")
		<< " scheduling policy.\n";
	std::cout << "Device " << device.id() << " is set to "
		<< (device.keeping_larger_local_mem_after_resize() ? "keeps" : "discards")
		<< " shared memory allocation after launch.\n";
	std::cout << "Device " << device.id()
		<< " is set " << (device.can_map_host_memory() ? "to allow" : "not to allow")
		<< " pinned mapped memory.\n";
	// TODO: Change the settings as well obtaining them

	// Peer-to-peer
	// --------------------

	auto device_count = cuda::device::count();
	if (device_count > 1) {
		// This makes assumptions about the valid IDs and their use
		auto peer_id = (argc > 2) ?
			std::stoi(argv[2]): (device.id() + 1) % cuda::device::count();
		auto peer_device = cuda::device::get(peer_id);
		if (device.can_access(peer_device)) {
			auto atomics_supported_over_link = cuda::device::peer_to_peer::get_attribute(
				cudaDevP2PAttrNativeAtomicSupported, device_id, peer_id);
			std::cout
				<< "Native atomics are " << (atomics_supported_over_link ? "" : "not ")
				<< "supported over the link from device " << device_id
				<< " to device " << peer_id << ".\n";
			device.disable_access_to(peer_device);
			// TODO: Try some device-to-device access here, expect an exception
			device.enable_access_to(peer_device);
			// TODO: Try some device-to-device access here
		}
	}

	// Current device manipulation
	// ----------------------------

	if (device_count > 1) {
		auto device_0 = cuda::device::get(0);
		auto device_1 = cuda::device::get(1);
		cuda::device::current::set(device_0.id());
		assert(cuda::device::current::get() == device_0);
		assert(cuda::device::current::get_id() == device_0.id());
		cuda::device::current::set(device_1.id());
		assert(cuda::device::current::get() == device_1);
		assert(cuda::device::current::get_id() == device_1.id());
	}

	try {
		cuda::device::current::set(device_count);
		die_("Should not have been able to set the current device to "
			+ std::to_string(device_count) + " since that's the device count, and "
			+ "the maximum valid ID should be " + std::to_string(device_count - 1)
			+ " (one less)");
	}
	catch(cuda::runtime_error& e) {
		assert(e.code() == cuda::status::invalid_device);
		// We expected to get this exception, just clear it
		cuda::outstanding_error::clear();
	}

	// Synchronize and reset
	// --------------------

	device.synchronize();
	device.reset();

	std::cout << "\nSUCCESS\n";
	return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2015, Google Inc.
 * 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 Google Inc. 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.
 *
 */

#include <atomic>
#include <chrono>
#include <future>  // for async()
#include <iostream>
#include <memory>
#include <random>
#include <string>

#include "helper.h"
#include "route_guide.grpc_cb.pb.h"

using grpc_cb::Channel;
using grpc_cb::ChannelSptr;
using grpc_cb::Status;
using routeguide::Point;
using routeguide::Feature;
using routeguide::Rectangle;
using routeguide::RouteSummary;
using routeguide::RouteNote;
using routeguide::RouteGuide::Stub;

const float kCoordFactor = 10000000.0;
static unsigned seed = (unsigned)std::chrono::system_clock::now().time_since_epoch().count();
static std::default_random_engine generator(seed);

Point MakePoint(long latitude, long longitude) {
  Point p;
  p.set_latitude(latitude);
  p.set_longitude(longitude);
  return p;
}

routeguide::Rectangle MakeRect(const Point& lo, const Point& hi) {
  routeguide::Rectangle rect;
  *rect.mutable_lo() = lo;
  *rect.mutable_hi() = hi;
  return rect;
}

routeguide::Rectangle MakeRect(long lo_latitude, long lo_longitude,
                   long hi_latitude, long hi_longitude) {
  return MakeRect(MakePoint(lo_latitude, lo_longitude),
                  MakePoint(hi_latitude, hi_longitude));
}

Feature MakeFeature(const std::string& name,
                    long latitude, long longitude) {
  Feature f;
  f.set_name(name);
  f.mutable_location()->CopyFrom(MakePoint(latitude, longitude));
  return f;
}

RouteNote MakeRouteNote(const std::string& message,
                        long latitude, long longitude) {
  RouteNote n;
  n.set_message(message);
  n.mutable_location()->CopyFrom(MakePoint(latitude, longitude));
  return n;
}

void PrintFeature(const Feature& feature) {
  if (!feature.has_location()) {
    std::cout << "Server returns incomplete feature." << std::endl;
    return;
  }
  if (feature.name().empty()) {
    std::cout << "Found no feature at "
        << feature.location().latitude()/kCoordFactor << ", "
        << feature.location().longitude()/kCoordFactor << std::endl;
  }
  else {
    std::cout << "Found feature called " << feature.name()  << " at "
        << feature.location().latitude()/kCoordFactor << ", "
        << feature.location().longitude()/kCoordFactor << std::endl;
  }
}  // PrintFeature()

void PrintServerNote(const RouteNote& server_note) {
  std::cout << "Got message " << server_note.message()
            << " at " << server_note.location().latitude() << ", "
            << server_note.location().longitude() << std::endl;
}

void RandomSleep() {
  std::uniform_int_distribution<int> delay_distribution(500, 1500);
  std::this_thread::sleep_for(std::chrono::milliseconds(
      delay_distribution(generator)));
}

void RunWriteRouteNote(Stub::RouteChat_SyncReaderWriter sync_reader_writer) {
  std::vector<RouteNote> notes{
    MakeRouteNote("First message", 0, 0),
    MakeRouteNote("Second message", 0, 1),
    MakeRouteNote("Third message", 1, 0),
    MakeRouteNote("Fourth message", 0, 0)};
  for (const RouteNote& note : notes) {
    std::cout << "Sending message " << note.message()
              << " at " << note.location().latitude() << ", "
              << note.location().longitude() << std::endl;
    sync_reader_writer.Write(note);
    // RandomSleep();
  }
  sync_reader_writer.CloseWriting();
}

class RouteGuideClient {
 public:
  RouteGuideClient(std::shared_ptr<Channel> channel, const std::string& db)
      : stub_(new Stub(channel)) {
    routeguide::ParseDb(db, &feature_list_);
    assert(!feature_list_.empty());
  }

  void BlockingGetFeature() {
    Point point;
    Feature feature;
    point = MakePoint(409146138, -746188906);
    BlockingGetOneFeature(point, &feature);
    point = MakePoint(0, 0);
    BlockingGetOneFeature(point, &feature);
  }

  void BlockingListFeatures() {
    routeguide::Rectangle rect = MakeRect(
        400000000, -750000000, 420000000, -730000000);
    Feature feature;

    std::cout << "Looking for features between 40, -75 and 42, -73"
        << std::endl;

    auto sync_reader(stub_->SyncListFeatures(rect));
    while (sync_reader.ReadOne(&feature)) {
      std::cout << "Found feature called "
                << feature.name() << " at "
                << feature.location().latitude()/kCoordFactor << ", "
                << feature.location().longitude()/kCoordFactor << std::endl;
    }
    Status status = sync_reader.RecvStatus();
    if (status.ok()) {
      std::cout << "ListFeatures rpc succeeded." << std::endl;
    } else {
      std::cout << "ListFeatures rpc failed." << std::endl;
    }
  }

  void BlockingRecordRoute() {
    Point point;
    const int kPoints = 10;
    std::uniform_int_distribution<int> feature_distribution(
        0, feature_list_.size() - 1);

    auto sync_writer(stub_->SyncRecordRoute());
    for (int i = 0; i < kPoints; i++) {
      const Feature& f = feature_list_[feature_distribution(generator)];
      std::cout << "Visiting point "
                << f.location().latitude()/kCoordFactor << ", "
                << f.location().longitude()/kCoordFactor << std::endl;
      if (!sync_writer.Write(f.location())) {
        // Broken stream.
        break;
      }
      RandomSleep();
    }
    RouteSummary stats;
    // Recv reponse and status.
    Status status = sync_writer.Close(&stats);  // Todo: timeout
    if (status.ok()) {
      std::cout << "Finished trip with " << stats.point_count() << " points\n"
                << "Passed " << stats.feature_count() << " features\n"
                << "Traveled " << stats.distance() << " meters\n"
                << "It took " << stats.elapsed_time() << " seconds"
                << std::endl;
    } else {
      std::cout << "RecordRoute rpc failed." << std::endl;
    }
  }

  // Todo: Callback on client stream response and status.

  void BlockingRouteChat() {
    auto sync_reader_writer(stub_->SyncRouteChat());
    auto f = std::async(std::launch::async, [sync_reader_writer]() {
        RunWriteRouteNote(sync_reader_writer);
    });

    RouteNote server_note;
    while (sync_reader_writer.ReadOne(&server_note))
        PrintServerNote(server_note);

    f.wait();
    // Todo: Close() should auto close writing.
    Status status = sync_reader_writer.RecvStatus();
    if (!status.ok()) {
      std::cout << "RouteChat rpc failed." << std::endl;
    }
  }

 private:
  bool BlockingGetOneFeature(const Point& point, Feature* feature) {
    Status status = stub_->BlockingGetFeature(point, feature);
    if (!status.ok()) {
      std::cout << "GetFeature rpc failed." << std::endl;
      return false;
    }
    PrintFeature(*feature);
    return feature->has_location();
  }

  std::unique_ptr<Stub> stub_;
  std::vector<Feature> feature_list_;
};

void GetFeatureAsync(const ChannelSptr& channel) {
  Stub stub(channel);

  // Ignore error status.
  stub.AsyncGetFeature(MakePoint(0, 0),
                       [](const Feature& feature) { PrintFeature(feature); });

  // Ignore response.
  stub.AsyncGetFeature(MakePoint(0, 0));

  Point point1 = MakePoint(409146138, -746188906);
  stub.AsyncGetFeature(point1,
      [&stub](const Feature& feature) {
        PrintFeature(feature);
        stub.Shutdown();
      },
      [&stub](const Status& err) {
        std::cout << "AsyncGetFeature rpc failed. "
            << err.GetDetails() << std::endl;
        stub.Shutdown();
      });  // AsyncGetFeature()
  stub.BlockingRun();  // until stub.Shutdown()
}

void ListFeaturesAsync(const ChannelSptr& channel) {
  Stub stub(channel);
  routeguide::Rectangle rect = MakeRect(
      400000000, -750000000, 420000000, -730000000);
  std::cout << "Looking for features between 40, -75 and 42, -73" << std::endl;

  stub.AsyncListFeatures(rect,
    [](const Feature& feature) {
      std::cout << "Got feature " << feature.name() << " at "
          << feature.location().latitude()/kCoordFactor << ", "
          << feature.location().longitude()/kCoordFactor << std::endl;
    },
    [&stub](const Status& status) {
      std::cout << "End status: (" << status.GetCode() << ") "
          << status.GetDetails() << std::endl;
      stub.Shutdown();  // To break BlockingRun().
    });
  stub.BlockingRun();  // until stub.Shutdown()
}

void RecordRouteAsync(const ChannelSptr& channel,
                      const std::string& db) {
  assert(!db.empty());
  std::vector<Feature> feature_list;
  routeguide::ParseDb(db, &feature_list);
  assert(!feature_list.empty());

  Point point;
  const int kPoints = 10;
  std::uniform_int_distribution<int> feature_distribution(
      0, feature_list.size() - 1);

  Stub stub(channel);
  auto f = std::async(std::launch::async, [&stub]() { stub.BlockingRun(); });

  // ClientAsyncWriter<Point, RouteSummary> async_writer;
  auto async_writer = stub.AsyncRecordRoute();
  for (int i = 0; i < kPoints; i++) {
    const Feature& f = feature_list[feature_distribution(generator)];
    std::cout << "Visiting point "
              << f.location().latitude()/kCoordFactor << ", "
              << f.location().longitude()/kCoordFactor << std::endl;
    if (!async_writer.Write(f.location())) {
      // Broken stream.
      break;
    }
    RandomSleep();
  }
  // Recv reponse and status.
  async_writer.Close([](const Status& status, const RouteSummary& resp) {
    if (!status.ok()) {
      std::cout << "RecordRoute rpc failed." << std::endl;
      return;
    }
    std::cout << "Finished trip with " << resp.point_count() << " points\n"
              << "Passed " << resp.feature_count() << " features\n"
              << "Traveled " << resp.distance() << " meters\n"
              << "It took " << resp.elapsed_time() << " seconds" << std::endl;
  });

  // Todo: timeout

  stub.Shutdown();
}  // RecordRouteAsync()

void AsyncWriteRouteNotes(Stub::RouteChat_AsyncReaderWriter async_reader_writer) {
  std::vector<RouteNote> notes{
    MakeRouteNote("First message", 0, 0),
    MakeRouteNote("Second message", 0, 1),
    MakeRouteNote("Third message", 1, 0),
    MakeRouteNote("Fourth message", 0, 0)};
  for (const RouteNote& note : notes) {
    std::cout << "Sending message " << note.message()
              << " at " << note.location().latitude() << ", "
              << note.location().longitude() << std::endl;
    async_reader_writer.Write(note);
    RandomSleep();
  }
  async_reader_writer.CloseWriting();  // Optional.
}

void RouteChatAsync(const ChannelSptr& channel) {
  Stub stub(channel);
  std::atomic_bool bReaderDone = false;
  auto async_reader_writer(
      stub.AsyncRouteChat([&bReaderDone](const Status& status) {
        if (!status.ok()) {
          std::cout << "RouteChat rpc failed. " << status.GetDetails()
                    << std::endl;
        }
        bReaderDone = true;
      }));

  async_reader_writer.ReadEach(
      [](const RouteNote& note) { PrintServerNote(note); });

  auto f_write = std::async(std::launch::async, AsyncWriteRouteNotes, async_reader_writer);
  auto f_run = std::async(std::launch::async, [&stub]() {
    stub.BlockingRun();
  });

  while (!bReaderDone)
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
  stub.Shutdown();  // To break BlockingRun().
}  // RouteChatAsync()

void TestRpcTimeout(const ChannelSptr& channel) {
  Stub stub(channel);
  stub.SetCallTimeoutMs(INT64_MIN);
  Point point = MakePoint(0, 0);
  Feature feature;
  Status status = stub.BlockingGetFeature(point, &feature);
  assert(status.GetCode() == GRPC_STATUS_DEADLINE_EXCEEDED);
}

int main(int argc, char** argv) {
  // Expect only arg: --db_path=path/to/route_guide_db.json.
  std::string db = routeguide::GetDbFileContent(argc, argv);
  assert(!db.empty());
  ChannelSptr channel(new Channel("localhost:50051"));
  RouteGuideClient guide(channel, db);

  TestRpcTimeout(channel);

  std::cout << "---- BlockingGetFeature --------------" << std::endl;
  guide.BlockingGetFeature();
  std::cout << "---- BlockingListFeatures --------------" << std::endl;
  guide.BlockingListFeatures();
  std::cout << "---- BlockingRecordRoute --------------" << std::endl;
  guide.BlockingRecordRoute();
  std::cout << "---- BlockingRouteChat --------------" << std::endl;
  guide.BlockingRouteChat();

  std::cout << "---- GetFeatureAsync ----" << std::endl;
  GetFeatureAsync(channel);
  std::cout << "---- ListFeaturesAsync ----" << std::endl;
  ListFeaturesAsync(channel);
  std::cout << "---- RecordRouteAsnyc ----" << std::endl;
  RecordRouteAsync(channel, db);
  std::cout << "---- RouteChatAsync ---" << std::endl;
  RouteChatAsync(channel);

  return 0;
}
<commit_msg>Change RouteChatAsync() example.<commit_after>/*
 *
 * Copyright 2015, Google Inc.
 * 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 Google Inc. 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.
 *
 */

#include <atomic>
#include <chrono>
#include <future>  // for async()
#include <iostream>
#include <memory>
#include <random>
#include <string>

#include "helper.h"
#include "route_guide.grpc_cb.pb.h"

using grpc_cb::Channel;
using grpc_cb::ChannelSptr;
using grpc_cb::Status;
using routeguide::Point;
using routeguide::Feature;
using routeguide::Rectangle;
using routeguide::RouteSummary;
using routeguide::RouteNote;
using routeguide::RouteGuide::Stub;

const float kCoordFactor = 10000000.0;
static unsigned seed = (unsigned)std::chrono::system_clock::now().time_since_epoch().count();
static std::default_random_engine generator(seed);

Point MakePoint(long latitude, long longitude) {
  Point p;
  p.set_latitude(latitude);
  p.set_longitude(longitude);
  return p;
}

routeguide::Rectangle MakeRect(const Point& lo, const Point& hi) {
  routeguide::Rectangle rect;
  *rect.mutable_lo() = lo;
  *rect.mutable_hi() = hi;
  return rect;
}

routeguide::Rectangle MakeRect(long lo_latitude, long lo_longitude,
                   long hi_latitude, long hi_longitude) {
  return MakeRect(MakePoint(lo_latitude, lo_longitude),
                  MakePoint(hi_latitude, hi_longitude));
}

Feature MakeFeature(const std::string& name,
                    long latitude, long longitude) {
  Feature f;
  f.set_name(name);
  f.mutable_location()->CopyFrom(MakePoint(latitude, longitude));
  return f;
}

RouteNote MakeRouteNote(const std::string& message,
                        long latitude, long longitude) {
  RouteNote n;
  n.set_message(message);
  n.mutable_location()->CopyFrom(MakePoint(latitude, longitude));
  return n;
}

void PrintFeature(const Feature& feature) {
  if (!feature.has_location()) {
    std::cout << "Server returns incomplete feature." << std::endl;
    return;
  }
  if (feature.name().empty()) {
    std::cout << "Found no feature at "
        << feature.location().latitude()/kCoordFactor << ", "
        << feature.location().longitude()/kCoordFactor << std::endl;
  }
  else {
    std::cout << "Found feature called " << feature.name()  << " at "
        << feature.location().latitude()/kCoordFactor << ", "
        << feature.location().longitude()/kCoordFactor << std::endl;
  }
}  // PrintFeature()

void PrintServerNote(const RouteNote& server_note) {
  std::cout << "Got message " << server_note.message()
            << " at " << server_note.location().latitude() << ", "
            << server_note.location().longitude() << std::endl;
}

void RandomSleep() {
  std::uniform_int_distribution<int> delay_distribution(500, 1500);
  std::this_thread::sleep_for(std::chrono::milliseconds(
      delay_distribution(generator)));
}

void RunWriteRouteNote(Stub::RouteChat_SyncReaderWriter sync_reader_writer) {
  std::vector<RouteNote> notes{
    MakeRouteNote("First message", 0, 0),
    MakeRouteNote("Second message", 0, 1),
    MakeRouteNote("Third message", 1, 0),
    MakeRouteNote("Fourth message", 0, 0)};
  for (const RouteNote& note : notes) {
    std::cout << "Sending message " << note.message()
              << " at " << note.location().latitude() << ", "
              << note.location().longitude() << std::endl;
    sync_reader_writer.Write(note);
    // RandomSleep();
  }
  sync_reader_writer.CloseWriting();
}

class RouteGuideClient {
 public:
  RouteGuideClient(std::shared_ptr<Channel> channel, const std::string& db)
      : stub_(new Stub(channel)) {
    routeguide::ParseDb(db, &feature_list_);
    assert(!feature_list_.empty());
  }

  void BlockingGetFeature() {
    Point point;
    Feature feature;
    point = MakePoint(409146138, -746188906);
    BlockingGetOneFeature(point, &feature);
    point = MakePoint(0, 0);
    BlockingGetOneFeature(point, &feature);
  }

  void BlockingListFeatures() {
    routeguide::Rectangle rect = MakeRect(
        400000000, -750000000, 420000000, -730000000);
    Feature feature;

    std::cout << "Looking for features between 40, -75 and 42, -73"
        << std::endl;

    auto sync_reader(stub_->SyncListFeatures(rect));
    while (sync_reader.ReadOne(&feature)) {
      std::cout << "Found feature called "
                << feature.name() << " at "
                << feature.location().latitude()/kCoordFactor << ", "
                << feature.location().longitude()/kCoordFactor << std::endl;
    }
    Status status = sync_reader.RecvStatus();
    if (status.ok()) {
      std::cout << "ListFeatures rpc succeeded." << std::endl;
    } else {
      std::cout << "ListFeatures rpc failed." << std::endl;
    }
  }

  void BlockingRecordRoute() {
    Point point;
    const int kPoints = 10;
    std::uniform_int_distribution<int> feature_distribution(
        0, feature_list_.size() - 1);

    auto sync_writer(stub_->SyncRecordRoute());
    for (int i = 0; i < kPoints; i++) {
      const Feature& f = feature_list_[feature_distribution(generator)];
      std::cout << "Visiting point "
                << f.location().latitude()/kCoordFactor << ", "
                << f.location().longitude()/kCoordFactor << std::endl;
      if (!sync_writer.Write(f.location())) {
        // Broken stream.
        break;
      }
      RandomSleep();
    }
    RouteSummary stats;
    // Recv reponse and status.
    Status status = sync_writer.Close(&stats);  // Todo: timeout
    if (status.ok()) {
      std::cout << "Finished trip with " << stats.point_count() << " points\n"
                << "Passed " << stats.feature_count() << " features\n"
                << "Traveled " << stats.distance() << " meters\n"
                << "It took " << stats.elapsed_time() << " seconds"
                << std::endl;
    } else {
      std::cout << "RecordRoute rpc failed." << std::endl;
    }
  }

  // Todo: Callback on client stream response and status.

  void BlockingRouteChat() {
    auto sync_reader_writer(stub_->SyncRouteChat());
    auto f = std::async(std::launch::async, [sync_reader_writer]() {
        RunWriteRouteNote(sync_reader_writer);
    });

    RouteNote server_note;
    while (sync_reader_writer.ReadOne(&server_note))
        PrintServerNote(server_note);

    f.wait();
    // Todo: Close() should auto close writing.
    Status status = sync_reader_writer.RecvStatus();
    if (!status.ok()) {
      std::cout << "RouteChat rpc failed." << std::endl;
    }
  }

 private:
  bool BlockingGetOneFeature(const Point& point, Feature* feature) {
    Status status = stub_->BlockingGetFeature(point, feature);
    if (!status.ok()) {
      std::cout << "GetFeature rpc failed." << std::endl;
      return false;
    }
    PrintFeature(*feature);
    return feature->has_location();
  }

  std::unique_ptr<Stub> stub_;
  std::vector<Feature> feature_list_;
};

void GetFeatureAsync(const ChannelSptr& channel) {
  Stub stub(channel);

  // Ignore error status.
  stub.AsyncGetFeature(MakePoint(0, 0),
                       [](const Feature& feature) { PrintFeature(feature); });

  // Ignore response.
  stub.AsyncGetFeature(MakePoint(0, 0));

  Point point1 = MakePoint(409146138, -746188906);
  stub.AsyncGetFeature(point1,
      [&stub](const Feature& feature) {
        PrintFeature(feature);
        stub.Shutdown();
      },
      [&stub](const Status& err) {
        std::cout << "AsyncGetFeature rpc failed. "
            << err.GetDetails() << std::endl;
        stub.Shutdown();
      });  // AsyncGetFeature()
  stub.BlockingRun();  // until stub.Shutdown()
}

void ListFeaturesAsync(const ChannelSptr& channel) {
  Stub stub(channel);
  routeguide::Rectangle rect = MakeRect(
      400000000, -750000000, 420000000, -730000000);
  std::cout << "Looking for features between 40, -75 and 42, -73" << std::endl;

  stub.AsyncListFeatures(rect,
    [](const Feature& feature) {
      std::cout << "Got feature " << feature.name() << " at "
          << feature.location().latitude()/kCoordFactor << ", "
          << feature.location().longitude()/kCoordFactor << std::endl;
    },
    [&stub](const Status& status) {
      std::cout << "End status: (" << status.GetCode() << ") "
          << status.GetDetails() << std::endl;
      stub.Shutdown();  // To break BlockingRun().
    });
  stub.BlockingRun();  // until stub.Shutdown()
}

void RecordRouteAsync(const ChannelSptr& channel,
                      const std::string& db) {
  assert(!db.empty());
  std::vector<Feature> feature_list;
  routeguide::ParseDb(db, &feature_list);
  assert(!feature_list.empty());

  Point point;
  const int kPoints = 10;
  std::uniform_int_distribution<int> feature_distribution(
      0, feature_list.size() - 1);

  Stub stub(channel);
  auto f = std::async(std::launch::async, [&stub]() { stub.BlockingRun(); });

  // ClientAsyncWriter<Point, RouteSummary> async_writer;
  auto async_writer = stub.AsyncRecordRoute();
  for (int i = 0; i < kPoints; i++) {
    const Feature& f = feature_list[feature_distribution(generator)];
    std::cout << "Visiting point "
              << f.location().latitude()/kCoordFactor << ", "
              << f.location().longitude()/kCoordFactor << std::endl;
    if (!async_writer.Write(f.location())) {
      // Broken stream.
      break;
    }
    RandomSleep();
  }
  // Recv reponse and status.
  async_writer.Close([](const Status& status, const RouteSummary& resp) {
    if (!status.ok()) {
      std::cout << "RecordRoute rpc failed." << std::endl;
      return;
    }
    std::cout << "Finished trip with " << resp.point_count() << " points\n"
              << "Passed " << resp.feature_count() << " features\n"
              << "Traveled " << resp.distance() << " meters\n"
              << "It took " << resp.elapsed_time() << " seconds" << std::endl;
  });

  // Todo: timeout

  stub.Shutdown();
}  // RecordRouteAsync()

void AsyncWriteRouteNotes(Stub::RouteChat_AsyncReaderWriter async_reader_writer) {
  std::vector<RouteNote> notes{
    MakeRouteNote("First message", 0, 0),
    MakeRouteNote("Second message", 0, 1),
    MakeRouteNote("Third message", 1, 0),
    MakeRouteNote("Fourth message", 0, 0)};
  for (const RouteNote& note : notes) {
    std::cout << "Sending message " << note.message()
              << " at " << note.location().latitude() << ", "
              << note.location().longitude() << std::endl;
    async_reader_writer.Write(note);
    RandomSleep();
  }
  async_reader_writer.CloseWriting();  // Optional.
}

void RouteChatAsync(const ChannelSptr& channel) {
  Stub stub(channel);
  auto f_run = std::async(std::launch::async, [&stub]() {
    stub.BlockingRun();
  });

  std::atomic_bool bReaderDone = false;
  auto async_reader_writer(
      stub.AsyncRouteChat([&bReaderDone](const Status& status) {
        if (!status.ok()) {
          std::cout << "RouteChat rpc failed. " << status.GetDetails()
                    << std::endl;
        }
        bReaderDone = true;
      }));

  async_reader_writer.ReadEach(
      [](const RouteNote& note) { PrintServerNote(note); });
  AsyncWriteRouteNotes(async_reader_writer);

  while (!bReaderDone)
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
  stub.Shutdown();  // To break BlockingRun().
}  // RouteChatAsync()

void TestRpcTimeout(const ChannelSptr& channel) {
  Stub stub(channel);
  stub.SetCallTimeoutMs(INT64_MIN);
  Point point = MakePoint(0, 0);
  Feature feature;
  Status status = stub.BlockingGetFeature(point, &feature);
  assert(status.GetCode() == GRPC_STATUS_DEADLINE_EXCEEDED);
}

int main(int argc, char** argv) {
  // Expect only arg: --db_path=path/to/route_guide_db.json.
  std::string db = routeguide::GetDbFileContent(argc, argv);
  assert(!db.empty());
  ChannelSptr channel(new Channel("localhost:50051"));
  RouteGuideClient guide(channel, db);

  TestRpcTimeout(channel);

  std::cout << "---- BlockingGetFeature --------------" << std::endl;
  guide.BlockingGetFeature();
  std::cout << "---- BlockingListFeatures --------------" << std::endl;
  guide.BlockingListFeatures();
  std::cout << "---- BlockingRecordRoute --------------" << std::endl;
  guide.BlockingRecordRoute();
  std::cout << "---- BlockingRouteChat --------------" << std::endl;
  guide.BlockingRouteChat();

  std::cout << "---- GetFeatureAsync ----" << std::endl;
  GetFeatureAsync(channel);
  std::cout << "---- ListFeaturesAsync ----" << std::endl;
  ListFeaturesAsync(channel);
  std::cout << "---- RecordRouteAsnyc ----" << std::endl;
  RecordRouteAsync(channel, db);
  std::cout << "---- RouteChatAsync ---" << std::endl;
  RouteChatAsync(channel);

  return 0;
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2015 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
#include "config.h"
#include "memcached.h"
#include "runtime.h"
#include <exception>

Connection::Connection()
    : all_next(nullptr),
      all_prev(nullptr),
      sfd(INVALID_SOCKET),
      max_reqs_per_event(settings.default_reqs_per_event),
      nevents(0),
      sasl_conn(nullptr),
      state(conn_immediate_close),
      substate(bin_no_state),
      protocol(Protocol::Memcached),
      admin(false),
      registered_in_libevent(false),
      ev_flags(0),
      currentEvent(0),
      write_and_go(nullptr),
      write_and_free(nullptr),
      ritem(nullptr),
      rlbytes(0),
      item(nullptr),
      iov(nullptr), iovsize(0), iovused(0),
      msglist(nullptr), msgsize(0), msgused(0), msgcurr(0), msgbytes(0),
      request_addr_size(0),
      hdrsize(0),
      noreply(false),
      nodelay(false),
      refcount(0),
      supports_datatype(false),
      supports_mutation_extras(false),
      engine_storage(nullptr),
      start(0),
      cas(0),
      cmd(PROTOCOL_BINARY_CMD_INVALID),
      opaque(0),
      keylen(0),
      list_state(0),
      next(nullptr),
      thread(nullptr),
      aiostat(ENGINE_SUCCESS),
      ewouldblock(false),
      tap_iterator(nullptr),
      parent_port(0),
      dcp(0),
      cmd_context(nullptr),
      auth_context(nullptr),
      peername("unknown"),
      sockname("unknown")
{
    MEMCACHED_CONN_CREATE(this);

    memset(&event, 0, sizeof(event));
    memset(&request_addr, 0, sizeof(request_addr));
    memset(&read, 0, sizeof(read));
    memset(&write, 0, sizeof(write));
    memset(&dynamic_buffer, 0, sizeof(dynamic_buffer));
    memset(&ssl, 0, sizeof(ssl));
    memset(&bucket, 0, sizeof(bucket));
    memset(&binary_header, 0, sizeof(binary_header));

    state = conn_immediate_close;
    sfd = INVALID_SOCKET;
    resetBufferSize();
}

Connection::~Connection() {
    MEMCACHED_CONN_DESTROY(this);
    auth_destroy(auth_context);
    cbsasl_dispose(&sasl_conn);
    free(read.buf);
    free(write.buf);
    free(iov);
    free(msglist);

    cb_assert(reservedItems.empty());
    for (auto *ptr : temp_alloc) {
        free(ptr);
    }
}

void Connection::resetBufferSize() {
    bool ret = true;

    if (iovsize != IOV_LIST_INITIAL) {
        auto *ptr = reinterpret_cast<struct iovec*>
        (malloc(sizeof(struct iovec) * IOV_LIST_INITIAL));
        if (ptr != NULL) {
            free(iov);
            iov = ptr;
            iovsize = IOV_LIST_INITIAL;
        } else {
            ret = false;
        }
    }

    if (msgsize != MSG_LIST_INITIAL) {
        auto* ptr = reinterpret_cast<struct msghdr*>
        (malloc(sizeof(struct msghdr) * MSG_LIST_INITIAL));
        if (ptr != NULL) {
            free(msglist);
            msglist = ptr;
            msgsize = MSG_LIST_INITIAL;
        } else {
            ret = false;
        }
    }

    if (!ret) {
        free(msglist);
        free(iov);
        std::bad_alloc ex;
        throw ex;
    }
}

/**
 * Convert a sockaddr_storage to a textual string (no name lookup).
 *
 * @param addr the sockaddr_storage received from getsockname or
 *             getpeername
 * @param addr_len the current length used by the sockaddr_storage
 * @return a textual string representing the connection. or NULL
 *         if an error occurs (caller takes ownership of the buffer and
 *         must call free)
 */
static std::string sockaddr_to_string(const struct sockaddr_storage *addr,
                                      socklen_t addr_len)
{
    char host[50];
    char port[50];
    int err = getnameinfo((struct sockaddr*)addr, addr_len, host, sizeof(host),
                          port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
    if (err != 0) {
        settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL,
                                        "getnameinfo failed with error %d",
                                        err);
        return NULL;
    }

    return std::string(host) + ":" + port;
}

void Connection::resolveConnectionName() {
    int err;
    struct sockaddr_storage peer;
    socklen_t peer_len = sizeof(peer);

    if ((err = getpeername(sfd, (struct sockaddr*)&peer, &peer_len)) != 0) {
        settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL,
                                        "getpeername for socket %d with error %d",
                                        sfd, err);
        return;
    }

    struct sockaddr_storage sock;
    socklen_t sock_len = sizeof(sock);
    if ((err = getsockname(sfd, (struct sockaddr*)&sock, &sock_len)) != 0) {
        settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL,
                                        "getsock for socket %d with error %d",
                                        sfd, err);
        return;
    }

    peername = sockaddr_to_string(&peer, peer_len);
    sockname = sockaddr_to_string(&sock, sock_len);

}

bool Connection::unregisterEvent() {
    cb_assert(registered_in_libevent);
    cb_assert(sfd != INVALID_SOCKET);

    if (event_del(&event) == -1) {
        log_system_error(EXTENSION_LOG_WARNING,
                         NULL,
                         "Failed to remove connection to libevent: %s");
        return false;
    }

    registered_in_libevent = false;
    return true;
}

bool Connection::registerEvent() {
    cb_assert(!registered_in_libevent);
    cb_assert(sfd != INVALID_SOCKET);

    if (event_add(&event, NULL) == -1) {
        log_system_error(EXTENSION_LOG_WARNING,
                         NULL,
                         "Failed to add connection to libevent: %s");
        return false;
    }

    registered_in_libevent = true;

    return true;
}

bool Connection::updateEvent(const int new_flags) {
    struct event_base *base = event.ev_base;

    if (ssl.isEnabled() && ssl.isConnected() && (new_flags & EV_READ)) {
        /*
         * If we want more data and we have SSL, that data might be inside
         * SSL's internal buffers rather than inside the socket buffer. In
         * that case signal an EV_READ event without actually polling the
         * socket.
         */
        char dummy;
        /* SSL_pending() will not work here despite the name */
        int rv = ssl.peek(&dummy, 1);
        if (rv > 0) {
            /* signal a call to the handler */
            event_active(&event, EV_READ, 0);
            return true;
        }
    }

    if (ev_flags == new_flags) {
        // There is no change in the event flags!
        return true;
    }

    if (settings.verbose > 1) {
        settings.extensions.logger->log(EXTENSION_LOG_DEBUG, NULL,
                                        "Updated event for %d to read=%s, write=%s\n",
                                        sfd, (new_flags & EV_READ ? "yes" : "no"),
                                        (new_flags & EV_WRITE ? "yes" : "no"));
    }

    if (!unregisterEvent()) {
        settings.extensions.logger->log(EXTENSION_LOG_DEBUG, this,
                                        "Failed to remove connection from "
                                                "event notification library. Shutting "
                                                "down connection [%s - %s]",
                                        getPeername().c_str(), getSockname().c_str());
        return false;
    }

    event_set(&event, sfd, new_flags, event_handler, reinterpret_cast<void*>(this));
    event_base_set(base, &event);
    ev_flags = new_flags;

    if (!registerEvent()) {
        settings.extensions.logger->log(EXTENSION_LOG_DEBUG, this,
                                        "Failed to add connection to the "
                                                "event notification library. Shutting "
                                                "down connection [%s - %s]",
                                        getPeername().c_str(), getSockname().c_str());
        return false;
    }

    return true;
}

bool Connection::initializeEvent(struct event_base *base) {
    int event_flags = (EV_READ | EV_PERSIST);
    event_set(&event, sfd, event_flags, event_handler,
              reinterpret_cast<void *>(this));
    event_base_set(base, &event);
    ev_flags = event_flags;

    return registerEvent();
}

SslContext::~SslContext() {
    if (enabled) {
        disable();
    }
}

bool SslContext::enable(const std::string &cert, const std::string &pkey) {
    ctx = SSL_CTX_new(SSLv23_server_method());

    /* MB-12359 - Disable SSLv2 & SSLv3 due to POODLE */
    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);

    /* @todo don't read files, but use in-memory-copies */
    if (!SSL_CTX_use_certificate_chain_file(ctx, cert.c_str()) ||
        !SSL_CTX_use_PrivateKey_file(ctx, pkey.c_str(), SSL_FILETYPE_PEM)) {
        return false;
    }

    set_ssl_ctx_cipher_list(ctx);

    enabled = true;
    error = false;
    client = NULL;

    try {
        in.buffer.resize(settings.bio_drain_buffer_sz);
        out.buffer.resize(settings.bio_drain_buffer_sz);
    } catch (std::bad_alloc) {
        return false;
    }

    BIO_new_bio_pair(&application, in.buffer.size(),
                     &network, out.buffer.size());

    client = SSL_new(ctx);
    SSL_set_bio(client, application, application);

    return true;
}

void SslContext::disable() {
    if (network != nullptr) {
        BIO_free_all(network);
    }
    if (client != nullptr) {
        SSL_free(client);
    }
    error = false;
    if (ctx != nullptr) {
        SSL_CTX_free(ctx);
    }
    enabled = false;
}

void SslContext::drainBioRecvPipe(SOCKET sfd) {
    int n;
    bool stop = false;

    do {
        if (in.current < in.total) {
            n = BIO_write(network, in.buffer.data() + in.current,
                          in.total - in.current);
            if (n > 0) {
                in.current += n;
                if (in.current == in.total) {
                    in.current = in.total = 0;
                }
            } else {
                /* Our input BIO is full, no need to grab more data from
                 * the network at this time..
                 */
                return ;
            }
        }

        if (in.total < in.buffer.size()) {
            n = recv(sfd, in.buffer.data() + in.total,
                     in.buffer.size() - in.total, 0);
            if (n > 0) {
                in.total += n;
            } else {
                stop = true;
                if (n == 0) {
                    error = true; /* read end shutdown */
                } else {
                    auto error = GetLastNetworkError();
                    if (!is_blocking(error)) {
                        error = true;
                    }
                }
            }
        }
    } while (!stop);
}

void SslContext::drainBioSendPipe(SOCKET sfd) {
    int n;
    bool stop = false;

    do {
        if (out.current < out.total) {
            n = send(sfd, out.buffer.data() + out.current,
                     out.total - out.current, 0);
            if (n > 0) {
                out.current += n;
                if (out.current == out.total) {
                    out.current = out.total = 0;
                }
            } else {
                if (n == -1) {
                    auto error = GetLastNetworkError();
                    if (!is_blocking(error)) {
                        error = true;
                    }
                }
                return ;
            }
        }

        if (out.total == 0) {
            n = BIO_read(network, out.buffer.data(), out.buffer.size());
            if (n > 0) {
                out.total = n;
            } else {
                stop = true;
            }
        }
    } while (!stop);
}

void SslContext::dumpCipherList(SOCKET sfd) {
    settings.extensions.logger->log(EXTENSION_LOG_DEBUG, NULL,
                                    "%d: Using SSL ciphers:", sfd);
    int ii = 0;
    const char *cipher;
    while ((cipher = SSL_get_cipher_list(client, ii++)) != NULL) {
        settings.extensions.logger->log(EXTENSION_LOG_DEBUG, NULL,
                                        "%d    %s", sfd, cipher);
    }
}<commit_msg>Split complex cast into two statements<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2015 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
#include "config.h"
#include "memcached.h"
#include "runtime.h"
#include <exception>

Connection::Connection()
    : all_next(nullptr),
      all_prev(nullptr),
      sfd(INVALID_SOCKET),
      max_reqs_per_event(settings.default_reqs_per_event),
      nevents(0),
      sasl_conn(nullptr),
      state(conn_immediate_close),
      substate(bin_no_state),
      protocol(Protocol::Memcached),
      admin(false),
      registered_in_libevent(false),
      ev_flags(0),
      currentEvent(0),
      write_and_go(nullptr),
      write_and_free(nullptr),
      ritem(nullptr),
      rlbytes(0),
      item(nullptr),
      iov(nullptr), iovsize(0), iovused(0),
      msglist(nullptr), msgsize(0), msgused(0), msgcurr(0), msgbytes(0),
      request_addr_size(0),
      hdrsize(0),
      noreply(false),
      nodelay(false),
      refcount(0),
      supports_datatype(false),
      supports_mutation_extras(false),
      engine_storage(nullptr),
      start(0),
      cas(0),
      cmd(PROTOCOL_BINARY_CMD_INVALID),
      opaque(0),
      keylen(0),
      list_state(0),
      next(nullptr),
      thread(nullptr),
      aiostat(ENGINE_SUCCESS),
      ewouldblock(false),
      tap_iterator(nullptr),
      parent_port(0),
      dcp(0),
      cmd_context(nullptr),
      auth_context(nullptr),
      peername("unknown"),
      sockname("unknown")
{
    MEMCACHED_CONN_CREATE(this);

    memset(&event, 0, sizeof(event));
    memset(&request_addr, 0, sizeof(request_addr));
    memset(&read, 0, sizeof(read));
    memset(&write, 0, sizeof(write));
    memset(&dynamic_buffer, 0, sizeof(dynamic_buffer));
    memset(&ssl, 0, sizeof(ssl));
    memset(&bucket, 0, sizeof(bucket));
    memset(&binary_header, 0, sizeof(binary_header));

    state = conn_immediate_close;
    sfd = INVALID_SOCKET;
    resetBufferSize();
}

Connection::~Connection() {
    MEMCACHED_CONN_DESTROY(this);
    auth_destroy(auth_context);
    cbsasl_dispose(&sasl_conn);
    free(read.buf);
    free(write.buf);
    free(iov);
    free(msglist);

    cb_assert(reservedItems.empty());
    for (auto *ptr : temp_alloc) {
        free(ptr);
    }
}

void Connection::resetBufferSize() {
    bool ret = true;

    if (iovsize != IOV_LIST_INITIAL) {
        void* mem = malloc(sizeof(struct iovec) * IOV_LIST_INITIAL);
        auto* ptr = reinterpret_cast<struct iovec*>(mem);
        if (ptr != NULL) {
            free(iov);
            iov = ptr;
            iovsize = IOV_LIST_INITIAL;
        } else {
            ret = false;
        }
    }

    if (msgsize != MSG_LIST_INITIAL) {
        void* mem = malloc(sizeof(struct msghdr) * MSG_LIST_INITIAL);
        auto* ptr = reinterpret_cast<struct msghdr*>(mem);
        if (ptr != NULL) {
            free(msglist);
            msglist = ptr;
            msgsize = MSG_LIST_INITIAL;
        } else {
            ret = false;
        }
    }

    if (!ret) {
        free(msglist);
        free(iov);
        std::bad_alloc ex;
        throw ex;
    }
}

/**
 * Convert a sockaddr_storage to a textual string (no name lookup).
 *
 * @param addr the sockaddr_storage received from getsockname or
 *             getpeername
 * @param addr_len the current length used by the sockaddr_storage
 * @return a textual string representing the connection. or NULL
 *         if an error occurs (caller takes ownership of the buffer and
 *         must call free)
 */
static std::string sockaddr_to_string(const struct sockaddr_storage *addr,
                                      socklen_t addr_len)
{
    char host[50];
    char port[50];
    int err = getnameinfo((struct sockaddr*)addr, addr_len, host, sizeof(host),
                          port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);
    if (err != 0) {
        settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL,
                                        "getnameinfo failed with error %d",
                                        err);
        return NULL;
    }

    return std::string(host) + ":" + port;
}

void Connection::resolveConnectionName() {
    int err;
    struct sockaddr_storage peer;
    socklen_t peer_len = sizeof(peer);

    if ((err = getpeername(sfd, (struct sockaddr*)&peer, &peer_len)) != 0) {
        settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL,
                                        "getpeername for socket %d with error %d",
                                        sfd, err);
        return;
    }

    struct sockaddr_storage sock;
    socklen_t sock_len = sizeof(sock);
    if ((err = getsockname(sfd, (struct sockaddr*)&sock, &sock_len)) != 0) {
        settings.extensions.logger->log(EXTENSION_LOG_WARNING, NULL,
                                        "getsock for socket %d with error %d",
                                        sfd, err);
        return;
    }

    peername = sockaddr_to_string(&peer, peer_len);
    sockname = sockaddr_to_string(&sock, sock_len);

}

bool Connection::unregisterEvent() {
    cb_assert(registered_in_libevent);
    cb_assert(sfd != INVALID_SOCKET);

    if (event_del(&event) == -1) {
        log_system_error(EXTENSION_LOG_WARNING,
                         NULL,
                         "Failed to remove connection to libevent: %s");
        return false;
    }

    registered_in_libevent = false;
    return true;
}

bool Connection::registerEvent() {
    cb_assert(!registered_in_libevent);
    cb_assert(sfd != INVALID_SOCKET);

    if (event_add(&event, NULL) == -1) {
        log_system_error(EXTENSION_LOG_WARNING,
                         NULL,
                         "Failed to add connection to libevent: %s");
        return false;
    }

    registered_in_libevent = true;

    return true;
}

bool Connection::updateEvent(const int new_flags) {
    struct event_base *base = event.ev_base;

    if (ssl.isEnabled() && ssl.isConnected() && (new_flags & EV_READ)) {
        /*
         * If we want more data and we have SSL, that data might be inside
         * SSL's internal buffers rather than inside the socket buffer. In
         * that case signal an EV_READ event without actually polling the
         * socket.
         */
        char dummy;
        /* SSL_pending() will not work here despite the name */
        int rv = ssl.peek(&dummy, 1);
        if (rv > 0) {
            /* signal a call to the handler */
            event_active(&event, EV_READ, 0);
            return true;
        }
    }

    if (ev_flags == new_flags) {
        // There is no change in the event flags!
        return true;
    }

    if (settings.verbose > 1) {
        settings.extensions.logger->log(EXTENSION_LOG_DEBUG, NULL,
                                        "Updated event for %d to read=%s, write=%s\n",
                                        sfd, (new_flags & EV_READ ? "yes" : "no"),
                                        (new_flags & EV_WRITE ? "yes" : "no"));
    }

    if (!unregisterEvent()) {
        settings.extensions.logger->log(EXTENSION_LOG_DEBUG, this,
                                        "Failed to remove connection from "
                                                "event notification library. Shutting "
                                                "down connection [%s - %s]",
                                        getPeername().c_str(), getSockname().c_str());
        return false;
    }

    event_set(&event, sfd, new_flags, event_handler, reinterpret_cast<void*>(this));
    event_base_set(base, &event);
    ev_flags = new_flags;

    if (!registerEvent()) {
        settings.extensions.logger->log(EXTENSION_LOG_DEBUG, this,
                                        "Failed to add connection to the "
                                                "event notification library. Shutting "
                                                "down connection [%s - %s]",
                                        getPeername().c_str(), getSockname().c_str());
        return false;
    }

    return true;
}

bool Connection::initializeEvent(struct event_base *base) {
    int event_flags = (EV_READ | EV_PERSIST);
    event_set(&event, sfd, event_flags, event_handler,
              reinterpret_cast<void *>(this));
    event_base_set(base, &event);
    ev_flags = event_flags;

    return registerEvent();
}

SslContext::~SslContext() {
    if (enabled) {
        disable();
    }
}

bool SslContext::enable(const std::string &cert, const std::string &pkey) {
    ctx = SSL_CTX_new(SSLv23_server_method());

    /* MB-12359 - Disable SSLv2 & SSLv3 due to POODLE */
    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);

    /* @todo don't read files, but use in-memory-copies */
    if (!SSL_CTX_use_certificate_chain_file(ctx, cert.c_str()) ||
        !SSL_CTX_use_PrivateKey_file(ctx, pkey.c_str(), SSL_FILETYPE_PEM)) {
        return false;
    }

    set_ssl_ctx_cipher_list(ctx);

    enabled = true;
    error = false;
    client = NULL;

    try {
        in.buffer.resize(settings.bio_drain_buffer_sz);
        out.buffer.resize(settings.bio_drain_buffer_sz);
    } catch (std::bad_alloc) {
        return false;
    }

    BIO_new_bio_pair(&application, in.buffer.size(),
                     &network, out.buffer.size());

    client = SSL_new(ctx);
    SSL_set_bio(client, application, application);

    return true;
}

void SslContext::disable() {
    if (network != nullptr) {
        BIO_free_all(network);
    }
    if (client != nullptr) {
        SSL_free(client);
    }
    error = false;
    if (ctx != nullptr) {
        SSL_CTX_free(ctx);
    }
    enabled = false;
}

void SslContext::drainBioRecvPipe(SOCKET sfd) {
    int n;
    bool stop = false;

    do {
        if (in.current < in.total) {
            n = BIO_write(network, in.buffer.data() + in.current,
                          in.total - in.current);
            if (n > 0) {
                in.current += n;
                if (in.current == in.total) {
                    in.current = in.total = 0;
                }
            } else {
                /* Our input BIO is full, no need to grab more data from
                 * the network at this time..
                 */
                return ;
            }
        }

        if (in.total < in.buffer.size()) {
            n = recv(sfd, in.buffer.data() + in.total,
                     in.buffer.size() - in.total, 0);
            if (n > 0) {
                in.total += n;
            } else {
                stop = true;
                if (n == 0) {
                    error = true; /* read end shutdown */
                } else {
                    auto error = GetLastNetworkError();
                    if (!is_blocking(error)) {
                        error = true;
                    }
                }
            }
        }
    } while (!stop);
}

void SslContext::drainBioSendPipe(SOCKET sfd) {
    int n;
    bool stop = false;

    do {
        if (out.current < out.total) {
            n = send(sfd, out.buffer.data() + out.current,
                     out.total - out.current, 0);
            if (n > 0) {
                out.current += n;
                if (out.current == out.total) {
                    out.current = out.total = 0;
                }
            } else {
                if (n == -1) {
                    auto error = GetLastNetworkError();
                    if (!is_blocking(error)) {
                        error = true;
                    }
                }
                return ;
            }
        }

        if (out.total == 0) {
            n = BIO_read(network, out.buffer.data(), out.buffer.size());
            if (n > 0) {
                out.total = n;
            } else {
                stop = true;
            }
        }
    } while (!stop);
}

void SslContext::dumpCipherList(SOCKET sfd) {
    settings.extensions.logger->log(EXTENSION_LOG_DEBUG, NULL,
                                    "%d: Using SSL ciphers:", sfd);
    int ii = 0;
    const char *cipher;
    while ((cipher = SSL_get_cipher_list(client, ii++)) != NULL) {
        settings.extensions.logger->log(EXTENSION_LOG_DEBUG, NULL,
                                        "%d    %s", sfd, cipher);
    }
}<|endoftext|>