<commit_before>#if !(defined MPL_OPERATOR_HPP)

#define MPL_OPERATOR_HPP

#include <mpi.h>
#include <functional>
#include <type_traits>

namespace mpl {

  template<typename T>
  struct max {
    T operator()(const T &x, const T &y) const {
      return (x<y) ? y : x;
    }
  };

  template<typename T>
  struct min {
    T operator()(const T &x, const T &y) const {
      return !(y<x) ? x : y;
    }
  };

  template<typename T>
  struct plus {
    T operator()(const T &x, const T &y) const {
      return x+y;
    }
  };

  template<typename T>
  struct multiplies {
    T operator()(const T &x, const T &y) const {
      return x*y;
    }
  };

  template<typename T>
  struct logical_and {
    T operator()(const T &x, const T &y) const {
      return x and y;
    }
  };

  template<typename T>
  struct logical_or {
    T operator()(const T &x, const T &y) const {
      return x or y;
    }
  };

  template<typename T>
  struct logical_xor {
    T operator()(const T &x, const T &y) const {
      return x xor y;
    }
  };

  template<typename T>
  struct bit_and {
    T operator()(const T &x, const T &y) const {
      return x & y;
    }
  };

  template<typename T>
  struct bit_or {
    T operator()(const T &x, const T &y) const {
      return x | y;
    }
  };

  template<typename T>
  struct bit_xor {
    T operator()(const T &x, const T &y) const {
      return x ^ y;
    }
  };

  // -------------------------------------------------------------------

  template<typename F>
  struct op_traits {
    static constexpr bool is_commutative() {
      return false;
    }
  };

  template<typename T>
  struct op_traits<max<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  template<typename T>
  struct op_traits<min<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  template<typename T>
  struct op_traits<plus<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  template<typename T>
  struct op_traits<multiplies<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  template<typename T>
  struct op_traits<logical_and<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  template<typename T>
  struct op_traits<logical_or<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  template<typename T>
  struct op_traits<logical_xor<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  template<typename T>
  struct op_traits<bit_and<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  template<typename T>
  struct op_traits<bit_or<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  template<typename T>
  struct op_traits<bit_xor<T>> {
    static constexpr bool is_commutative() {
      return true;
    }
  };

  // -------------------------------------------------------------------

  namespace detail {

    template<typename T> struct remove_class { };
    template<typename C, typename R, typename... A>
    struct remove_class<R(C::*)(A...)> { using type = R(A...); };
    template<typename C, typename R, typename... A>
    struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
    template<typename C, typename R, typename... A>
    struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
    template<typename C, typename R, typename... A>
    struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };

    template<typename T>
    struct get_signature_impl {
      using type = typename remove_class<decltype(&std::remove_reference<T>::type::operator())>::type;
    };

    template<typename R, typename... A>
    struct get_signature_impl<R(A...)> { using type = R(A...); };
    template<typename R, typename... A>
    struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
    template<typename R, typename... A>
    struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
    template<typename T> using get_signature = typename get_signature_impl<T>::type;

    template<typename T>
    struct get_result_type_impl;
    template<typename R, typename... A>
    struct get_result_type_impl<R(A...)> { using type = R; };
    template<typename T> using get_result_type = typename get_result_type_impl<T>::type;

    template<typename T>
    struct get_first_argument_type_impl;
    template<typename R, typename A1, typename... A>
    struct get_first_argument_type_impl<R(A1, A...)> { using type = A1; };
    template<typename T> using get_first_argument_type = typename get_first_argument_type_impl<T>::type;

    template<typename T>
    struct get_second_argument_type_impl;
    template<typename R, typename A1, typename A2, typename... A>
    struct get_second_argument_type_impl<R(A1, A2, A...)> { using type = A2; };
    template<typename T> using get_second_argument_type = typename get_second_argument_type_impl<T>::type;

    template<typename F> using make_function_type = std::function<get_signature<F>>;

    template<typename F> make_function_type<F> make_function(F &&f) {
      return make_function_type<F>(std::forward<F>(f));
    }

    //------------------------------------------------------------------

    template<typename T, typename F>
    class op {
    public:
      typedef F functor;
      typedef get_signature<functor> signature;
      typedef typename std::decay<get_first_argument_type<signature>>::type first_argument_type;
      typedef typename std::decay<get_second_argument_type<signature>>::type second_argument_type;
      typedef typename std::decay<get_result_type<signature>>::type result_type;
      static_assert(std::is_assignable<typename std::add_lvalue_reference<first_argument_type>::type, T>::value and
		    std::is_assignable<typename std::add_lvalue_reference<second_argument_type>::type, T>::value and
		    std::is_assignable<T &, result_type>::value, "argument type mismatch");

      static functor *f;
      static void apply(void *invec, void *inoutvec, int *len,
    			MPI_Datatype *datatype) {
	functor local_f(*f);
    	T *i1=reinterpret_cast<T *>(invec);
    	T *i2=reinterpret_cast<T *>(inoutvec);
    	for (int i=0, i_end=*len; i<i_end; ++i, ++i1, ++i2)
    	  *i2=local_f(*i1, *i2);
      }
      MPI_Op mpi_op;
      op() {
    	MPI_Op_create(op::apply, op_traits<functor>::is_commutative(), &mpi_op);
      }
      ~op() {
    	MPI_Op_free(&mpi_op);
      }
      static constexpr bool is_s_commutative() {
    	return op_traits<functor>::is_commutative();
      }
      op(op const &)=delete;
      void operator=(op const&)=delete;
    };

    template<typename T, typename F>
    F *op<T, F>::f;

    template<typename T, typename F>
    op<T, F> & get_op() {
      static detail::op<T, F> op;
      return op;
    }

  }

}

#endif
<commit_msg>make is_commutative a constexpr member variable<commit_after>#if !(defined MPL_OPERATOR_HPP)

#define MPL_OPERATOR_HPP

#include <mpi.h>
#include <functional>
#include <type_traits>

namespace mpl {

  template<typename T>
  struct max {
    T operator()(const T &x, const T &y) const {
      return (x<y) ? y : x;
    }
  };

  template<typename T>
  struct min {
    T operator()(const T &x, const T &y) const {
      return !(y<x) ? x : y;
    }
  };

  template<typename T>
  struct plus {
    T operator()(const T &x, const T &y) const {
      return x+y;
    }
  };

  template<typename T>
  struct multiplies {
    T operator()(const T &x, const T &y) const {
      return x*y;
    }
  };

  template<typename T>
  struct logical_and {
    T operator()(const T &x, const T &y) const {
      return x and y;
    }
  };

  template<typename T>
  struct logical_or {
    T operator()(const T &x, const T &y) const {
      return x or y;
    }
  };

  template<typename T>
  struct logical_xor {
    T operator()(const T &x, const T &y) const {
      return x xor y;
    }
  };

  template<typename T>
  struct bit_and {
    T operator()(const T &x, const T &y) const {
      return x & y;
    }
  };

  template<typename T>
  struct bit_or {
    T operator()(const T &x, const T &y) const {
      return x | y;
    }
  };

  template<typename T>
  struct bit_xor {
    T operator()(const T &x, const T &y) const {
      return x ^ y;
    }
  };

  // -------------------------------------------------------------------

  template<typename F>
  struct op_traits {
    static constexpr bool is_commutative=false;
  };

  template<typename T>
  struct op_traits<max<T>> {
    static constexpr bool is_commutative=true;
  };

  template<typename T>
  struct op_traits<min<T>> {
    static constexpr bool is_commutative=true;
  };

  template<typename T>
  struct op_traits<plus<T>> {
    static constexpr bool is_commutative=true;
  };

  template<typename T>
  struct op_traits<multiplies<T>> {
    static constexpr bool is_commutative=true;
  };

  template<typename T>
  struct op_traits<logical_and<T>> {
    static constexpr bool is_commutative=true;
  };

  template<typename T>
  struct op_traits<logical_or<T>> {
    static constexpr bool is_commutative=true;
  };

  template<typename T>
  struct op_traits<logical_xor<T>> {
    static constexpr bool is_commutative=true;
  };

  template<typename T>
  struct op_traits<bit_and<T>> {
    static constexpr bool is_commutative=true;
  };

  template<typename T>
  struct op_traits<bit_or<T>> {
    static constexpr bool is_commutative=true;
  };

  template<typename T>
  struct op_traits<bit_xor<T>> {
    static constexpr bool is_commutative=true;
  };

  // -------------------------------------------------------------------

  namespace detail {

    template<typename T> struct remove_class { };
    template<typename C, typename R, typename... A>
    struct remove_class<R(C::*)(A...)> { using type = R(A...); };
    template<typename C, typename R, typename... A>
    struct remove_class<R(C::*)(A...) const> { using type = R(A...); };
    template<typename C, typename R, typename... A>
    struct remove_class<R(C::*)(A...) volatile> { using type = R(A...); };
    template<typename C, typename R, typename... A>
    struct remove_class<R(C::*)(A...) const volatile> { using type = R(A...); };

    template<typename T>
    struct get_signature_impl {
      using type = typename remove_class<decltype(&std::remove_reference<T>::type::operator())>::type;
    };

    template<typename R, typename... A>
    struct get_signature_impl<R(A...)> { using type = R(A...); };
    template<typename R, typename... A>
    struct get_signature_impl<R(&)(A...)> { using type = R(A...); };
    template<typename R, typename... A>
    struct get_signature_impl<R(*)(A...)> { using type = R(A...); };
    template<typename T> using get_signature = typename get_signature_impl<T>::type;

    template<typename T>
    struct get_result_type_impl;
    template<typename R, typename... A>
    struct get_result_type_impl<R(A...)> { using type = R; };
    template<typename T> using get_result_type = typename get_result_type_impl<T>::type;

    template<typename T>
    struct get_first_argument_type_impl;
    template<typename R, typename A1, typename... A>
    struct get_first_argument_type_impl<R(A1, A...)> { using type = A1; };
    template<typename T> using get_first_argument_type = typename get_first_argument_type_impl<T>::type;

    template<typename T>
    struct get_second_argument_type_impl;
    template<typename R, typename A1, typename A2, typename... A>
    struct get_second_argument_type_impl<R(A1, A2, A...)> { using type = A2; };
    template<typename T> using get_second_argument_type = typename get_second_argument_type_impl<T>::type;

    template<typename F> using make_function_type = std::function<get_signature<F>>;

    template<typename F> make_function_type<F> make_function(F &&f) {
      return make_function_type<F>(std::forward<F>(f));
    }

    //------------------------------------------------------------------

    template<typename T, typename F>
    class op {
    public:
      typedef F functor;
      typedef get_signature<functor> signature;
      typedef typename std::decay<get_first_argument_type<signature>>::type first_argument_type;
      typedef typename std::decay<get_second_argument_type<signature>>::type second_argument_type;
      typedef typename std::decay<get_result_type<signature>>::type result_type;
      static_assert(std::is_assignable<typename std::add_lvalue_reference<first_argument_type>::type, T>::value and
		    std::is_assignable<typename std::add_lvalue_reference<second_argument_type>::type, T>::value and
		    std::is_assignable<T &, result_type>::value, "argument type mismatch");

      static constexpr bool is_commutative=op_traits<functor>::is_commutative;
      static functor *f;
      static void apply(void *invec, void *inoutvec, int *len,
    			MPI_Datatype *datatype) {
	functor local_f(*f);
    	T *i1=reinterpret_cast<T *>(invec);
    	T *i2=reinterpret_cast<T *>(inoutvec);
    	for (int i=0, i_end=*len; i<i_end; ++i, ++i1, ++i2)
    	  *i2=local_f(*i1, *i2);
      }
      MPI_Op mpi_op;
      op() {
    	MPI_Op_create(op::apply, is_commutative, &mpi_op);
      }
      ~op() {
    	MPI_Op_free(&mpi_op);
      }
      op(op const &)=delete;
      void operator=(op const&)=delete;
    };

    template<typename T, typename F>
    F *op<T, F>::f;

    template<typename T, typename F>
    op<T, F> & get_op() {
      static detail::op<T, F> op;
      return op;
    }

  }

}

#endif
<|endoftext|>


<commit_before>#include "gtest/gtest.h"
#include "log_sigmoid.hpp"

TEST(LogSigmoidTest, Sigmoid0) {
    double expectedVal = 0.5;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(0);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, Sigmoid1) {
    double expectedVal = 0.7310585786300048792511592418218362743651446401650565192763659079190404530702046393874745320759812453;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(1);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, Sigmoid2) {
    double expectedVal = 0.8807970779778824440597291413023967952063842986289682757984052500609766222883192417294737608368383572;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(2);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, Sigmoid3) {
    double expectedVal = 0.9525741268224332191211518482282477986138205675793908992821119912255512884972897661142163455089399610;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(3);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, Sigmoid4) {
    double expectedVal = 0.9820137900379084419732068620504615751275149881204673880241316087065539731588051012797374250226038446;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(4);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, DISABLED_SigmoidDeriv0) {
    double expectedVal = 0.5;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(0);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, DISABLED_SigmoidDeriv1) {
    double expectedVal = 0.2099871708070130346972483695208507224585933641153854773566557201222949497620241528078470044311593630;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(1);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, DISABLED_SigmoidDeriv2) {
    double expectedVal = 0.03532541242658223284312382793052739615606362991882798056243190491343103030472792095272482867191154543;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(2);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, DISABLED_SigmoidDeriv3) {
    double expectedVal = 0.004933018582720095636578084841761743663660804559399863602124745023211662148054750855857728873900145776;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(3);

    ASSERT_NEAR(expectedVal, actualVal, 0.0000000000000001L);
}

TEST(LogSigmoidTest, DISABLED_SigmoidDeriv4) {
    double expectedVal = 0.0006704753415129484399851094516655066155078830348121154630238921104701595170235635223347777256572355192;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(4);

    ASSERT_NEAR(expectedVal, actualVal, 0.0000000000000001L);
}

<commit_msg>Add log sigmoid derivative tests<commit_after>#include "gtest/gtest.h"
#include "log_sigmoid.hpp"

TEST(LogSigmoidTest, Sigmoid0) {
    double expectedVal = 0.5;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(0);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, Sigmoid1) {
    double expectedVal = 0.7310585786300048792511592418218362743651446401650565192763659079190404530702046393874745320759812453;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(1);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, Sigmoid2) {
    double expectedVal = 0.8807970779778824440597291413023967952063842986289682757984052500609766222883192417294737608368383572;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(2);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, Sigmoid3) {
    double expectedVal = 0.9525741268224332191211518482282477986138205675793908992821119912255512884972897661142163455089399610;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(3);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, Sigmoid4) {
    double expectedVal = 0.9820137900379084419732068620504615751275149881204673880241316087065539731588051012797374250226038446;

    LogSigmoid kernel;
    double actualVal = kernel.kernelFunc(4);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, SigmoidDeriv0) {
    double expectedVal = 0.25;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(0);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, SigmoidDeriv1) {
    double expectedVal = 0.1966119332414818525374247335859090256222672854273135775763268324868302873926450596563475285482006501;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(1);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, SigmoidDeriv2) {
    double expectedVal = 0.1049935854035065173486241847604253612292966820576927386783278600611474748810120764039235022155796815;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(2);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, SigmoidDeriv3) {
    double expectedVal = 0.04517665973091213264936002843565163799485995239748969246454782778480922682026316173926092989723483536;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(3);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

TEST(LogSigmoidTest, SigmoidDeriv4) {
    double expectedVal = 0.01766270621329111642156191396526369807803181495941399028121595245671551515236396047636241433595577272;

    LogSigmoid kernel;
    double actualVal = kernel.kernelDeriv(4);

    ASSERT_DOUBLE_EQ(expectedVal, actualVal);
}

<|endoftext|>


<commit_before>#include <allegro5/allegro.h>

#include <sstream>
#include <vector>
#include <utility>

#include "config.h"
#include "creature.h"
#include "game_context.h"

GameContext::GameContext():
    config(new Config()),
    display(new Display()),
    dungeon(new Dungeon()),
    queue(al_create_event_queue()),
    clock_time(0)
{
    al_register_event_source(
        queue, al_get_keyboard_event_source()
    );
    al_register_event_source(
        queue, al_get_display_event_source(display->display)
    );

    int free_pos_x;
    int free_pos_y;
    while (1)
    {
        int rand_x = rand() % dungeon->get_width();
        int rand_y = rand() % dungeon->get_height();

        if (dungeon->entity_at_index(rand_x, rand_y).is_passable())
        {
            free_pos_x = rand_x;
            free_pos_y = rand_y;

            break;
        }
    }

    creatures.push_back(
        new Creature(
            '@', "Glorzak", true, true, true, free_pos_x, free_pos_y, 50, 10
        )
    );

    auto gobber = config->load_creature("gobber");
    gobber->set_position(free_pos_x + 2, free_pos_y - 2);

    creatures.push_back(gobber);
}

Creature* GameContext::creature_at_index(int x, int y)
{
    for (auto it = creatures.cbegin(); it != creatures.cend(); it++)
    {
        if ((*it)->get_x() == x && (*it)->get_y() == y)
        {
            return *it;
        }
    }

    return NULL;
}

void GameContext::kill_creature_at_index(int x, int y) {
    for (auto it = creatures.begin(); it != creatures.end(); it++) {
        if ((*it)->get_x() == x && (*it)->get_y() == y) {
            creatures.erase(it);

            break;
        }
    }
}

void GameContext::conduct_melee_attack(Creature* source, Creature* target) {
    auto damage = source->get_strength();

    target->take_damage(damage);

    std::ostringstream stream;

    stream << "You hit the " << target->get_name() << " with your fists.";

    event_messages.push_back(stream.str());
    // Clear the sstream
    stream.str(std::string());

    stream << "The " << target->get_name();

    if (target->get_health() <= 0) {
        kill_creature_at_index(target->get_x(), target->get_y());

        stream << " dies.";
    }
    else if (target->get_health() >= target->get_max_health() * 0.8) {
        stream << " is lightly wounded.";
    }
    else if (target->get_health() >= target->get_max_health() * 0.5) {
        stream << " is moderately wounded.";
    }
    else if (target->get_health() >= target->get_max_health() * 0.2) {
        stream << " is heavily wounded.";
    }
    else {
        stream << " is gravely wounded.";
    }

    event_messages.push_back(stream.str());
}

void GameContext::process_movement(int x, int y, Creature* creature)
{
    if (auto target = creature_at_index(x, y))
    {
        // Swap positions with the friendly creature
        if (target->is_friendly())
        {
            target->set_position(creature->get_x(), creature->get_y());
            creature->set_position(x, y);
        }
        else
        {
            conduct_melee_attack(creature, target);
        }
    }
    else if (dungeon->entity_at_index(x, y).is_passable())
    {
        creature->set_position(x, y);
    }
}

bool GameContext::take_input(Creature* controllable)
{
    ALLEGRO_EVENT event;

    do {
        al_wait_for_event(queue, &event);
    } while(
        event.type != ALLEGRO_EVENT_DISPLAY_CLOSE &&
        event.type != ALLEGRO_EVENT_KEY_CHAR
    );

    if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
    {
        return true;
    }

    int cur_x = controllable->get_x();
    int cur_y = controllable->get_y();

    if (event.type == ALLEGRO_EVENT_KEY_CHAR)
    {
        switch (event.keyboard.keycode)
        {
        case ALLEGRO_KEY_LEFT:
            process_movement(cur_x - 1, cur_y, controllable);
            break;
        case ALLEGRO_KEY_RIGHT:
            process_movement(cur_x + 1, cur_y, controllable);
            break;
        case ALLEGRO_KEY_UP:
            process_movement(cur_x, cur_y - 1, controllable);
            break;
        case ALLEGRO_KEY_DOWN:
            process_movement(cur_x, cur_y + 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_1:
            process_movement(cur_x - 1, cur_y + 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_2:
            process_movement(cur_x, cur_y + 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_3:
            process_movement(cur_x + 1, cur_y + 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_4:
            process_movement(cur_x - 1, cur_y, controllable);
            break;
        case ALLEGRO_KEY_PAD_6:
            process_movement(cur_x + 1, cur_y, controllable);
            break;
        case ALLEGRO_KEY_PAD_7:
            process_movement(cur_x - 1, cur_y - 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_8:
            process_movement(cur_x, cur_y - 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_9:
            process_movement(cur_x + 1, cur_y - 1, controllable);
            break;
        }

        if (
            event.keyboard.keycode == ALLEGRO_KEY_ESCAPE ||
            event.keyboard.unichar == 'q'
        ) {
            return true;
        }
    }

    return false;
}

void GameContext::ai_turn(Creature* player, Creature* creature) {

    //check to see if player is nearby
    if (creature_nearby(creature, player, 5)) {
        if (creature_nearby(creature, player, 1)) {
            conduct_melee_attack(creature, player);
        }
        //move towards player
        move_towards(creature, player);
    }
    else {
        wander(creature);
    }
}

//Check to see if Creature A is within thresh of Creature B
bool GameContext::creature_nearby(Creature* creat_a, Creature* creat_b, int thresh) {
    if (
        creat_a->get_x() + thresh >= creat_b->get_x() &&
        creat_a->get_x() - thresh <= creat_b->get_x() &&
        creat_a->get_y() + thresh >= creat_b->get_y() &&
        creat_a->get_y() - thresh <= creat_b->get_y()
    ) {
        //printf("NEAR\n");
        return true;
    }
    return false;
}

bool GameContext::index_is_unoccupied(int x, int y) {
    if (
        dungeon->entity_at_index(x, y).is_passable() &&
        creature_at_index(x, y) == NULL
    ) {
        return true;
    }
    return false;
}

//Check to see what diretion to go
void GameContext::move_towards(Creature* creat_a, Creature* creat_b) {
    if (creat_a->get_x() > creat_b->get_x()) {
        if (index_is_unoccupied(creat_a->get_x() - 1, creat_a->get_y())) {
            creat_a->set_position(creat_a->get_x() - 1, creat_a->get_y());
        }
    }
    else if (creat_a->get_x() < creat_b->get_x()) {
        if (index_is_unoccupied(creat_a->get_x() + 1, creat_a->get_y())) {
            creat_a->set_position(creat_a->get_x() + 1, creat_a->get_y());
        }
    }
    else if (creat_a->get_y() > creat_b->get_y()) {
        if (index_is_unoccupied(creat_a->get_x(), creat_a->get_y() - 1)) {
            creat_a->set_position(creat_a->get_x(), creat_a->get_y() - 1);
        }
    }
    else if (creat_a->get_y() < creat_b->get_y()) {
        if (index_is_unoccupied(creat_a->get_x(), creat_a->get_y() + 1)) {
            creat_a->set_position(creat_a->get_x(), creat_a->get_y() + 1);
        }
    }
}

/*
    have a Creature wander into a new space

    1 2 3
    4 X 5   Pick one of the numbered spots to move into
    6 7 8
*/
void GameContext::wander(Creature* creature) {

    std::vector<std::pair<int,int>> spaces;

    int x = -1;
    while (x < 2) {
        int y = -1;
        while (y < 2) {
            if (index_is_unoccupied(creature->get_x() + x, creature->get_y() + y)) {
                spaces.push_back(std::make_pair(creature->get_x() + x, creature->get_y() + y));
            }
            y++;
        }
        x++;
    }

    if (!spaces.empty()) {
        int r = rand() % spaces.size();
        creature->set_position(spaces[r].first, spaces[r].second);
    }
}

void GameContext::game_loop()
{
    Creature* player;
    if (!(player = get_player()))
    {
        return;
    }

    while (1)
    {
        for (auto it = creatures.begin(); it != creatures.end(); it++)
        {
            if (!(*it)->is_turn(clock_time))
            {
                continue;
            }

            if (!(*it)->is_controllable())
            {
                ai_turn(player, *it);
                continue;
            }

            display->draw_dungeon(
                (*it)->get_x(), (*it)->get_y(),
                dungeon, creatures.cbegin(), creatures.cend()
            );

            display->draw_event_messages(event_messages);

            display->draw_borders();

            display->update_screen();

            bool exit_request = take_input(*it);

            if (exit_request)
            {
                goto BREAK_GAME_LOOP;
            }
        }

        clock_time += 5;
    }

BREAK_GAME_LOOP:

    return;
}

Creature* GameContext::get_player()
{
    for (auto it = creatures.begin(); it != creatures.end(); it++)
    {
        if ((*it)->is_player())
        {
            return *it;
        }
    }

    return NULL;
}

GameContext::~GameContext()
{
    delete display;
    delete dungeon;

    for (auto it = creatures.begin(); it != creatures.end(); it++)
    {
        delete *it;
    }

    al_destroy_event_queue(queue);
}
<commit_msg>Fixed problem where enemy can both attack and move in the same turn.<commit_after>#include <allegro5/allegro.h>

#include <sstream>
#include <vector>
#include <utility>

#include "config.h"
#include "creature.h"
#include "game_context.h"

GameContext::GameContext():
    config(new Config()),
    display(new Display()),
    dungeon(new Dungeon()),
    queue(al_create_event_queue()),
    clock_time(0)
{
    al_register_event_source(
        queue, al_get_keyboard_event_source()
    );
    al_register_event_source(
        queue, al_get_display_event_source(display->display)
    );

    int free_pos_x;
    int free_pos_y;
    while (1)
    {
        int rand_x = rand() % dungeon->get_width();
        int rand_y = rand() % dungeon->get_height();

        if (dungeon->entity_at_index(rand_x, rand_y).is_passable())
        {
            free_pos_x = rand_x;
            free_pos_y = rand_y;

            break;
        }
    }

    creatures.push_back(
        new Creature(
            '@', "Glorzak", true, true, true, free_pos_x, free_pos_y, 50, 10
        )
    );

    auto gobber = config->load_creature("gobber");
    gobber->set_position(free_pos_x + 2, free_pos_y - 2);

    creatures.push_back(gobber);
}

Creature* GameContext::creature_at_index(int x, int y)
{
    for (auto it = creatures.cbegin(); it != creatures.cend(); it++)
    {
        if ((*it)->get_x() == x && (*it)->get_y() == y)
        {
            return *it;
        }
    }

    return NULL;
}

void GameContext::kill_creature_at_index(int x, int y) {
    for (auto it = creatures.begin(); it != creatures.end(); it++) {
        if ((*it)->get_x() == x && (*it)->get_y() == y) {
            creatures.erase(it);

            break;
        }
    }
}

void GameContext::conduct_melee_attack(Creature* source, Creature* target) {
    auto damage = source->get_strength();

    target->take_damage(damage);

    std::ostringstream stream;

    stream << "You hit the " << target->get_name() << " with your fists.";

    event_messages.push_back(stream.str());
    // Clear the sstream
    stream.str(std::string());

    stream << "The " << target->get_name();

    if (target->get_health() <= 0) {
        kill_creature_at_index(target->get_x(), target->get_y());

        stream << " dies.";
    }
    else if (target->get_health() >= target->get_max_health() * 0.8) {
        stream << " is lightly wounded.";
    }
    else if (target->get_health() >= target->get_max_health() * 0.5) {
        stream << " is moderately wounded.";
    }
    else if (target->get_health() >= target->get_max_health() * 0.2) {
        stream << " is heavily wounded.";
    }
    else {
        stream << " is gravely wounded.";
    }

    event_messages.push_back(stream.str());
}

void GameContext::process_movement(int x, int y, Creature* creature)
{
    if (auto target = creature_at_index(x, y))
    {
        // Swap positions with the friendly creature
        if (target->is_friendly())
        {
            target->set_position(creature->get_x(), creature->get_y());
            creature->set_position(x, y);
        }
        else
        {
            conduct_melee_attack(creature, target);
        }
    }
    else if (dungeon->entity_at_index(x, y).is_passable())
    {
        creature->set_position(x, y);
    }
}

bool GameContext::take_input(Creature* controllable)
{
    ALLEGRO_EVENT event;

    do {
        al_wait_for_event(queue, &event);
    } while(
        event.type != ALLEGRO_EVENT_DISPLAY_CLOSE &&
        event.type != ALLEGRO_EVENT_KEY_CHAR
    );

    if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
    {
        return true;
    }

    int cur_x = controllable->get_x();
    int cur_y = controllable->get_y();

    if (event.type == ALLEGRO_EVENT_KEY_CHAR)
    {
        switch (event.keyboard.keycode)
        {
        case ALLEGRO_KEY_LEFT:
            process_movement(cur_x - 1, cur_y, controllable);
            break;
        case ALLEGRO_KEY_RIGHT:
            process_movement(cur_x + 1, cur_y, controllable);
            break;
        case ALLEGRO_KEY_UP:
            process_movement(cur_x, cur_y - 1, controllable);
            break;
        case ALLEGRO_KEY_DOWN:
            process_movement(cur_x, cur_y + 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_1:
            process_movement(cur_x - 1, cur_y + 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_2:
            process_movement(cur_x, cur_y + 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_3:
            process_movement(cur_x + 1, cur_y + 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_4:
            process_movement(cur_x - 1, cur_y, controllable);
            break;
        case ALLEGRO_KEY_PAD_6:
            process_movement(cur_x + 1, cur_y, controllable);
            break;
        case ALLEGRO_KEY_PAD_7:
            process_movement(cur_x - 1, cur_y - 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_8:
            process_movement(cur_x, cur_y - 1, controllable);
            break;
        case ALLEGRO_KEY_PAD_9:
            process_movement(cur_x + 1, cur_y - 1, controllable);
            break;
        }

        if (
            event.keyboard.keycode == ALLEGRO_KEY_ESCAPE ||
            event.keyboard.unichar == 'q'
        ) {
            return true;
        }
    }

    return false;
}

void GameContext::ai_turn(Creature* player, Creature* creature) {

    //check to see if player is nearby
    if (creature_nearby(creature, player, 5)) {
        if (creature_nearby(creature, player, 1)) {
            conduct_melee_attack(creature, player);
        }
        else {
            //move towards player
            move_towards(creature, player);
        }
    }
    else {
        wander(creature);
    }
}

//Check to see if Creature A is within thresh of Creature B
bool GameContext::creature_nearby(Creature* creat_a, Creature* creat_b, int thresh) {
    if (
        creat_a->get_x() + thresh >= creat_b->get_x() &&
        creat_a->get_x() - thresh <= creat_b->get_x() &&
        creat_a->get_y() + thresh >= creat_b->get_y() &&
        creat_a->get_y() - thresh <= creat_b->get_y()
    ) {
        //printf("NEAR\n");
        return true;
    }
    return false;
}

bool GameContext::index_is_unoccupied(int x, int y) {
    if (
        dungeon->entity_at_index(x, y).is_passable() &&
        creature_at_index(x, y) == NULL
    ) {
        return true;
    }
    return false;
}

//Check to see what diretion to go
void GameContext::move_towards(Creature* creat_a, Creature* creat_b) {
    if (creat_a->get_x() > creat_b->get_x()) {
        if (index_is_unoccupied(creat_a->get_x() - 1, creat_a->get_y())) {
            creat_a->set_position(creat_a->get_x() - 1, creat_a->get_y());
        }
    }
    else if (creat_a->get_x() < creat_b->get_x()) {
        if (index_is_unoccupied(creat_a->get_x() + 1, creat_a->get_y())) {
            creat_a->set_position(creat_a->get_x() + 1, creat_a->get_y());
        }
    }
    else if (creat_a->get_y() > creat_b->get_y()) {
        if (index_is_unoccupied(creat_a->get_x(), creat_a->get_y() - 1)) {
            creat_a->set_position(creat_a->get_x(), creat_a->get_y() - 1);
        }
    }
    else if (creat_a->get_y() < creat_b->get_y()) {
        if (index_is_unoccupied(creat_a->get_x(), creat_a->get_y() + 1)) {
            creat_a->set_position(creat_a->get_x(), creat_a->get_y() + 1);
        }
    }
}

/*
    have a Creature wander into a new space

    1 2 3
    4 X 5   Pick one of the numbered spots to move into
    6 7 8
*/
void GameContext::wander(Creature* creature) {

    std::vector<std::pair<int,int>> spaces;

    int x = -1;
    while (x < 2) {
        int y = -1;
        while (y < 2) {
            if (index_is_unoccupied(creature->get_x() + x, creature->get_y() + y)) {
                spaces.push_back(std::make_pair(creature->get_x() + x, creature->get_y() + y));
            }
            y++;
        }
        x++;
    }

    if (!spaces.empty()) {
        int r = rand() % spaces.size();
        creature->set_position(spaces[r].first, spaces[r].second);
    }
}

void GameContext::game_loop()
{
    Creature* player;
    if (!(player = get_player()))
    {
        return;
    }

    while (1)
    {
        for (auto it = creatures.begin(); it != creatures.end(); it++)
        {
            if (!(*it)->is_turn(clock_time))
            {
                continue;
            }

            if (!(*it)->is_controllable())
            {
                ai_turn(player, *it);
                continue;
            }

            display->draw_dungeon(
                (*it)->get_x(), (*it)->get_y(),
                dungeon, creatures.cbegin(), creatures.cend()
            );

            display->draw_event_messages(event_messages);

            display->draw_borders();

            display->update_screen();

            bool exit_request = take_input(*it);

            if (exit_request)
            {
                goto BREAK_GAME_LOOP;
            }
        }

        clock_time += 5;
    }

BREAK_GAME_LOOP:

    return;
}

Creature* GameContext::get_player()
{
    for (auto it = creatures.begin(); it != creatures.end(); it++)
    {
        if ((*it)->is_player())
        {
            return *it;
        }
    }

    return NULL;
}

GameContext::~GameContext()
{
    delete display;
    delete dungeon;

    for (auto it = creatures.begin(); it != creatures.end(); it++)
    {
        delete *it;
    }

    al_destroy_event_queue(queue);
}
<|endoftext|>


<commit_before>#include "direct.hxx"
#include "istream/istream.hxx"
#include "istream/istream_byte.hxx"
#include "istream/istream_cat.hxx"
#include "istream/istream_fail.hxx"
#include "istream/istream_four.hxx"
#include "istream/istream_head.hxx"
#include "istream/istream_hold.hxx"
#include "istream/istream_inject.hxx"
#include "istream/istream_later.hxx"

#include <glib.h>
#include <event.h>

#include <stdio.h>
#ifdef EXPECTED_RESULT
#include <string.h>
#endif

enum {
#ifdef NO_BLOCKING
    enable_blocking = false,
#else
    enable_blocking = true,
#endif
};

static inline GQuark
test_quark(void)
{
    return g_quark_from_static_string("test");
}

#ifndef FILTER_CLEANUP
static void
cleanup(void)
{
}
#endif

struct Context {
    bool half = false;
    bool got_data;
    bool eof = false;
#ifdef EXPECTED_RESULT
    bool record = false;
    char buffer[sizeof(EXPECTED_RESULT) * 2];
    size_t buffer_length = 0;
#endif
    struct istream *abort_istream = nullptr;
    int abort_after = 0;

    int block_after = -1;

    bool block_byte = false, block_byte_state = false;
};

/*
 * istream handler
 *
 */

static size_t
my_istream_data(const void *data, size_t length, void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    (void)data;

    ctx.got_data = true;

    if (ctx.block_byte) {
        ctx.block_byte_state = !ctx.block_byte_state;
        if (ctx.block_byte_state)
            return 0;
    }

    if (ctx.abort_istream != nullptr && ctx.abort_after-- == 0) {
        GError *error = g_error_new_literal(test_quark(), 0, "abort_istream");
        istream_inject_fault(ctx.abort_istream, error);
        ctx.abort_istream = nullptr;
        return 0;
    }

    if (ctx.half && length > 8)
        length = (length + 1) / 2;

    if (ctx.block_after >= 0) {
        --ctx.block_after;
        if (ctx.block_after == -1)
            /* block once */
            return 0;
    }

#ifdef EXPECTED_RESULT
    if (ctx.record) {
#ifdef __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstring-plus-int"
#endif

        assert(ctx.buffer_length + length < sizeof(ctx.buffer));
        assert(memcmp(EXPECTED_RESULT + ctx.buffer_length, data, length) == 0);

#ifdef __clang__
#pragma GCC diagnostic pop
#endif

        if (ctx.buffer_length + length < sizeof(ctx.buffer))
            memcpy(ctx.buffer + ctx.buffer_length, data, length);
        ctx.buffer_length += length;
    }
#endif

    return length;
}

static ssize_t
my_istream_direct(gcc_unused FdType type, int fd,
                  size_t max_length, void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    (void)fd;

    ctx.got_data = true;

    if (ctx.abort_istream != nullptr) {
        GError *error = g_error_new_literal(test_quark(), 0, "abort_istream");
        istream_inject_fault(ctx.abort_istream, error);
        ctx.abort_istream = nullptr;
        return 0;
    }

    return max_length;
}

static void
my_istream_eof(void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    ctx.eof = true;
}

static void
my_istream_abort(GError *error, void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    g_error_free(error);

#ifdef EXPECTED_RESULT
    assert(!ctx.record);
#endif

    ctx.eof = true;
}

static const struct istream_handler my_istream_handler = {
    .data = my_istream_data,
    .direct = my_istream_direct,
    .eof = my_istream_eof,
    .abort = my_istream_abort,
};

/*
 * utils
 *
 */

static int
istream_read_event(struct istream *istream)
{
    istream_read(istream);
    return event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
}

static inline void
istream_read_expect(Context &ctx, struct istream *istream)
{
    assert(!ctx.eof);

    ctx.got_data = false;

    const auto ret = istream_read_event(istream);
    assert(ctx.eof || ctx.got_data || ret == 0);

    /* give istream_later another chance to breathe */
    event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
}

static void
run_istream_ctx(Context &ctx, struct pool *pool, struct istream *istream)
{
    ctx.eof = false;

    gcc_unused off_t a1 = istream_available(istream, false);
    gcc_unused off_t a2 = istream_available(istream, true);

    istream_handler_set(istream, &my_istream_handler, &ctx, 0);

    pool_unref(pool);
    pool_commit();

#ifndef NO_GOT_DATA_ASSERT
    while (!ctx.eof)
        istream_read_expect(ctx, istream);
#else
    for (int i = 0; i < 1000 && !ctx.eof; ++i)
           istream_read_event(istream);
#endif

#ifdef EXPECTED_RESULT
    if (ctx.record) {
        assert(ctx.buffer_length == sizeof(EXPECTED_RESULT) - 1);
        assert(memcmp(ctx.buffer, EXPECTED_RESULT, ctx.buffer_length) == 0);
    }
#endif

    cleanup();
    pool_commit();
}

static void
run_istream_block(struct pool *pool, struct istream *istream,
                  gcc_unused bool record,
                  int block_after)
{
    Context ctx;
    ctx.block_after = block_after;
#ifdef EXPECTED_RESULT
    ctx.record = record;
#endif

    run_istream_ctx(ctx, pool, istream);
}

static void
run_istream(struct pool *pool, struct istream *istream, bool record)
{
    run_istream_block(pool, istream, record, -1);
}

/*
 * tests
 *
 */

/** normal run */
static void
test_normal(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_normal", 8192);

    auto *istream = create_test(pool, create_input(pool));
    assert(istream != nullptr);
    assert(!istream_has_handler(istream));

    run_istream(pool, istream, true);
}

/** block once after n data() invocations */
static void
test_block(struct pool *parent_pool)
{
    for (int n = 0; n < 8; ++n) {
        struct istream *istream;

        auto *pool = pool_new_linear(parent_pool, "test_block", 8192);

        istream = create_test(pool, create_input(pool));
        assert(istream != nullptr);
        assert(!istream_has_handler(istream));

        run_istream_block(pool, istream, true, n);
    }
}

/** test with istream_byte */
static void
test_byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_byte", 8192);

    auto *istream =
        create_test(pool, istream_byte_new(*pool, *create_input(pool)));
    run_istream(pool, istream, true);
}

/** block and consume one byte at a time */
static void
test_block_byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_byte", 8192);

    auto *istream =
        create_test(pool, istream_byte_new(*pool, *create_input(pool)));

    Context ctx;
    ctx.block_byte = true;
#ifdef EXPECTED_RESULT
    ctx.record = true;
#endif

    run_istream_ctx(ctx, pool, istream);
}

/** accept only half of the data */
static void
test_half(struct pool *pool)
{
    Context ctx;
    ctx.half = true;
#ifdef EXPECTED_RESULT
    ctx.record = true;
#endif

    pool = pool_new_linear(pool, "test_half", 8192);

    run_istream_ctx(ctx, pool, create_test(pool, create_input(pool)));
}

/** input fails */
static void
test_fail(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_fail", 8192);

    GError *error = g_error_new_literal(test_quark(), 0, "test_fail");
    auto *istream = create_test(pool, istream_fail_new(pool, error));
    run_istream(pool, istream, false);
}

/** input fails after the first byte */
static void
test_fail_1byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_fail_1byte", 8192);

    GError *error = g_error_new_literal(test_quark(), 0, "test_fail");
    auto *istream =
        create_test(pool,
                    istream_cat_new(pool,
                                    istream_head_new(pool, create_input(pool),
                                                     1, false),
                                    istream_fail_new(pool, error),
                                    nullptr));
    run_istream(pool, istream, false);
}

/** abort without handler */
static void
test_abort_without_handler(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_abort_without_handler", 8192);

    auto *istream = create_test(pool, create_input(pool));
    pool_unref(pool);
    pool_commit();

    istream_close_unused(istream);

    cleanup();
    pool_commit();
}

#ifndef NO_ABORT_ISTREAM

/** abort in handler */
static void
test_abort_in_handler(struct pool *pool)
{
    Context ctx;
    ctx.block_after = -1;

    pool = pool_new_linear(pool, "test_abort_in_handler", 8192);

    ctx.abort_istream = istream_inject_new(pool, create_input(pool));
    auto *istream = create_test(pool, ctx.abort_istream);
    istream_handler_set(istream, &my_istream_handler, &ctx, 0);
    pool_unref(pool);
    pool_commit();

    while (!ctx.eof) {
        istream_read_expect(ctx, istream);
        event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
    }

    assert(ctx.abort_istream == nullptr);

    cleanup();
    pool_commit();
}

/** abort in handler, with some data consumed */
static void
test_abort_in_handler_half(struct pool *pool)
{
    Context ctx;
    ctx.half = true;
    ctx.abort_after = 2;

    pool = pool_new_linear(pool, "test_abort_in_handler_half", 8192);

    ctx.abort_istream = istream_inject_new(pool, istream_four_new(pool, create_input(pool)));
    auto *istream = create_test(pool, istream_byte_new(*pool, *ctx.abort_istream));
    istream_handler_set(istream, &my_istream_handler, &ctx, 0);
    pool_unref(pool);
    pool_commit();

    while (!ctx.eof) {
        istream_read_expect(ctx, istream);
        event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
    }

    assert(ctx.abort_istream == nullptr || ctx.abort_after >= 0);

    cleanup();
    pool_commit();
}

#endif

/** abort after 1 byte of output */
static void
test_abort_1byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_abort_1byte", 8192);

    auto *istream = istream_head_new(pool,
                                     create_test(pool,
                                                 create_input(pool)),
                                     1, false);
    run_istream(pool, istream, false);
}

/** test with istream_later filter */
static void
test_later(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_later", 8192);

    auto *istream =
        create_test(pool, istream_later_new(pool, create_input(pool)));
    run_istream(pool, istream, true);
}

#ifdef EXPECTED_RESULT
/** test with large input and blocking handler */
static void
test_big_hold(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_big_hold", 8192);

    struct istream *istream = create_input(pool);
    for (unsigned i = 0; i < 1024; ++i)
        istream = istream_cat_new(pool, istream, create_input(pool), nullptr);

    istream = create_test(pool, istream);
    struct istream *hold = istream_hold_new(pool, istream);

    istream_read(istream);

    istream_close_unused(hold);

    pool_unref(pool);
}
#endif

/*
 * main
 *
 */

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

    direct_global_init();
    auto *const event_base = event_init();

    auto *const root_pool = pool_new_libc(nullptr, "root");

    /* run test suite */

    test_normal(root_pool);
    if (enable_blocking) {
        test_block(root_pool);
        test_byte(root_pool);
        test_block_byte(root_pool);
    }
    test_half(root_pool);
    test_fail(root_pool);
    test_fail_1byte(root_pool);
    test_abort_without_handler(root_pool);
#ifndef NO_ABORT_ISTREAM
    test_abort_in_handler(root_pool);
    if (enable_blocking)
        test_abort_in_handler_half(root_pool);
#endif
    test_abort_1byte(root_pool);
    test_later(root_pool);

#ifdef EXPECTED_RESULT
    test_big_hold(root_pool);
#endif

#ifdef CUSTOM_TEST
    test_custom(root_pool);
#endif

    /* cleanup */

    pool_unref(root_pool);
    pool_commit();

    pool_recycler_clear();

    event_base_free(event_base);
    direct_global_deinit();
}
<commit_msg>test/t_istream_filter: use class IstreamPointer<commit_after>#include "direct.hxx"
#include "istream/istream.hxx"
#include "istream/istream_byte.hxx"
#include "istream/istream_cat.hxx"
#include "istream/istream_fail.hxx"
#include "istream/istream_four.hxx"
#include "istream/istream_head.hxx"
#include "istream/istream_hold.hxx"
#include "istream/istream_inject.hxx"
#include "istream/istream_later.hxx"
#include "istream/istream_pointer.hxx"

#include <glib.h>
#include <event.h>

#include <stdio.h>
#ifdef EXPECTED_RESULT
#include <string.h>
#endif

enum {
#ifdef NO_BLOCKING
    enable_blocking = false,
#else
    enable_blocking = true,
#endif
};

static inline GQuark
test_quark(void)
{
    return g_quark_from_static_string("test");
}

#ifndef FILTER_CLEANUP
static void
cleanup(void)
{
}
#endif

struct Context {
    IstreamPointer input;

    bool half = false;
    bool got_data;
    bool eof = false;
#ifdef EXPECTED_RESULT
    bool record = false;
    char buffer[sizeof(EXPECTED_RESULT) * 2];
    size_t buffer_length = 0;
#endif
    struct istream *abort_istream = nullptr;
    int abort_after = 0;

    int block_after = -1;

    bool block_byte = false, block_byte_state = false;

    explicit Context(struct istream &_input);
};

/*
 * istream handler
 *
 */

static size_t
my_istream_data(const void *data, size_t length, void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    (void)data;

    ctx.got_data = true;

    if (ctx.block_byte) {
        ctx.block_byte_state = !ctx.block_byte_state;
        if (ctx.block_byte_state)
            return 0;
    }

    if (ctx.abort_istream != nullptr && ctx.abort_after-- == 0) {
        GError *error = g_error_new_literal(test_quark(), 0, "abort_istream");
        istream_inject_fault(ctx.abort_istream, error);
        ctx.abort_istream = nullptr;
        return 0;
    }

    if (ctx.half && length > 8)
        length = (length + 1) / 2;

    if (ctx.block_after >= 0) {
        --ctx.block_after;
        if (ctx.block_after == -1)
            /* block once */
            return 0;
    }

#ifdef EXPECTED_RESULT
    if (ctx.record) {
#ifdef __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstring-plus-int"
#endif

        assert(ctx.buffer_length + length < sizeof(ctx.buffer));
        assert(memcmp(EXPECTED_RESULT + ctx.buffer_length, data, length) == 0);

#ifdef __clang__
#pragma GCC diagnostic pop
#endif

        if (ctx.buffer_length + length < sizeof(ctx.buffer))
            memcpy(ctx.buffer + ctx.buffer_length, data, length);
        ctx.buffer_length += length;
    }
#endif

    return length;
}

static ssize_t
my_istream_direct(gcc_unused FdType type, int fd,
                  size_t max_length, void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    (void)fd;

    ctx.got_data = true;

    if (ctx.abort_istream != nullptr) {
        GError *error = g_error_new_literal(test_quark(), 0, "abort_istream");
        istream_inject_fault(ctx.abort_istream, error);
        ctx.abort_istream = nullptr;
        return 0;
    }

    return max_length;
}

static void
my_istream_eof(void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    ctx.eof = true;
}

static void
my_istream_abort(GError *error, void *_ctx)
{
    auto &ctx = *(Context *)_ctx;

    g_error_free(error);

#ifdef EXPECTED_RESULT
    assert(!ctx.record);
#endif

    ctx.eof = true;
}

static const struct istream_handler my_istream_handler = {
    .data = my_istream_data,
    .direct = my_istream_direct,
    .eof = my_istream_eof,
    .abort = my_istream_abort,
};

Context::Context(struct istream &_input)
    :input(_input, my_istream_handler, this) {}

/*
 * utils
 *
 */

static int
istream_read_event(IstreamPointer &istream)
{
    istream.Read();
    return event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
}

static inline void
istream_read_expect(Context &ctx)
{
    assert(!ctx.eof);

    ctx.got_data = false;

    const auto ret = istream_read_event(ctx.input);
    assert(ctx.eof || ctx.got_data || ret == 0);

    /* give istream_later another chance to breathe */
    event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
}

static void
run_istream_ctx(Context &ctx, struct pool *pool)
{
    ctx.eof = false;

    gcc_unused off_t a1 = ctx.input.GetAvailable(false);
    gcc_unused off_t a2 = ctx.input.GetAvailable(true);

    pool_unref(pool);
    pool_commit();

#ifndef NO_GOT_DATA_ASSERT
    while (!ctx.eof)
        istream_read_expect(ctx);
#else
    for (int i = 0; i < 1000 && !ctx.eof; ++i)
           istream_read_event(ctx.input);
#endif

#ifdef EXPECTED_RESULT
    if (ctx.record) {
        assert(ctx.buffer_length == sizeof(EXPECTED_RESULT) - 1);
        assert(memcmp(ctx.buffer, EXPECTED_RESULT, ctx.buffer_length) == 0);
    }
#endif

    cleanup();
    pool_commit();
}

static void
run_istream_block(struct pool *pool, struct istream *istream,
                  gcc_unused bool record,
                  int block_after)
{
    Context ctx(*istream);
    ctx.block_after = block_after;
#ifdef EXPECTED_RESULT
    ctx.record = record;
#endif

    run_istream_ctx(ctx, pool);
}

static void
run_istream(struct pool *pool, struct istream *istream, bool record)
{
    run_istream_block(pool, istream, record, -1);
}

/*
 * tests
 *
 */

/** normal run */
static void
test_normal(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_normal", 8192);

    auto *istream = create_test(pool, create_input(pool));
    assert(istream != nullptr);
    assert(!istream_has_handler(istream));

    run_istream(pool, istream, true);
}

/** block once after n data() invocations */
static void
test_block(struct pool *parent_pool)
{
    for (int n = 0; n < 8; ++n) {
        struct istream *istream;

        auto *pool = pool_new_linear(parent_pool, "test_block", 8192);

        istream = create_test(pool, create_input(pool));
        assert(istream != nullptr);
        assert(!istream_has_handler(istream));

        run_istream_block(pool, istream, true, n);
    }
}

/** test with istream_byte */
static void
test_byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_byte", 8192);

    auto *istream =
        create_test(pool, istream_byte_new(*pool, *create_input(pool)));
    run_istream(pool, istream, true);
}

/** block and consume one byte at a time */
static void
test_block_byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_byte", 8192);

    Context ctx(*create_test(pool,
                             istream_byte_new(*pool, *create_input(pool))));
    ctx.block_byte = true;
#ifdef EXPECTED_RESULT
    ctx.record = true;
#endif

    run_istream_ctx(ctx, pool);
}

/** accept only half of the data */
static void
test_half(struct pool *pool)
{
    Context ctx(*create_test(pool, create_input(pool)));
    ctx.half = true;
#ifdef EXPECTED_RESULT
    ctx.record = true;
#endif

    pool = pool_new_linear(pool, "test_half", 8192);

    run_istream_ctx(ctx, pool);
}

/** input fails */
static void
test_fail(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_fail", 8192);

    GError *error = g_error_new_literal(test_quark(), 0, "test_fail");
    auto *istream = create_test(pool, istream_fail_new(pool, error));
    run_istream(pool, istream, false);
}

/** input fails after the first byte */
static void
test_fail_1byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_fail_1byte", 8192);

    GError *error = g_error_new_literal(test_quark(), 0, "test_fail");
    auto *istream =
        create_test(pool,
                    istream_cat_new(pool,
                                    istream_head_new(pool, create_input(pool),
                                                     1, false),
                                    istream_fail_new(pool, error),
                                    nullptr));
    run_istream(pool, istream, false);
}

/** abort without handler */
static void
test_abort_without_handler(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_abort_without_handler", 8192);

    auto *istream = create_test(pool, create_input(pool));
    pool_unref(pool);
    pool_commit();

    istream_close_unused(istream);

    cleanup();
    pool_commit();
}

#ifndef NO_ABORT_ISTREAM

/** abort in handler */
static void
test_abort_in_handler(struct pool *pool)
{

    pool = pool_new_linear(pool, "test_abort_in_handler", 8192);

    auto *abort_istream = istream_inject_new(pool, create_input(pool));
    auto *istream = create_test(pool, abort_istream);
    pool_unref(pool);
    pool_commit();

    Context ctx(*istream);
    ctx.block_after = -1;
    ctx.abort_istream = abort_istream;

    while (!ctx.eof) {
        istream_read_expect(ctx);
        event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
    }

    assert(ctx.abort_istream == nullptr);

    cleanup();
    pool_commit();
}

/** abort in handler, with some data consumed */
static void
test_abort_in_handler_half(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_abort_in_handler_half", 8192);

    auto *abort_istream =
        istream_inject_new(pool, istream_four_new(pool, create_input(pool)));
    auto *istream = create_test(pool, istream_byte_new(*pool, *abort_istream));
    pool_unref(pool);
    pool_commit();

    Context ctx(*istream);
    ctx.half = true;
    ctx.abort_after = 2;
    ctx.abort_istream = abort_istream;

    while (!ctx.eof) {
        istream_read_expect(ctx);
        event_loop(EVLOOP_ONCE|EVLOOP_NONBLOCK);
    }

    assert(ctx.abort_istream == nullptr || ctx.abort_after >= 0);

    cleanup();
    pool_commit();
}

#endif

/** abort after 1 byte of output */
static void
test_abort_1byte(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_abort_1byte", 8192);

    auto *istream = istream_head_new(pool,
                                     create_test(pool,
                                                 create_input(pool)),
                                     1, false);
    run_istream(pool, istream, false);
}

/** test with istream_later filter */
static void
test_later(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_later", 8192);

    auto *istream =
        create_test(pool, istream_later_new(pool, create_input(pool)));
    run_istream(pool, istream, true);
}

#ifdef EXPECTED_RESULT
/** test with large input and blocking handler */
static void
test_big_hold(struct pool *pool)
{
    pool = pool_new_linear(pool, "test_big_hold", 8192);

    struct istream *istream = create_input(pool);
    for (unsigned i = 0; i < 1024; ++i)
        istream = istream_cat_new(pool, istream, create_input(pool), nullptr);

    istream = create_test(pool, istream);
    struct istream *hold = istream_hold_new(pool, istream);

    istream_read(istream);

    istream_close_unused(hold);

    pool_unref(pool);
}
#endif

/*
 * main
 *
 */

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

    direct_global_init();
    auto *const event_base = event_init();

    auto *const root_pool = pool_new_libc(nullptr, "root");

    /* run test suite */

    test_normal(root_pool);
    if (enable_blocking) {
        test_block(root_pool);
        test_byte(root_pool);
        test_block_byte(root_pool);
    }
    test_half(root_pool);
    test_fail(root_pool);
    test_fail_1byte(root_pool);
    test_abort_without_handler(root_pool);
#ifndef NO_ABORT_ISTREAM
    test_abort_in_handler(root_pool);
    if (enable_blocking)
        test_abort_in_handler_half(root_pool);
#endif
    test_abort_1byte(root_pool);
    test_later(root_pool);

#ifdef EXPECTED_RESULT
    test_big_hold(root_pool);
#endif

#ifdef CUSTOM_TEST
    test_custom(root_pool);
#endif

    /* cleanup */

    pool_unref(root_pool);
    pool_commit();

    pool_recycler_clear();

    event_base_free(event_base);
    direct_global_deinit();
}
<|endoftext|>


<commit_before>
#include "service.h"
#include "../../core/system/cpu.h"
#include "../../thread/array.h"
#include "../../private/jobs/queue/thread.h"
#include <unistd.h>

namespace Yuni
{
namespace Job
{

	enum
	{
		maxNumberOfThreads = 512,
	};

	typedef Yuni::Thread::Array<Yuni::Private::QueueService::QueueThread>  ThreadArray;

	static inline uint OptimalCPUCount()
	{
		uint count = System::CPU::Count();
		if (count < 1)
			return 1;
		if (count > 2)
			return count - 1;
		return count;
	}

	QueueService::QueueService()
		: pThreads(((void*) new ThreadArray()))
	{
		uint count = OptimalCPUCount();
		pMinimumThreadCount = count;
		pMaximumThreadCount = count;
	}

	QueueService::QueueService(bool autostart)
		: pThreads(((void*) new ThreadArray()))
	{
		uint count = OptimalCPUCount();
		pMinimumThreadCount = count;
		pMaximumThreadCount = count;

		if (YUNI_LIKELY(autostart))
			start();
	}

	QueueService::~QueueService()
	{
		if (pStarted)
		{
			// wait for the execution of all jobs
			if (not idle() or not pWaitingRoom.empty())
				pSignalAllThreadHaveStopped.wait();

			// retrieve the thread pool
			ThreadArray* threads = nullptr; // the thread pool
			{
				MutexLocker locker(*this);
				threads = (ThreadArray*) pThreads;
				pThreads = nullptr;
			}

			// Destroying the thread pool
			if (threads)
			{
				threads->stop(defaultTimeout);
				delete threads;
			}
		}
		else
		{
			// the queueservice might be stopped, but the thread pool might have been created
			delete ((ThreadArray*) pThreads);
		}
	}

	bool QueueService::maximumThreadCount(uint count)
	{
		if (count > maxNumberOfThreads) // hard-coded value
			return false;
		if (0 == count)
			count = OptimalCPUCount();

		MutexLocker locker(*this);

		// checking for the lower bound
		if (pMinimumThreadCount > count)
			pMinimumThreadCount = count;
		// reseting the upper bound
		pMaximumThreadCount = count;
		return true;
	}

	uint QueueService::maximumThreadCount() const
	{
		MutexLocker locker(*this);
		return pMaximumThreadCount;
	}

	bool QueueService::minimumThreadCount(uint count)
	{
		if (count > maxNumberOfThreads) // hard-coded value
			return false;
		if (0 == count)
			count = OptimalCPUCount();

		MutexLocker locker(*this);

		// checking for the upper bound
		if (pMaximumThreadCount < count)
			pMaximumThreadCount = count;
		// reseting the lower bound
		pMinimumThreadCount = count;
		return true;
	}

	uint QueueService::minimumThreadCount() const
	{
		MutexLocker locker(*this);
		return pMinimumThreadCount;
	}

	std::pair<uint,uint> QueueService::minmaxThreadCount() const
	{
		MutexLocker locker(*this);
		return std::pair<uint, uint>(pMinimumThreadCount, pMaximumThreadCount);
	}

	bool QueueService::minmaxThreadCount(const std::pair<uint, uint>& values)
	{
		uint maxv = values.second;
		if (maxv > maxNumberOfThreads)
			return false;
		uint minv = values.first;
		if (minv > maxv)
			minv = maxv;
		if (maxv == 0)
		{
			maxv = OptimalCPUCount();
			minv = maxv;
		}

		MutexLocker locker(*this);
		pMinimumThreadCount = minv;
		pMaximumThreadCount = maxv;
		return true;
	}

	bool QueueService::start()
	{
		MutexLocker locker(*this);
		if (YUNI_LIKELY(not pStarted))
		{
			// alias to the thread pool
			ThreadArray& array = *((ThreadArray*) pThreads);

			// recreate the thread pool
			// adding the minimum number of threads
			array.clear();
			for (uint i = 0; i != pMinimumThreadCount; ++i)
				array += new Yuni::Private::QueueService::QueueThread(*this);

			// Start all threads at once
			array.start();

			// Ok now we have started
			pStarted = true;
		}
		return true;
	}

	bool QueueService::stop(uint timeout)
	{
		if (pStarted)
		{
			ThreadArray* threads; // the thread pool

			// The method stop must avoid as much as possible to lock the inner mutex
			{
				MutexLocker locker(*this);
				if (not pStarted)
					return true;

				threads = (ThreadArray*) pThreads;
				pThreads = nullptr;

				// The service is now really stopped
				pStarted = false;
			}

			// Destroying the thread pool
			if (YUNI_LIKELY(threads))
			{
				threads->stop(timeout);
				delete threads;
			}
		}
		return true;
	}

	void QueueService::wait()
	{
		if (pStarted)
		{
			while (not idle() or not pWaitingRoom.empty())
				pSignalAllThreadHaveStopped.waitAndReset();
		}
	}

	bool QueueService::wait(uint timeout, uint /*pollInterval*/)
	{
		if (pStarted)
		{
			// note: the timeout may not be respected here
			do
			{
				if (idle() and pWaitingRoom.empty())
					return true;

				if (not pSignalAllThreadHaveStopped.wait(timeout))
					return false; // timeout reached

				// we have been notified
				pSignalAllThreadHaveStopped.reset();
			}
			while (true);
		}
		return true;
	}

	void QueueService::add(IJob* job)
	{
		if (YUNI_LIKELY(job))
		{
			pWaitingRoom.add(job);
			((ThreadArray*) pThreads)->wakeUp();
		}
	}

	void QueueService::add(const IJob::Ptr& job)
	{
		if (YUNI_LIKELY(!(!job)))
		{
			pWaitingRoom.add(job);
			((ThreadArray*) pThreads)->wakeUp();
		}
	}

	void QueueService::add(const IJob::Ptr& job, Priority priority)
	{
		if (YUNI_LIKELY(!(!job)))
		{
			pWaitingRoom.add(job, priority);
			((ThreadArray*) pThreads)->wakeUp();
		}
	}

	void QueueService::add(IJob* job, Priority priority)
	{
		if (YUNI_LIKELY(!(!job)))
		{
			pWaitingRoom.add(job, priority);
			((ThreadArray*) pThreads)->wakeUp();
		}
	}

	uint QueueService::threadCount() const
	{
		return ((const ThreadArray*) pThreads)->size();
	}

	namespace // anonymous
	{

		class QueueActivityPredicate final
		{
		public:
			//!
			typedef typename QueueService::ThreadInfo ThreadInfoType;
			typedef typename ThreadInfoType::Vector VectorType;

		public:
			QueueActivityPredicate(VectorType* out) :
				pList(out)
			{
				pList->clear();
			}

			template<class ThreadPtrT>
			bool operator () (const ThreadPtrT& thread) const
			{
				ThreadInfoType* info = new ThreadInfoType();
				info->thread = thread;
				if (!(!(info->thread)))
				{
					info->job = thread->currentJob();
					if (!(!(info->job)))
					{
						// We have a job which is currently working !
						info->hasJob = true;
						info->job->fillInformation(*info);
						pList->push_back(info);
						return true;
					}
				}
				info->hasJob = false;
				info->state = Yuni::Job::stateIdle;
				info->canceling = false;
				info->progression = 0;
				pList->push_back(info);
				return true;
			}

		private:
			mutable VectorType* pList;
		};

	} // anonymous namespace

	void QueueService::activitySnapshot(QueueService::ThreadInfo::Vector& out)
	{
		QueueActivityPredicate predicate(&out);
		((ThreadArray*) pThreads)->foreachThread(predicate);
	}

} // namespace Job
} // namespace Yuni

<commit_msg>job: fixed compilation with Visual Studio<commit_after>
#include "service.h"
#include "../../core/system/cpu.h"
#include "../../thread/array.h"
#include "../../private/jobs/queue/thread.h"
#ifndef YUNI_OS_WINDOWS
	#include <unistd.h>
#endif

namespace Yuni
{
namespace Job
{

	enum
	{
		maxNumberOfThreads = 512,
	};

	typedef Yuni::Thread::Array<Yuni::Private::QueueService::QueueThread>  ThreadArray;

	static inline uint OptimalCPUCount()
	{
		uint count = System::CPU::Count();
		if (count < 1)
			return 1;
		if (count > 2)
			return count - 1;
		return count;
	}

	QueueService::QueueService()
		: pThreads(((void*) new ThreadArray()))
	{
		uint count = OptimalCPUCount();
		pMinimumThreadCount = count;
		pMaximumThreadCount = count;
	}

	QueueService::QueueService(bool autostart)
		: pThreads(((void*) new ThreadArray()))
	{
		uint count = OptimalCPUCount();
		pMinimumThreadCount = count;
		pMaximumThreadCount = count;

		if (YUNI_LIKELY(autostart))
			start();
	}

	QueueService::~QueueService()
	{
		if (pStarted)
		{
			// wait for the execution of all jobs
			if (not idle() or not pWaitingRoom.empty())
				pSignalAllThreadHaveStopped.wait();

			// retrieve the thread pool
			ThreadArray* threads = nullptr; // the thread pool
			{
				MutexLocker locker(*this);
				threads = (ThreadArray*) pThreads;
				pThreads = nullptr;
			}

			// Destroying the thread pool
			if (threads)
			{
				threads->stop(defaultTimeout);
				delete threads;
			}
		}
		else
		{
			// the queueservice might be stopped, but the thread pool might have been created
			delete ((ThreadArray*) pThreads);
		}
	}

	bool QueueService::maximumThreadCount(uint count)
	{
		if (count > maxNumberOfThreads) // hard-coded value
			return false;
		if (0 == count)
			count = OptimalCPUCount();

		MutexLocker locker(*this);

		// checking for the lower bound
		if (pMinimumThreadCount > count)
			pMinimumThreadCount = count;
		// reseting the upper bound
		pMaximumThreadCount = count;
		return true;
	}

	uint QueueService::maximumThreadCount() const
	{
		MutexLocker locker(*this);
		return pMaximumThreadCount;
	}

	bool QueueService::minimumThreadCount(uint count)
	{
		if (count > maxNumberOfThreads) // hard-coded value
			return false;
		if (0 == count)
			count = OptimalCPUCount();

		MutexLocker locker(*this);

		// checking for the upper bound
		if (pMaximumThreadCount < count)
			pMaximumThreadCount = count;
		// reseting the lower bound
		pMinimumThreadCount = count;
		return true;
	}

	uint QueueService::minimumThreadCount() const
	{
		MutexLocker locker(*this);
		return pMinimumThreadCount;
	}

	std::pair<uint,uint> QueueService::minmaxThreadCount() const
	{
		MutexLocker locker(*this);
		return std::pair<uint, uint>(pMinimumThreadCount, pMaximumThreadCount);
	}

	bool QueueService::minmaxThreadCount(const std::pair<uint, uint>& values)
	{
		uint maxv = values.second;
		if (maxv > maxNumberOfThreads)
			return false;
		uint minv = values.first;
		if (minv > maxv)
			minv = maxv;
		if (maxv == 0)
		{
			maxv = OptimalCPUCount();
			minv = maxv;
		}

		MutexLocker locker(*this);
		pMinimumThreadCount = minv;
		pMaximumThreadCount = maxv;
		return true;
	}

	bool QueueService::start()
	{
		MutexLocker locker(*this);
		if (YUNI_LIKELY(not pStarted))
		{
			// alias to the thread pool
			ThreadArray& array = *((ThreadArray*) pThreads);

			// recreate the thread pool
			// adding the minimum number of threads
			array.clear();
			for (uint i = 0; i != pMinimumThreadCount; ++i)
				array += new Yuni::Private::QueueService::QueueThread(*this);

			// Start all threads at once
			array.start();

			// Ok now we have started
			pStarted = true;
		}
		return true;
	}

	bool QueueService::stop(uint timeout)
	{
		if (pStarted)
		{
			ThreadArray* threads; // the thread pool

			// The method stop must avoid as much as possible to lock the inner mutex
			{
				MutexLocker locker(*this);
				if (not pStarted)
					return true;

				threads = (ThreadArray*) pThreads;
				pThreads = nullptr;

				// The service is now really stopped
				pStarted = false;
			}

			// Destroying the thread pool
			if (YUNI_LIKELY(threads))
			{
				threads->stop(timeout);
				delete threads;
			}
		}
		return true;
	}

	void QueueService::wait()
	{
		if (pStarted)
		{
			while (not idle() or not pWaitingRoom.empty())
				pSignalAllThreadHaveStopped.waitAndReset();
		}
	}

	bool QueueService::wait(uint timeout, uint /*pollInterval*/)
	{
		if (pStarted)
		{
			// note: the timeout may not be respected here
			do
			{
				if (idle() and pWaitingRoom.empty())
					return true;

				if (not pSignalAllThreadHaveStopped.wait(timeout))
					return false; // timeout reached

				// we have been notified
				pSignalAllThreadHaveStopped.reset();
			}
			while (true);
		}
		return true;
	}

	void QueueService::add(IJob* job)
	{
		if (YUNI_LIKELY(job))
		{
			pWaitingRoom.add(job);
			((ThreadArray*) pThreads)->wakeUp();
		}
	}

	void QueueService::add(const IJob::Ptr& job)
	{
		if (YUNI_LIKELY(!(!job)))
		{
			pWaitingRoom.add(job);
			((ThreadArray*) pThreads)->wakeUp();
		}
	}

	void QueueService::add(const IJob::Ptr& job, Priority priority)
	{
		if (YUNI_LIKELY(!(!job)))
		{
			pWaitingRoom.add(job, priority);
			((ThreadArray*) pThreads)->wakeUp();
		}
	}

	void QueueService::add(IJob* job, Priority priority)
	{
		if (YUNI_LIKELY(!(!job)))
		{
			pWaitingRoom.add(job, priority);
			((ThreadArray*) pThreads)->wakeUp();
		}
	}

	uint QueueService::threadCount() const
	{
		return ((const ThreadArray*) pThreads)->size();
	}

	namespace // anonymous
	{

		class QueueActivityPredicate final
		{
		public:
			//!
			typedef typename QueueService::ThreadInfo ThreadInfoType;
			typedef typename ThreadInfoType::Vector VectorType;

		public:
			QueueActivityPredicate(VectorType* out) :
				pList(out)
			{
				pList->clear();
			}

			template<class ThreadPtrT>
			bool operator () (const ThreadPtrT& thread) const
			{
				ThreadInfoType* info = new ThreadInfoType();
				info->thread = thread;
				if (!(!(info->thread)))
				{
					info->job = thread->currentJob();
					if (!(!(info->job)))
					{
						// We have a job which is currently working !
						info->hasJob = true;
						info->job->fillInformation(*info);
						pList->push_back(info);
						return true;
					}
				}
				info->hasJob = false;
				info->state = Yuni::Job::stateIdle;
				info->canceling = false;
				info->progression = 0;
				pList->push_back(info);
				return true;
			}

		private:
			mutable VectorType* pList;
		};

	} // anonymous namespace

	void QueueService::activitySnapshot(QueueService::ThreadInfo::Vector& out)
	{
		QueueActivityPredicate predicate(&out);
		((ThreadArray*) pThreads)->foreachThread(predicate);
	}

} // namespace Job
} // namespace Yuni

<|endoftext|>


<commit_before>#include "ConfigQuorumContext.h"
#include "Framework/Replication/ReplicationConfig.h"
#include "Framework/Replication/PaxosLease/PaxosLeaseMessage.h"
#include "Framework/Replication/Paxos/PaxosMessage.h"
#include "Application/Common/CatchupMessage.h"
#include "ConfigQuorumProcessor.h"

void ConfigQuorumContext::Init(ConfigQuorumProcessor* quorumProcessor_, unsigned numConfigServers,
 StorageShardProxy* quorumPaxosShard, StorageShardProxy* quorumLogShard)
{
    uint64_t nodeID;

    quorumProcessor = quorumProcessor_;

    quorumID = 0;
    for (nodeID = 0; nodeID < numConfigServers; nodeID++)
        quorum.AddNode(nodeID);

    transport.SetQuorum(&quorum);
    transport.SetQuorumID(quorumID);

    database.Init(quorumPaxosShard, quorumLogShard);

    replicatedLog.Init(this);
    replicatedLog.SetUseProposeTimeouts(true);
    replicatedLog.SetCommitChaining(false);
    replicatedLog.SetAsyncCommit(false);
    paxosLease.Init(this);
    transport.SetQuorumID(quorumID);
    highestPaxosID = 0;

    paxosLease.AcquireLease();

    isReplicationActive = true;
}

void ConfigQuorumContext::Append(ConfigMessage* message)
{
    message->Write(nextValue);

    replicatedLog.TryAppendNextValue();
}

bool ConfigQuorumContext::IsAppending()
{
    return (nextValue.GetLength() != 0);
}

bool ConfigQuorumContext::IsLeaseOwner()
{
    return paxosLease.IsLeaseOwner();
}

bool ConfigQuorumContext::IsLeaseKnown()
{
    return paxosLease.IsLeaseKnown();
}

uint64_t ConfigQuorumContext::GetLeaseOwner()
{
    return paxosLease.GetLeaseOwner();
}

bool ConfigQuorumContext::IsLeader()
{
    return IsLeaseOwner() && replicatedLog.IsMultiPaxosEnabled();
}

void ConfigQuorumContext::OnLearnLease()
{
    replicatedLog.OnLearnLease();
    quorumProcessor->OnLearnLease();
}

void ConfigQuorumContext::OnLeaseTimeout()
{
    nextValue.Clear();
    replicatedLog.OnLeaseTimeout();
    quorumProcessor->OnLeaseTimeout();
}

void ConfigQuorumContext::OnIsLeader()
{
    quorumProcessor->OnIsLeader();
}

uint64_t ConfigQuorumContext::GetQuorumID()
{
    return quorumID;
}

void ConfigQuorumContext::SetPaxosID(uint64_t paxosID)
{
    replicatedLog.SetPaxosID(paxosID);
}

uint64_t ConfigQuorumContext::GetPaxosID()
{
    return replicatedLog.GetPaxosID();
}

uint64_t ConfigQuorumContext::GetHighestPaxosID()
{
    return highestPaxosID;
}

Quorum* ConfigQuorumContext::GetQuorum()
{
    return &quorum;
}

QuorumDatabase* ConfigQuorumContext::GetDatabase()
{
    return &database;
}

QuorumTransport* ConfigQuorumContext::GetTransport()
{
    return &transport;
}

bool ConfigQuorumContext::IsPaxosBlocked()
{
    return false;
}

Buffer& ConfigQuorumContext::GetNextValue()
{
    return nextValue;
}

void ConfigQuorumContext::OnAppend(uint64_t paxosID, Buffer& value, bool ownAppend)
{
    bool            ret;
    ReadBuffer      rbValue;
    ConfigMessage   message;

    nextValue.Clear();

    rbValue.Wrap(value);
    ret = message.Read(rbValue);
    ASSERT(ret);
    quorumProcessor->OnAppend(paxosID, message, ownAppend);
}

void ConfigQuorumContext::OnMessage(uint64_t /*nodeID*/, ReadBuffer buffer)
{
    char proto;

    Log_Trace("%R", &buffer);

//    quorumProcessor->RegisterHeartbeat(nodeID);

    if (buffer.GetLength() < 2)
        ASSERT_FAIL();

    proto = buffer.GetCharAt(0);
    ASSERT(buffer.GetCharAt(1) == ':');

    switch(proto)
    {
        case PAXOSLEASE_PROTOCOL_ID:        // 'L':
            OnPaxosLeaseMessage(buffer);
            break;
        case PAXOS_PROTOCOL_ID:             // 'P':
            OnPaxosMessage(buffer);
            break;
        case CATCHUP_PROTOCOL_ID:           // 'C'
            OnCatchupMessage(buffer);
            break;
        default:
            ASSERT_FAIL();
            break;
    }
}

void ConfigQuorumContext::OnStartCatchup()
{
    quorumProcessor->OnStartCatchup();
}

void ConfigQuorumContext::OnCatchupComplete(uint64_t paxosID)
{
    replicatedLog.OnCatchupComplete(paxosID);
}

void ConfigQuorumContext::OnAppendComplete()
{
    replicatedLog.OnAppendComplete();
}

void ConfigQuorumContext::StopReplication()
{
    // TODO: xxx
}

void ConfigQuorumContext::ContinueReplication()
{
    // TODO: xxx
}

void ConfigQuorumContext::OnPaxosLeaseMessage(ReadBuffer buffer)
{
    PaxosLeaseMessage msg;

    Log_Trace("%R", &buffer);

    msg.Read(buffer);
    if (msg.type == PAXOSLEASE_PREPARE_REQUEST || msg.type == PAXOSLEASE_LEARN_CHOSEN)
    {
        if (isReplicationActive)
        {
            RegisterPaxosID(msg.paxosID);
            replicatedLog.RegisterPaxosID(msg.paxosID, msg.nodeID);
        }
    }
    paxosLease.OnMessage(msg);
    // TODO: right now PaxosLeaseLearner will not call back
}

void ConfigQuorumContext::OnPaxosMessage(ReadBuffer buffer)
{
    PaxosMessage msg;

    if (!isReplicationActive)
        return;

    msg.Read(buffer);
    RegisterPaxosID(msg.paxosID);
    replicatedLog.RegisterPaxosID(msg.paxosID, msg.nodeID);
    replicatedLog.OnMessage(msg);
}

void ConfigQuorumContext::OnCatchupMessage(ReadBuffer buffer)
{
    CatchupMessage msg;

    msg.Read(buffer);
    quorumProcessor->OnCatchupMessage(msg);
}

void ConfigQuorumContext::RegisterPaxosID(uint64_t paxosID)
{
    if (paxosID > highestPaxosID)
        highestPaxosID = paxosID;
}
<commit_msg>Don't use PaxosLease on the controller in single mode.<commit_after>#include "ConfigQuorumContext.h"
#include "Framework/Replication/ReplicationConfig.h"
#include "Framework/Replication/PaxosLease/PaxosLeaseMessage.h"
#include "Framework/Replication/Paxos/PaxosMessage.h"
#include "Application/Common/CatchupMessage.h"
#include "ConfigQuorumProcessor.h"

void ConfigQuorumContext::Init(ConfigQuorumProcessor* quorumProcessor_, unsigned numConfigServers,
 StorageShardProxy* quorumPaxosShard, StorageShardProxy* quorumLogShard)
{
    uint64_t nodeID;

    quorumProcessor = quorumProcessor_;

    quorumID = 0;
    for (nodeID = 0; nodeID < numConfigServers; nodeID++)
        quorum.AddNode(nodeID);

    transport.SetQuorum(&quorum);
    transport.SetQuorumID(quorumID);

    database.Init(quorumPaxosShard, quorumLogShard);

    replicatedLog.Init(this);
    replicatedLog.SetUseProposeTimeouts(true);
    replicatedLog.SetCommitChaining(false);
    replicatedLog.SetAsyncCommit(false);
    paxosLease.Init(this);
    transport.SetQuorumID(quorumID);
    highestPaxosID = 0;

    isReplicationActive = true;

    if (quorum.GetNumNodes() > 1)
    {
        paxosLease.AcquireLease();
    }
    else
    {
        replicatedLog.OnLearnLease();
        quorumProcessor->OnLearnLease();
    }

}

void ConfigQuorumContext::Append(ConfigMessage* message)
{
    message->Write(nextValue);

    replicatedLog.TryAppendNextValue();
}

bool ConfigQuorumContext::IsAppending()
{
    return (nextValue.GetLength() != 0);
}

bool ConfigQuorumContext::IsLeaseOwner()
{
    if (quorum.GetNumNodes() == 1)
        return true;
    else
        return paxosLease.IsLeaseOwner();
}

bool ConfigQuorumContext::IsLeaseKnown()
{
    if (quorum.GetNumNodes() == 1)
        return true;
    else
        return paxosLease.IsLeaseKnown();
}

uint64_t ConfigQuorumContext::GetLeaseOwner()
{
    if (quorum.GetNumNodes() == 1)
        return MY_NODEID;
    else
        return paxosLease.GetLeaseOwner();
}

bool ConfigQuorumContext::IsLeader()
{
    if (quorum.GetNumNodes() == 1)
        return true;
    else
        return IsLeaseOwner() && replicatedLog.IsMultiPaxosEnabled();
}

void ConfigQuorumContext::OnLearnLease()
{
    replicatedLog.OnLearnLease();
    quorumProcessor->OnLearnLease();
}

void ConfigQuorumContext::OnLeaseTimeout()
{
    nextValue.Clear();
    replicatedLog.OnLeaseTimeout();
    quorumProcessor->OnLeaseTimeout();
}

void ConfigQuorumContext::OnIsLeader()
{
    quorumProcessor->OnIsLeader();
}

uint64_t ConfigQuorumContext::GetQuorumID()
{
    return quorumID;
}

void ConfigQuorumContext::SetPaxosID(uint64_t paxosID)
{
    replicatedLog.SetPaxosID(paxosID);
}

uint64_t ConfigQuorumContext::GetPaxosID()
{
    return replicatedLog.GetPaxosID();
}

uint64_t ConfigQuorumContext::GetHighestPaxosID()
{
    return highestPaxosID;
}

Quorum* ConfigQuorumContext::GetQuorum()
{
    return &quorum;
}

QuorumDatabase* ConfigQuorumContext::GetDatabase()
{
    return &database;
}

QuorumTransport* ConfigQuorumContext::GetTransport()
{
    return &transport;
}

bool ConfigQuorumContext::IsPaxosBlocked()
{
    return false;
}

Buffer& ConfigQuorumContext::GetNextValue()
{
    return nextValue;
}

void ConfigQuorumContext::OnAppend(uint64_t paxosID, Buffer& value, bool ownAppend)
{
    bool            ret;
    ReadBuffer      rbValue;
    ConfigMessage   message;

    nextValue.Clear();

    rbValue.Wrap(value);
    ret = message.Read(rbValue);
    ASSERT(ret);
    quorumProcessor->OnAppend(paxosID, message, ownAppend);
}

void ConfigQuorumContext::OnMessage(uint64_t /*nodeID*/, ReadBuffer buffer)
{
    char proto;

    Log_Trace("%R", &buffer);

//    quorumProcessor->RegisterHeartbeat(nodeID);

    if (buffer.GetLength() < 2)
        ASSERT_FAIL();

    proto = buffer.GetCharAt(0);
    ASSERT(buffer.GetCharAt(1) == ':');

    switch(proto)
    {
        case PAXOSLEASE_PROTOCOL_ID:        // 'L':
            OnPaxosLeaseMessage(buffer);
            break;
        case PAXOS_PROTOCOL_ID:             // 'P':
            OnPaxosMessage(buffer);
            break;
        case CATCHUP_PROTOCOL_ID:           // 'C'
            OnCatchupMessage(buffer);
            break;
        default:
            ASSERT_FAIL();
            break;
    }
}

void ConfigQuorumContext::OnStartCatchup()
{
    quorumProcessor->OnStartCatchup();
}

void ConfigQuorumContext::OnCatchupComplete(uint64_t paxosID)
{
    replicatedLog.OnCatchupComplete(paxosID);
}

void ConfigQuorumContext::OnAppendComplete()
{
    replicatedLog.OnAppendComplete();
}

void ConfigQuorumContext::StopReplication()
{
    // TODO: xxx
}

void ConfigQuorumContext::ContinueReplication()
{
    // TODO: xxx
}

void ConfigQuorumContext::OnPaxosLeaseMessage(ReadBuffer buffer)
{
    PaxosLeaseMessage msg;

    Log_Trace("%R", &buffer);

    msg.Read(buffer);
    if (msg.type == PAXOSLEASE_PREPARE_REQUEST || msg.type == PAXOSLEASE_LEARN_CHOSEN)
    {
        if (isReplicationActive)
        {
            RegisterPaxosID(msg.paxosID);
            replicatedLog.RegisterPaxosID(msg.paxosID, msg.nodeID);
        }
    }
    paxosLease.OnMessage(msg);
    // TODO: right now PaxosLeaseLearner will not call back
}

void ConfigQuorumContext::OnPaxosMessage(ReadBuffer buffer)
{
    PaxosMessage msg;

    if (!isReplicationActive)
        return;

    msg.Read(buffer);
    RegisterPaxosID(msg.paxosID);
    replicatedLog.RegisterPaxosID(msg.paxosID, msg.nodeID);
    replicatedLog.OnMessage(msg);
}

void ConfigQuorumContext::OnCatchupMessage(ReadBuffer buffer)
{
    CatchupMessage msg;

    msg.Read(buffer);
    quorumProcessor->OnCatchupMessage(msg);
}

void ConfigQuorumContext::RegisterPaxosID(uint64_t paxosID)
{
    if (paxosID > highestPaxosID)
        highestPaxosID = paxosID;
}
<|endoftext|>


<commit_before>#include "ShardMigrationWriter.h"
#include "Application/Common/ContextTransport.h"
#include "ShardServer.h"
#include "ShardQuorumProcessor.h"

ShardMigrationWriter::ShardMigrationWriter()
{
    onTimeout.SetCallable(MFUNC(ShardMigrationWriter, OnTimeout));
    onTimeout.SetDelay(SHARD_MIGRATION_WRITER_DELAY);
    Reset();
}

ShardMigrationWriter::~ShardMigrationWriter()
{
    if (cursor != NULL)
        delete cursor;
}

void ShardMigrationWriter::Init(ShardServer* shardServer_)
{
    shardServer = shardServer_;
    environment = shardServer->GetDatabaseManager()->GetEnvironment();

    Reset();
}

void ShardMigrationWriter::Reset()
{
    cursor = NULL;
    isActive = false;
    sendFirst = false;
    quorumProcessor = NULL;
    bytesSent = 0;
    bytesTotal = 0;
    startTime = 0;
    prevBytesSent = 0;
    EventLoop::Remove(&onTimeout);
}

bool ShardMigrationWriter::IsActive()
{
    return isActive;
}

uint64_t ShardMigrationWriter::GetShardID()
{
    return shardID;
}

uint64_t ShardMigrationWriter::GetQuorumID()
{
    return quorumID;
}

uint64_t ShardMigrationWriter::GetNodeID()
{
    return nodeID;
}

uint64_t ShardMigrationWriter::GetBytesSent()
{
    return bytesSent;
}

uint64_t ShardMigrationWriter::GetBytesTotal()
{
    return bytesTotal;
}

uint64_t ShardMigrationWriter::GetThroughput()
{
    uint64_t now;

    now = NowClock();

    if (now > startTime)
        return bytesSent / ((now - startTime)/1000.0);
    else
        return 0;
}

void ShardMigrationWriter::Begin(ClusterMessage& request)
{
    ConfigState*        configState;
    ConfigShard*        configShard;
    ConfigShardServer*  configShardServer;

    assert(!isActive);
    assert(cursor == NULL);

    configState = shardServer->GetConfigState();
    configShard = configState->GetShard(request.shardID);
    assert(configShard != NULL);
    configShardServer = configState->GetShardServer(request.nodeID);
    assert(configShardServer != NULL);

    quorumProcessor = shardServer->GetQuorumProcessor(configShard->quorumID);
    assert(quorumProcessor != NULL);

    isActive = true;
    nodeID = request.nodeID;
    quorumID = request.quorumID;
    shardID = request.shardID;

    bytesTotal = environment->GetSize(QUORUM_DATABASE_DATA_CONTEXT, shardID);
    startTime = NowClock();

    CONTEXT_TRANSPORT->AddNode(nodeID, configShardServer->endpoint);

    Log_Debug("ShardMigrationWriter::Begin() nodeID = %U", nodeID);
    Log_Debug("ShardMigrationWriter::Begin() quorumID = %U", quorumID);
    Log_Debug("ShardMigrationWriter::Begin() shardID = %U", shardID);

    Log_Message("Migrating shard %U into quorum %U (sending)", shardID, quorumID);

    sendFirst = true;
    CONTEXT_TRANSPORT->RegisterWriteReadyness(nodeID, MFUNC(ShardMigrationWriter, OnWriteReadyness));
    EventLoop::Add(&onTimeout);
}

void ShardMigrationWriter::Abort()
{
    Log_Message("Aborting shard migration...", shardID, quorumID);

    CONTEXT_TRANSPORT->UnregisterWriteReadyness(nodeID, MFUNC(ShardMigrationWriter, OnWriteReadyness));
    Reset();

    if (cursor != NULL)
    {
        delete cursor;
        cursor = NULL;
    }
}

void ShardMigrationWriter::SendFirst()
{
    ClusterMessage      msg;
    ReadBuffer          key;
    ReadBuffer          value;

    assert(cursor == NULL);
    cursor = environment->GetBulkCursor(QUORUM_DATABASE_DATA_CONTEXT, shardID);

    msg.ShardMigrationBegin(quorumID, shardID);
    CONTEXT_TRANSPORT->SendClusterMessage(nodeID, msg);

    Log_Debug("ShardMigrationWriter sending BEGIN");

    // send first KV
    kv = cursor->First();
    if (kv)
        SendItem(kv);
    else
        SendCommit();
}

void ShardMigrationWriter::SendNext()
{
    assert(cursor != NULL);
    kv = cursor->Next(kv);
    if (kv)
        SendItem(kv);
    else
        SendCommit();
}

void ShardMigrationWriter::SendCommit()
{
    ClusterMessage msg;

    msg.ShardMigrationCommit(quorumID, shardID);
    CONTEXT_TRANSPORT->SendClusterMessage(nodeID, msg);

    Log_Message("Finished sending shard %U...", shardID);

    if (cursor != NULL)
    {
        delete cursor;
        cursor = NULL;
    }

    CONTEXT_TRANSPORT->UnregisterWriteReadyness(nodeID, MFUNC(ShardMigrationWriter, OnWriteReadyness));

    Reset();
}

void ShardMigrationWriter::SendItem(StorageKeyValue* kv)
{
    ClusterMessage msg;

    if (kv->GetType() == STORAGE_KEYVALUE_TYPE_SET)
    {
        msg.ShardMigrationSet(quorumID, shardID, kv->GetKey(), kv->GetValue());
        bytesSent += kv->GetKey().GetLength() + kv->GetValue().GetLength();
    }
    else
    {
        msg.ShardMigrationDelete(quorumID, shardID, kv->GetKey());
        bytesSent += kv->GetKey().GetLength();
    }

    CONTEXT_TRANSPORT->SendClusterMessage(nodeID, msg);
}

void ShardMigrationWriter::OnWriteReadyness()
{
    assert(quorumProcessor != NULL);
    if (!quorumProcessor->IsPrimary()
     || !shardServer->GetConfigState()->isMigrating
     || (shardServer->GetConfigState()->isMigrating &&
         (shardServer->GetConfigState()->migrateShardID != shardID ||
          shardServer->GetConfigState()->migrateQuorumID != quorumID)))
    {
        Abort();
        return;
    }

    if (sendFirst)
    {
        sendFirst = false;
        SendFirst();
    }
    else
    {
        SendNext();
    }
}

void ShardMigrationWriter::OnTimeout()
{
    ConfigState*    configState;
    ConfigQuorum*   configQuorum;

    assert(quorumProcessor != NULL);
    if (!quorumProcessor->IsPrimary()
     || !shardServer->GetConfigState()->isMigrating
     || (shardServer->GetConfigState()->isMigrating &&
         (shardServer->GetConfigState()->migrateShardID != shardID ||
          shardServer->GetConfigState()->migrateQuorumID != quorumID)))
    {
        Abort();
        return;
    }

    // check the destination nodeID is still in the quorum
    configState = quorumProcessor->GetShardServer()->GetConfigState();
    configQuorum = configState->GetQuorum(quorumID);
    if (configQuorum == NULL)
    {
        Abort();
        return;
    }
    if (!configQuorum->IsMember(nodeID))
    {
        Abort();
        return;
    }

    if (bytesSent == prevBytesSent)
    {
        Abort();
        return;
    }

    prevBytesSent = bytesSent;
    EventLoop::Add(&onTimeout);
}
<commit_msg>Fixed bug where ShardServer would crash if migrating empty database.<commit_after>#include "ShardMigrationWriter.h"
#include "Application/Common/ContextTransport.h"
#include "ShardServer.h"
#include "ShardQuorumProcessor.h"

ShardMigrationWriter::ShardMigrationWriter()
{
    onTimeout.SetCallable(MFUNC(ShardMigrationWriter, OnTimeout));
    onTimeout.SetDelay(SHARD_MIGRATION_WRITER_DELAY);
    Reset();
}

ShardMigrationWriter::~ShardMigrationWriter()
{
    if (cursor != NULL)
        delete cursor;
}

void ShardMigrationWriter::Init(ShardServer* shardServer_)
{
    shardServer = shardServer_;
    environment = shardServer->GetDatabaseManager()->GetEnvironment();

    Reset();
}

void ShardMigrationWriter::Reset()
{
    cursor = NULL;
    isActive = false;
    sendFirst = false;
    quorumProcessor = NULL;
    bytesSent = 0;
    bytesTotal = 0;
    startTime = 0;
    prevBytesSent = 0;
    EventLoop::Remove(&onTimeout);
}

bool ShardMigrationWriter::IsActive()
{
    return isActive;
}

uint64_t ShardMigrationWriter::GetShardID()
{
    return shardID;
}

uint64_t ShardMigrationWriter::GetQuorumID()
{
    return quorumID;
}

uint64_t ShardMigrationWriter::GetNodeID()
{
    return nodeID;
}

uint64_t ShardMigrationWriter::GetBytesSent()
{
    return bytesSent;
}

uint64_t ShardMigrationWriter::GetBytesTotal()
{
    return bytesTotal;
}

uint64_t ShardMigrationWriter::GetThroughput()
{
    uint64_t now;

    now = NowClock();

    if (now > startTime)
        return bytesSent / ((now - startTime)/1000.0);
    else
        return 0;
}

void ShardMigrationWriter::Begin(ClusterMessage& request)
{
    ConfigState*        configState;
    ConfigShard*        configShard;
    ConfigShardServer*  configShardServer;

    assert(!isActive);
    assert(cursor == NULL);

    configState = shardServer->GetConfigState();
    configShard = configState->GetShard(request.shardID);
    assert(configShard != NULL);
    configShardServer = configState->GetShardServer(request.nodeID);
    assert(configShardServer != NULL);

    quorumProcessor = shardServer->GetQuorumProcessor(configShard->quorumID);
    assert(quorumProcessor != NULL);

    isActive = true;
    nodeID = request.nodeID;
    quorumID = request.quorumID;
    shardID = request.shardID;

    bytesTotal = environment->GetSize(QUORUM_DATABASE_DATA_CONTEXT, shardID);
    startTime = NowClock();

    CONTEXT_TRANSPORT->AddNode(nodeID, configShardServer->endpoint);

    Log_Debug("ShardMigrationWriter::Begin() nodeID = %U", nodeID);
    Log_Debug("ShardMigrationWriter::Begin() quorumID = %U", quorumID);
    Log_Debug("ShardMigrationWriter::Begin() shardID = %U", shardID);

    Log_Message("Migrating shard %U into quorum %U (sending)", shardID, quorumID);

    sendFirst = true;
    EventLoop::Add(&onTimeout);
    CONTEXT_TRANSPORT->RegisterWriteReadyness(nodeID, MFUNC(ShardMigrationWriter, OnWriteReadyness));
}

void ShardMigrationWriter::Abort()
{
    Log_Message("Aborting shard migration...", shardID, quorumID);

    CONTEXT_TRANSPORT->UnregisterWriteReadyness(nodeID, MFUNC(ShardMigrationWriter, OnWriteReadyness));
    Reset();

    if (cursor != NULL)
    {
        delete cursor;
        cursor = NULL;
    }
}

void ShardMigrationWriter::SendFirst()
{
    ClusterMessage      msg;
    ReadBuffer          key;
    ReadBuffer          value;

    assert(cursor == NULL);
    cursor = environment->GetBulkCursor(QUORUM_DATABASE_DATA_CONTEXT, shardID);

    msg.ShardMigrationBegin(quorumID, shardID);
    CONTEXT_TRANSPORT->SendClusterMessage(nodeID, msg);

    Log_Debug("ShardMigrationWriter sending BEGIN");

    // send first KV
    kv = cursor->First();
    if (kv)
        SendItem(kv);
    else
        SendCommit();
}

void ShardMigrationWriter::SendNext()
{
    assert(cursor != NULL);
    kv = cursor->Next(kv);
    if (kv)
        SendItem(kv);
    else
        SendCommit();
}

void ShardMigrationWriter::SendCommit()
{
    ClusterMessage msg;

    msg.ShardMigrationCommit(quorumID, shardID);
    CONTEXT_TRANSPORT->SendClusterMessage(nodeID, msg);

    Log_Message("Finished sending shard %U...", shardID);

    if (cursor != NULL)
    {
        delete cursor;
        cursor = NULL;
    }

    CONTEXT_TRANSPORT->UnregisterWriteReadyness(nodeID, MFUNC(ShardMigrationWriter, OnWriteReadyness));

    Reset();
}

void ShardMigrationWriter::SendItem(StorageKeyValue* kv)
{
    ClusterMessage msg;

    if (kv->GetType() == STORAGE_KEYVALUE_TYPE_SET)
    {
        msg.ShardMigrationSet(quorumID, shardID, kv->GetKey(), kv->GetValue());
        bytesSent += kv->GetKey().GetLength() + kv->GetValue().GetLength();
    }
    else
    {
        msg.ShardMigrationDelete(quorumID, shardID, kv->GetKey());
        bytesSent += kv->GetKey().GetLength();
    }

    CONTEXT_TRANSPORT->SendClusterMessage(nodeID, msg);
}

void ShardMigrationWriter::OnWriteReadyness()
{
    assert(quorumProcessor != NULL);
    if (!quorumProcessor->IsPrimary()
     || !shardServer->GetConfigState()->isMigrating
     || (shardServer->GetConfigState()->isMigrating &&
         (shardServer->GetConfigState()->migrateShardID != shardID ||
          shardServer->GetConfigState()->migrateQuorumID != quorumID)))
    {
        Abort();
        return;
    }

    if (sendFirst)
    {
        sendFirst = false;
        SendFirst();
    }
    else
    {
        SendNext();
    }
}

void ShardMigrationWriter::OnTimeout()
{
    ConfigState*    configState;
    ConfigQuorum*   configQuorum;

    assert(quorumProcessor != NULL);
    if (!quorumProcessor->IsPrimary()
     || !shardServer->GetConfigState()->isMigrating
     || (shardServer->GetConfigState()->isMigrating &&
         (shardServer->GetConfigState()->migrateShardID != shardID ||
          shardServer->GetConfigState()->migrateQuorumID != quorumID)))
    {
        Abort();
        return;
    }

    // check the destination nodeID is still in the quorum
    configState = quorumProcessor->GetShardServer()->GetConfigState();
    configQuorum = configState->GetQuorum(quorumID);
    if (configQuorum == NULL)
    {
        Abort();
        return;
    }
    if (!configQuorum->IsMember(nodeID))
    {
        Abort();
        return;
    }

    if (bytesSent == prevBytesSent)
    {
        Abort();
        return;
    }

    prevBytesSent = bytesSent;
    EventLoop::Add(&onTimeout);
}
<|endoftext|>