<commit_before>/*
 * DefaultForwardChainerCB.cc
 *
 * Copyright (C) 2015 Misgana Bayetta
 *
 * Author: Misgana Bayetta <misgana.bayetta@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "DefaultForwardChainerCB.h"
#include "PLNCommons.h"

#include <opencog/query/PatternMatch.h>
#include <opencog/guile/SchemeEval.h>
#include <opencog/guile/SchemeSmob.h>

DefaultForwardChainerCB::DefaultForwardChainerCB(AtomSpace* as) :
		ForwardChainerCallBack(as) {
	as_ = as;
	fcim_ = new ForwardChainInputMatchCB(as);
	fcpm_ = new ForwardChainPatternMatchCB(as);
}

DefaultForwardChainerCB::~DefaultForwardChainerCB() {
	delete fcim_;
	delete fcpm_;
}

//-----------------callbacks-------------------------------------------------------------------------------//

//choose rule based on premises of rule matching the target
//uses temporary atomspace to limit the search space and avoid
vector<Rule*> DefaultForwardChainerCB::choose_rule(FCMemory& fcmem) {
	//create temporary atomspace and copy target
	AtomSpace rule_atomspace;
	SchemeEval sc(&rule_atomspace);
	Handle target = fcmem.get_cur_target();
	if (target == Handle::UNDEFINED or NodeCast(target))
		throw InvalidParamException(TRACE_INFO,
				"Needs a target atom of type LINK");
	sc.eval(SchemeSmob::to_string(target));
	//create bindlink with target as an implicant
	PLNCommons pc(&rule_atomspace);
	Handle target_cpy = pc.replace_nodes_with_varnode(target, NODE);
	Handle bind_link = pc.create_bindLink(target_cpy, false);
	//copy rules to the temporary atomspace
	vector<Rule*> rules = fcmem.get_rules();
	for (Rule* r : rules)
		sc.eval_h(SchemeSmob::to_string(r->get_handle()));
	//pattern match
	DefaultImplicator imp(&rule_atomspace);
	PatternMatch pm;
	try {
		pm.do_bindlink(bind_link, imp);
	} catch (InvalidParamException& e) {
		cout << "VALIDATION FAILED:" << endl << e.what() << endl;
	}
	//get matched bindLinks
	auto matchings = imp.result_list;
	if (matchings.empty())
		return vector<Rule*> { };
	HandleSeq bindlinks;
	for (Handle hm : matchings) {
		HandleSeq check_bindlink;
		HandleSeq parents;
		pc.get_top_level_parent(hm, parents);
		for (Handle hp : parents) {
			if (as_->getType(hp) == BIND_LINK
					and find(bindlinks.begin(), bindlinks.end(), hp)
							== bindlinks.end())
				check_bindlink.push_back(hp);
		}
		//make sure matches are actually part of the premise list rather than the output of the bindLink
		for (Handle hb : check_bindlink) {
			auto outgoing = [this](Handle h) {return as_->getOutgoing(h);};
			Handle hpremise = outgoing(outgoing(hb)[1])[0]; //extracting premise from (BindLink((ListLinK..)(ImpLink (premise) (..))))
			if (pc.exists_in(hpremise, hm))
				bindlinks.push_back(hb);
		}
	}
	//transfer back bindlinks to main atomspace and  get their handle
	SchemeEval scm(as_);
	HandleSeq copied_back;
	for (Handle h : bindlinks)
		copied_back.push_back(scm.eval_h(SchemeSmob::to_string(h)));
	//find the rules containing the bindLink in copied_back
	vector<Rule*> matched_rules;
	for (Rule* r : rules)
		if (find(copied_back.begin(), copied_back.end(), r->get_handle())
				!= copied_back.end())
			matched_rules.push_back(r);
	return matched_rules;
}

HandleSeq DefaultForwardChainerCB::choose_input(FCMemory& fcmem) {
	Handle htarget = fcmem.get_cur_target();
	HandleSeq inputs;
	if (NodeCast(htarget)) {
		inputs = as_->getIncoming(htarget);
	}
	if (LinkCast(htarget)) {
		map<Handle, string> hnode_vname_map = choose_variable(htarget);
		Handle implicant = target_to_pmimplicant(htarget, hnode_vname_map);
		PLNCommons pc(as_);
		Handle bindLink = pc.create_bindLink(implicant);
		//find inputs by pattern matching with custom callback fcim_
		PatternMatch pm;
		try {
			pm.do_bindlink(bindLink, *fcim_);
		} catch (InvalidParamException& e) {
			cout << "VALIDATION FAILED:" << endl << e.what() << endl;
		}
		inputs = fcim_->get_input_matches();
	}
	return inputs;
}

Handle DefaultForwardChainerCB::choose_next_target(FCMemory& fcmem) {
	HandleSeq tlist = fcmem.get_target_list();
	map<Handle, float> tournament_elem;
	PLNCommons pc(as_);
	for (Handle t : tlist) {
		float fitness = pc.target_tv_fitness(t);
		tournament_elem[t] = fitness;
	}
	return pc.tournament_select(tournament_elem);
}

//TODO applier should check on atoms (Inference.matched_atoms when Inference.Rule =Cur_Rule), for mutex rules
HandleSeq DefaultForwardChainerCB::apply_rule(FCMemory& fcmem) {
	Rule * cur_rule = fcmem.get_cur_rule();
	fcpm_->set_fcmem(&fcmem);
	PatternMatch pm;
	try {
		pm.do_bindlink(cur_rule->get_handle(), *fcpm_);
	} catch (InvalidParamException& e) {
		cout << "VALIDATION FAILED:" << endl << e.what() << endl;
	}
	return fcpm_->get_products();
}

//-------------------private helper methods------------------------------------------------------------------------------//

map<Handle, string> DefaultForwardChainerCB::choose_variable(Handle htarget) {
	PLNCommons pc(as_);
	vector<Handle> candidates = pc.get_nodes(htarget, vector<Type>());
	map<Handle, HandleSeq> node_iset_map;
	//enforce not choosing two or more variables in the same Link
	for (auto it = candidates.begin(); it != candidates.end(); ++it) {
		HandleSeq hs = as_->getIncoming(*it);
		if (distance(candidates.begin(), it) == 0) {
			node_iset_map[*it] = hs;
		} else {
			bool has_same_link = false;
			for (auto i = node_iset_map.begin(); i != node_iset_map.end();
					++i) {
				HandleSeq tmp;
				sort(hs.begin(), hs.end());
				sort(i->second.begin(), i->second.end());
				set_intersection(hs.begin(), hs.end(), i->second.begin(),
						i->second.end(), back_inserter((tmp)));
				if (tmp.size() > 0) {
					has_same_link = true;
					break;
				}
			}
			if (!has_same_link)
				node_iset_map[*it] = hs;
		}
	}
	map<Handle, string> hnode_vname_map;
	for (auto it = node_iset_map.begin(); it != node_iset_map.end(); ++it) {
		Handle h = it->first;
		hnode_vname_map[h] = ("$var-" + NodeCast((h))->getName());
	}
	return hnode_vname_map;
}

Handle DefaultForwardChainerCB::target_to_pmimplicant(Handle htarget,
		map<Handle, string> hnode_vname_map)
{
	if (LinkCast(htarget)) {
	   HandleSeq hsvariablized;
		LinkPtr p_htarget = LinkCast(htarget);
		Type link_type = p_htarget->getType();
		HandleSeq hsoutgoing = as_->getOutgoing(htarget);
		for (auto i = hsoutgoing.begin(); i != hsoutgoing.end(); ++i) {
			Handle htmp = target_to_pmimplicant(*i, hnode_vname_map);
			hsvariablized.push_back(htmp);
		}
		Handle h(as_->addLink(link_type, hsvariablized));
		h->setTruthValue(TruthValue::TRUE_TV());
		return h;
	} else {
		if (NodeCast(htarget)) {
			auto it_var = hnode_vname_map.find(htarget); //TODO replace by find-if for linear complexity
			NodePtr p_htarget = NodeCast(htarget);
			if (it_var != hnode_vname_map.end()) {
				Handle h = as_->addNode(VARIABLE_NODE, it_var->second);
				h->setTruthValue(TruthValue::TRUE_TV());
            return h;
         }
			else
				return htarget;
		}
	}
	return Handle::UNDEFINED; //unreachable?
}
<commit_msg>Replace with getNeighbors<commit_after>/*
 * DefaultForwardChainerCB.cc
 *
 * Copyright (C) 2015 Misgana Bayetta
 *
 * Author: Misgana Bayetta <misgana.bayetta@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "DefaultForwardChainerCB.h"
#include "PLNCommons.h"

#include <opencog/query/PatternMatch.h>
#include <opencog/guile/SchemeEval.h>
#include <opencog/guile/SchemeSmob.h>

DefaultForwardChainerCB::DefaultForwardChainerCB(AtomSpace* as) :
		ForwardChainerCallBack(as) {
	as_ = as;
	fcim_ = new ForwardChainInputMatchCB(as);
	fcpm_ = new ForwardChainPatternMatchCB(as);
}

DefaultForwardChainerCB::~DefaultForwardChainerCB() {
	delete fcim_;
	delete fcpm_;
}

//-----------------callbacks-------------------------------------------------------------------------------//

//choose rule based on premises of rule matching the target
//uses temporary atomspace to limit the search space and avoid
vector<Rule*> DefaultForwardChainerCB::choose_rule(FCMemory& fcmem) {
	//create temporary atomspace and copy target
	AtomSpace rule_atomspace;
	SchemeEval sc(&rule_atomspace);
	Handle target = fcmem.get_cur_target();
	if (target == Handle::UNDEFINED or NodeCast(target))
		throw InvalidParamException(TRACE_INFO,
				"Needs a target atom of type LINK");
	sc.eval(SchemeSmob::to_string(target));
	//create bindlink with target as an implicant
	PLNCommons pc(&rule_atomspace);
	Handle target_cpy = pc.replace_nodes_with_varnode(target, NODE);
	Handle bind_link = pc.create_bindLink(target_cpy, false);
	//copy rules to the temporary atomspace
	vector<Rule*> rules = fcmem.get_rules();
	for (Rule* r : rules)
		sc.eval_h(SchemeSmob::to_string(r->get_handle()));
	//pattern match
	DefaultImplicator imp(&rule_atomspace);
	PatternMatch pm;
	try {
		pm.do_bindlink(bind_link, imp);
	} catch (InvalidParamException& e) {
		cout << "VALIDATION FAILED:" << endl << e.what() << endl;
	}
	//get matched bindLinks
	auto matchings = imp.result_list;
	if (matchings.empty())
		return vector<Rule*> { };
	HandleSeq bindlinks;
	for (Handle hm : matchings) {
		HandleSeq check_bindlink;
		HandleSeq parents;
		pc.get_top_level_parent(hm, parents);
		for (Handle hp : parents) {
			if (as_->getType(hp) == BIND_LINK
					and find(bindlinks.begin(), bindlinks.end(), hp)
							== bindlinks.end())
				check_bindlink.push_back(hp);
		}
		//make sure matches are actually part of the premise list rather than the output of the bindLink
		for (Handle hb : check_bindlink) {
			auto outgoing = [this](Handle h) {return as_->getOutgoing(h);};
			Handle hpremise = outgoing(outgoing(hb)[1])[0]; //extracting premise from (BindLink((ListLinK..)(ImpLink (premise) (..))))
			if (pc.exists_in(hpremise, hm))
				bindlinks.push_back(hb);
		}
	}
	//transfer back bindlinks to main atomspace and  get their handle
	SchemeEval scm(as_);
	HandleSeq copied_back;
	for (Handle h : bindlinks)
		copied_back.push_back(scm.eval_h(SchemeSmob::to_string(h)));
	//find the rules containing the bindLink in copied_back
	vector<Rule*> matched_rules;
	for (Rule* r : rules)
		if (find(copied_back.begin(), copied_back.end(), r->get_handle())
				!= copied_back.end())
			matched_rules.push_back(r);
	return matched_rules;
}

HandleSeq DefaultForwardChainerCB::choose_input(FCMemory& fcmem) {
	Handle htarget = fcmem.get_cur_target();
	//get everything associated with the target handle
	HandleSeq inputs = as_->getNeighbors(htarget,true,true,LINK,true);
	return inputs;
}

Handle DefaultForwardChainerCB::choose_next_target(FCMemory& fcmem) {
	HandleSeq tlist = fcmem.get_target_list();
	map<Handle, float> tournament_elem;
	PLNCommons pc(as_);
	for (Handle t : tlist) {
		float fitness = pc.target_tv_fitness(t);
		tournament_elem[t] = fitness;
	}
	return pc.tournament_select(tournament_elem);
}

//TODO applier should check on atoms (Inference.matched_atoms when Inference.Rule =Cur_Rule), for mutex rules
HandleSeq DefaultForwardChainerCB::apply_rule(FCMemory& fcmem) {
	Rule * cur_rule = fcmem.get_cur_rule();
	fcpm_->set_fcmem(&fcmem);
	PatternMatch pm;
	try {
		pm.do_bindlink(cur_rule->get_handle(), *fcpm_);
	} catch (InvalidParamException& e) {
		cout << "VALIDATION FAILED:" << endl << e.what() << endl;
	}
	return fcpm_->get_products();
}

//-------------------private helper methods------------------------------------------------------------------------------//

map<Handle, string> DefaultForwardChainerCB::choose_variable(Handle htarget) {
	PLNCommons pc(as_);
	vector<Handle> candidates = pc.get_nodes(htarget, vector<Type>());
	map<Handle, HandleSeq> node_iset_map;
	//enforce not choosing two or more variables in the same Link
	for (auto it = candidates.begin(); it != candidates.end(); ++it) {
		HandleSeq hs = as_->getIncoming(*it);
		if (distance(candidates.begin(), it) == 0) {
			node_iset_map[*it] = hs;
		} else {
			bool has_same_link = false;
			for (auto i = node_iset_map.begin(); i != node_iset_map.end();
					++i) {
				HandleSeq tmp;
				sort(hs.begin(), hs.end());
				sort(i->second.begin(), i->second.end());
				set_intersection(hs.begin(), hs.end(), i->second.begin(),
						i->second.end(), back_inserter((tmp)));
				if (tmp.size() > 0) {
					has_same_link = true;
					break;
				}
			}
			if (!has_same_link)
				node_iset_map[*it] = hs;
		}
	}
	map<Handle, string> hnode_vname_map;
	for (auto it = node_iset_map.begin(); it != node_iset_map.end(); ++it) {
		Handle h = it->first;
		hnode_vname_map[h] = ("$var-" + NodeCast((h))->getName());
	}
	return hnode_vname_map;
}

Handle DefaultForwardChainerCB::target_to_pmimplicant(Handle htarget,
		map<Handle, string> hnode_vname_map)
{
	if (LinkCast(htarget)) {
	   HandleSeq hsvariablized;
		LinkPtr p_htarget = LinkCast(htarget);
		Type link_type = p_htarget->getType();
		HandleSeq hsoutgoing = as_->getOutgoing(htarget);
		for (auto i = hsoutgoing.begin(); i != hsoutgoing.end(); ++i) {
			Handle htmp = target_to_pmimplicant(*i, hnode_vname_map);
			hsvariablized.push_back(htmp);
		}
		Handle h(as_->addLink(link_type, hsvariablized));
		h->setTruthValue(TruthValue::TRUE_TV());
		return h;
	} else {
		if (NodeCast(htarget)) {
			auto it_var = hnode_vname_map.find(htarget); //TODO replace by find-if for linear complexity
			NodePtr p_htarget = NodeCast(htarget);
			if (it_var != hnode_vname_map.end()) {
				Handle h = as_->addNode(VARIABLE_NODE, it_var->second);
				h->setTruthValue(TruthValue::TRUE_TV());
            return h;
         }
			else
				return htarget;
		}
	}
	return Handle::UNDEFINED; //unreachable?
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Xiang Zhang. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "thunder/tensor.hpp"

#include <complex>
#include <memory>
#include <typeinfo>

#include "gtest/gtest.h"
#include "thunder/exception.hpp"
#include "thunder/storage.hpp"

namespace thunder {
namespace {

#define EXPECT_COMPLEX_EQ(x, y)                         \
  EXPECT_FLOAT_EQ(::std::real(x), ::std::real(y));      \
  EXPECT_FLOAT_EQ(::std::imag(x), ::std::imag(y));

template< typename T >
void reductionTest() {
  T t1(10, 20, 7);
  int t1_val_real = -788;
  int t1_val_imag = 801;
  typename T::value_type t1_sum(0, 0);
  typename T::value_type t1_prod(1, 0);
  typename T::value_type t1_mean(0, 0);
  for (typename T::reference_iterator begin = t1.reference_begin(),
           end = t1.reference_end(); begin != end; ++begin) {
    *begin = typename T::value_type(t1_val_real++, t1_val_imag--) /
        static_cast< typename T::value_type >(700);
    t1_sum += *begin;
    t1_prod *= *begin;
  }
  t1_mean = t1_sum / static_cast< typename T::value_type >(t1.length());
  EXPECT_COMPLEX_EQ(t1_sum, T::sum(t1));
  EXPECT_COMPLEX_EQ(t1_prod, T::prod(t1));
  EXPECT_COMPLEX_EQ(t1_mean, T::mean(t1));
  typename T::value_type t1_var(0, 0);
  typename T::value_type t1_std(0, 0);
  for (typename T::reference_iterator begin = t1.reference_begin(),
           end = t1.reference_end(); begin != end; ++begin) {
    t1_var += (*begin - t1_mean) * ::std::conj(*begin - t1_mean);
  }
  t1_var /= static_cast< typename T::value_type >(t1.length());
  t1_std = ::std::sqrt(t1_var);
  EXPECT_COMPLEX_EQ(t1_var, T::var(t1));
  EXPECT_COMPLEX_EQ(t1_std, T::std(t1));

  T t2({10, 20, 7}, {161, 8, 1});
  int t2_val_real = -778;
  int t2_val_imag = 811;
  typename T::value_type t2_sum(0, 0);
  typename T::value_type t2_prod(1, 0);
  typename T::value_type t2_mean(0, 0);
  for (typename T::reference_iterator begin = t2.reference_begin(),
           end = t2.reference_end(); begin != end; ++begin) {
    *begin = typename T::value_type(t2_val_real++, t2_val_imag--) /
        static_cast< typename T::value_type >(700);
    t2_sum += *begin;
    t2_prod *= *begin;
  }
  t2_mean = t2_sum / static_cast< typename T::value_type >(t2.length());
  EXPECT_COMPLEX_EQ(t2_sum, T::sum(t2));
  EXPECT_COMPLEX_EQ(t2_prod, T::prod(t2));
  EXPECT_COMPLEX_EQ(t2_mean, T::mean(t2));
  typename T::value_type t2_var(0, 0);
  typename T::value_type t2_std(0, 0);
  for (typename T::reference_iterator begin = t2.reference_begin(),
           end = t2.reference_end(); begin != end; ++begin) {
    t2_var += (*begin - t2_mean) * ::std::conj(*begin - t2_mean);
  }
  t2_var /= static_cast< typename T::value_type >(t2.length());
  t2_std = ::std::sqrt(t2_var);
  EXPECT_COMPLEX_EQ(t2_var, T::var(t2));
  EXPECT_COMPLEX_EQ(t2_std, T::std(t2));
}

TEST(ComplexTest, reductionTest) {
  reductionTest< DoubleComplexTensor >();
  reductionTest< FloatComplexTensor >();
}

#define TEST_DIM_REDUCTION(func)                                        \
  template < typename T >                                               \
  void func ## Test() {                                                 \
    T t1(10, 20, 7);                                                    \
    int t1_val_real = -788;                                             \
    int t1_val_imag = 801;                                              \
    for (typename T::reference_iterator begin = t1.reference_begin(),   \
             end = t1.reference_end(); begin != end; ++begin) {         \
      *begin = typename T::value_type(t1_val_real++, t1_val_imag--) /   \
          static_cast< typename T::value_type >(300);                   \
    }                                                                   \
                                                                        \
    T t1_result = T::func(t1, 1);                                       \
    EXPECT_EQ(3, t1_result.dimension());                                \
    EXPECT_EQ(10, t1_result.size(0));                                   \
    EXPECT_EQ(1, t1_result.size(1));                                    \
    EXPECT_EQ(7, t1_result.size(2));                                    \
    for (int i = 0; i < 10; ++i) {                                      \
      for (int j = 0; j < 7; ++j) {                                     \
        EXPECT_EQ(t1[i].select(1, j).func(), t1_result(i, 0, j));       \
      }                                                                 \
    }                                                                   \
                                                                        \
    T t2({10, 20, 7, 9}, {1431, 71, 10, 1});                            \
    int t2_val_real = -754;                                             \
    int t2_val_imag = 903;                                              \
    for (typename T::reference_iterator begin = t2.reference_begin(),   \
             end = t2.reference_end(); begin != end; ++begin) {         \
      *begin = typename T::value_type(t2_val_real++, t2_val_imag--) /   \
          static_cast< typename T::value_type >(300);                   \
    }                                                                   \
                                                                        \
    T t2_result = T::func(t2, 1);                                       \
    EXPECT_EQ(4, t2_result.dimension());                                \
    EXPECT_EQ(10, t2_result.size(0));                                   \
    EXPECT_EQ(1, t2_result.size(1));                                    \
    EXPECT_EQ(7, t2_result.size(2));                                    \
    EXPECT_EQ(9, t2_result.size(3));                                    \
    for (int i = 0; i < 10; ++i) {                                      \
      for (int j = 0; j < 7; ++j) {                                     \
        for (int k = 0; k < 9; ++k) {                                   \
          EXPECT_EQ(t2[i].select(1, j).select(1, k).func(),             \
                    t2_result(i, 0, j, k));                             \
        }                                                               \
      }                                                                 \
    }                                                                   \
  }                                                                     \
  TEST(TensorTest, func ## Test) {                                      \
    func ## Test< DoubleComplexTensor >();                              \
    func ## Test< FloatComplexTensor >();                               \
  }

TEST_DIM_REDUCTION(sum);
TEST_DIM_REDUCTION(prod);
TEST_DIM_REDUCTION(mean);
TEST_DIM_REDUCTION(var);
TEST_DIM_REDUCTION(std);

}  // namespace
}  // namespace thunder
<commit_msg>Corrected test names<commit_after>/*
 * Copyright 2014 Xiang Zhang. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "thunder/tensor.hpp"

#include <complex>
#include <memory>
#include <typeinfo>

#include "gtest/gtest.h"
#include "thunder/exception.hpp"
#include "thunder/storage.hpp"

namespace thunder {
namespace {

#define EXPECT_COMPLEX_EQ(x, y)                         \
  EXPECT_FLOAT_EQ(::std::real(x), ::std::real(y));      \
  EXPECT_FLOAT_EQ(::std::imag(x), ::std::imag(y));

template< typename T >
void reductionTest() {
  T t1(10, 20, 7);
  int t1_val_real = -788;
  int t1_val_imag = 801;
  typename T::value_type t1_sum(0, 0);
  typename T::value_type t1_prod(1, 0);
  typename T::value_type t1_mean(0, 0);
  for (typename T::reference_iterator begin = t1.reference_begin(),
           end = t1.reference_end(); begin != end; ++begin) {
    *begin = typename T::value_type(t1_val_real++, t1_val_imag--) /
        static_cast< typename T::value_type >(700);
    t1_sum += *begin;
    t1_prod *= *begin;
  }
  t1_mean = t1_sum / static_cast< typename T::value_type >(t1.length());
  EXPECT_COMPLEX_EQ(t1_sum, T::sum(t1));
  EXPECT_COMPLEX_EQ(t1_prod, T::prod(t1));
  EXPECT_COMPLEX_EQ(t1_mean, T::mean(t1));
  typename T::value_type t1_var(0, 0);
  typename T::value_type t1_std(0, 0);
  for (typename T::reference_iterator begin = t1.reference_begin(),
           end = t1.reference_end(); begin != end; ++begin) {
    t1_var += (*begin - t1_mean) * ::std::conj(*begin - t1_mean);
  }
  t1_var /= static_cast< typename T::value_type >(t1.length());
  t1_std = ::std::sqrt(t1_var);
  EXPECT_COMPLEX_EQ(t1_var, T::var(t1));
  EXPECT_COMPLEX_EQ(t1_std, T::std(t1));

  T t2({10, 20, 7}, {161, 8, 1});
  int t2_val_real = -778;
  int t2_val_imag = 811;
  typename T::value_type t2_sum(0, 0);
  typename T::value_type t2_prod(1, 0);
  typename T::value_type t2_mean(0, 0);
  for (typename T::reference_iterator begin = t2.reference_begin(),
           end = t2.reference_end(); begin != end; ++begin) {
    *begin = typename T::value_type(t2_val_real++, t2_val_imag--) /
        static_cast< typename T::value_type >(700);
    t2_sum += *begin;
    t2_prod *= *begin;
  }
  t2_mean = t2_sum / static_cast< typename T::value_type >(t2.length());
  EXPECT_COMPLEX_EQ(t2_sum, T::sum(t2));
  EXPECT_COMPLEX_EQ(t2_prod, T::prod(t2));
  EXPECT_COMPLEX_EQ(t2_mean, T::mean(t2));
  typename T::value_type t2_var(0, 0);
  typename T::value_type t2_std(0, 0);
  for (typename T::reference_iterator begin = t2.reference_begin(),
           end = t2.reference_end(); begin != end; ++begin) {
    t2_var += (*begin - t2_mean) * ::std::conj(*begin - t2_mean);
  }
  t2_var /= static_cast< typename T::value_type >(t2.length());
  t2_std = ::std::sqrt(t2_var);
  EXPECT_COMPLEX_EQ(t2_var, T::var(t2));
  EXPECT_COMPLEX_EQ(t2_std, T::std(t2));
}

TEST(ComplexTest, reductionTest) {
  reductionTest< DoubleComplexTensor >();
  reductionTest< FloatComplexTensor >();
}

#define TEST_DIM_REDUCTION(func)                                        \
  template < typename T >                                               \
  void func ## Test() {                                                 \
    T t1(10, 20, 7);                                                    \
    int t1_val_real = -788;                                             \
    int t1_val_imag = 801;                                              \
    for (typename T::reference_iterator begin = t1.reference_begin(),   \
             end = t1.reference_end(); begin != end; ++begin) {         \
      *begin = typename T::value_type(t1_val_real++, t1_val_imag--) /   \
          static_cast< typename T::value_type >(300);                   \
    }                                                                   \
                                                                        \
    T t1_result = T::func(t1, 1);                                       \
    EXPECT_EQ(3, t1_result.dimension());                                \
    EXPECT_EQ(10, t1_result.size(0));                                   \
    EXPECT_EQ(1, t1_result.size(1));                                    \
    EXPECT_EQ(7, t1_result.size(2));                                    \
    for (int i = 0; i < 10; ++i) {                                      \
      for (int j = 0; j < 7; ++j) {                                     \
        EXPECT_EQ(t1[i].select(1, j).func(), t1_result(i, 0, j));       \
      }                                                                 \
    }                                                                   \
                                                                        \
    T t2({10, 20, 7, 9}, {1431, 71, 10, 1});                            \
    int t2_val_real = -754;                                             \
    int t2_val_imag = 903;                                              \
    for (typename T::reference_iterator begin = t2.reference_begin(),   \
             end = t2.reference_end(); begin != end; ++begin) {         \
      *begin = typename T::value_type(t2_val_real++, t2_val_imag--) /   \
          static_cast< typename T::value_type >(300);                   \
    }                                                                   \
                                                                        \
    T t2_result = T::func(t2, 1);                                       \
    EXPECT_EQ(4, t2_result.dimension());                                \
    EXPECT_EQ(10, t2_result.size(0));                                   \
    EXPECT_EQ(1, t2_result.size(1));                                    \
    EXPECT_EQ(7, t2_result.size(2));                                    \
    EXPECT_EQ(9, t2_result.size(3));                                    \
    for (int i = 0; i < 10; ++i) {                                      \
      for (int j = 0; j < 7; ++j) {                                     \
        for (int k = 0; k < 9; ++k) {                                   \
          EXPECT_EQ(t2[i].select(1, j).select(1, k).func(),             \
                    t2_result(i, 0, j, k));                             \
        }                                                               \
      }                                                                 \
    }                                                                   \
  }                                                                     \
  TEST(ComplexTest, func ## Test) {                                     \
    func ## Test< DoubleComplexTensor >();                              \
    func ## Test< FloatComplexTensor >();                               \
  }

TEST_DIM_REDUCTION(sum);
TEST_DIM_REDUCTION(prod);
TEST_DIM_REDUCTION(mean);
TEST_DIM_REDUCTION(var);
TEST_DIM_REDUCTION(std);

}  // namespace
}  // namespace thunder
<|endoftext|>


<commit_before>/// @file spsc/queue.hpp
/// @brief Contains definition of single producer/single consumer thread-safe
///        lock-free fixed-size shared queue.
/// @copyright Licensed under the MIT License
/// @author Rostislav Ostapenko (rostislav.ostapenko@gmail.com)
/// @date 6-Apr-2014

#pragma once

#include <atomic>
#include <chrono>
#include <memory>
#include <stdexcept>

#include <boost/assert.hpp>
#include <boost/circular_buffer.hpp>
#include <boost/version.hpp>

// Unfortunately on Windows std::high_resolution_clock is not accurate enough, so using another
// bicyle implementation from StackOverflow.
#include "high_res_clock.hpp"

// Check Boost version. We need at least 1.55 to run properly.
#if BOOST_VERSION < 105500
#error This code has been developed and tested with Boost 1.55. It's not guaranteed to compile and run with earlier Boost versions.
#endif

namespace xcon
{

namespace spsc
{

// Fwd declaration of unit tests utility struct.
namespace queue_ut
{
   struct invariants_checker;
}

/// Thread-safe (single producer/single consumer) lock-free fixed-size queue.
///
/// @note The queue owns the elements that it contains.
///
///       Implementation idea:
///       Queue algorithm is lock-free and for several cases wait-free.
///       Selected solution is based on 2 independent preallocated queues - for producer and
///       consumer thread respectively. Each internal queue preallocates memory size equivalent
///       to queue size passed on construction.
///       Drawback is doubled memory consumption, but the benefit is good performance and
///       simplier algorithm. boost::circular_buffer is used for internal queues - it has
///       push_back()/pop_front() operations of constant time complexity.
///
///       Ideas to improve: parametrize with Allocator to be able to use custom allocators
///       with memory pools etc.
template <typename T> class queue
{
public:

   /// @enum YieldPolicy
   /// Describes how to yield CPU time in wait loop.
   enum yield_policy
   {
       spinning, ///< Don't yield CPU time in wait loop, just spin.
       yielding, ///< Yield CPU time in wait loop.
       sleeping  ///< Use sleep() in wait loop.
   };

   /// Constructor.
   ///
   /// @param[in] size Queue size.
   /// @throws std::length_error if provided size is too big or zero
   ///         std::bad_alloc if underlying container unable to allocate memory
   ///         block of requested size.
   explicit queue(size_t size, yield_policy policy = spinning)
      : yield_cpu_time_(
           policy == spinning ? yield_function([](){}) :
           (policy == yielding ? yield_function([](){ std::this_thread::yield(); }) :
              yield_function([](){ std::this_thread::sleep_for(std::chrono::milliseconds(1)); })))
      , max_size_(size)
      , count_(0)
      , queue1_(align_capacity_to_cache_line(size))
      , queue2_(align_capacity_to_cache_line(size))
      , producer_queue_(&queue1_)
      , consumer_queue_(&queue2_)
      , in_enqueue_(false)
      , in_dequeue_(false)
   {
      // Ensure queue storages are aligned to cache line size.
      BOOST_ASSERT(queue1_.capacity() * sizeof(raw_ptr_type) % cache_line_size == 0);
      BOOST_ASSERT(queue2_.capacity() * sizeof(raw_ptr_type) % cache_line_size == 0);

      if(size == 0)
      {
         throw std::length_error("Zero-sized queue is not allowed");
      }

      if(queue1_.capacity() < size)
      {
         // Oops, got overflow with alignment.
         throw std::length_error("Queue size is too big");
      }
   }

   /// Destructor.
   ///
   /// @note Destructor is NOT thread-safe, it's responsibility of caller to ensure no other
   ///       threads are accessing this object.
   ~queue()
   {
      destroy_objects(queue1_);
      destroy_objects(queue2_);
   }

   /// Prohibit copying.
   queue(const queue<T>&) = delete;
   queue<T>& operator=(const queue<T>&) = delete;

   /// Returns the number of elements contained in the Queue.
   size_t count() const
   {
      return count_;
   }

   /// Puts the item into the queue.
   ///
   /// @param[in] item Source item to put. Cannot be NULL.
   /// @note If the queue is full then this method blocks until there is the room
   ///       for the item again.
   ///       Item is owned by queue in case of success.
   ///       In case of failure item is not owned (not placed to queue) and it's responsibility
   ///       of caller to manage it.
   /// @throws std::invalid_argument if item is NULL.
   ///         std::runtime_error if reentrance is detected (more than one thread
   ///         called enqueue() for same object the same time).
   void enqueue(T* item)
   {
      enqueue_impl(item, wait_eternally);
   }

   /// Puts the item into the queue.
   ///
   /// @param[in] item Source item to put. Cannot be NULL.
   /// @param[in] milliseconds_timeout Number of milliseconds to wait.
   ///
   /// @returns 'true' if the operation was completed successfully, 'false'
   ///         if the operation timed out.
   ///
   /// @note If the queue is full then this method blocks until there is the room
   ///       for the item again or the operation timed out.
   ///       Item is owned by queue in case of success.
   ///       In case of failure item is not owned (not placed to queue) and it's responsibility
   ///       of caller to manage it.
   /// @throws std::invalid_argument if item is NULL.
   ///         std::runtime_error if reentrance is detected (more than one thread
   ///         called enqueue() for same object the same time).
   bool enqueue(T* item, int milliseconds_timeout)
   {
      return enqueue_impl(item, milliseconds_timeout);
   }

   /// Removes and returns the item at the beginning of the Queue.
   ///
   /// @note if the queue is empty then this method blocks until there is an item again.
   ///       Item is owned by caller in case of success.
   /// @throws std::runtime_error if reentrance is detected (more than one thread
   ///         called dequeue() for same object the same time).
   T* dequeue()
   {
      return dequeue_impl(wait_eternally);
   }

   /// Removes and returns the item at the beginning of the Queue.
   ///
   /// @param[in] milliseconds_timeout Number of milliseconds to wait.
   ///
   /// @returns The item at the betting of the Queue or NULL if the operation timed out.
   ///
   /// @note if the queue is empty then this method blocks until there is an item again
   ///       or the operation timed out.
   ///       Item is owned by caller in case of success.
   /// @throws std::runtime_error if reentrance is detected (more than one thread
   ///         called dequeue() for same object the same time).
   T* dequeue(int milliseconds_timeout)
   {
      return dequeue_impl(milliseconds_timeout);
   }

private:

   /// Internal types
   using value_type         = T;
   using raw_ptr_type       = T*;
   using smart_ptr_type     = std::unique_ptr<value_type>;
   using container_type     = boost::circular_buffer<raw_ptr_type>;
   using container_ptr_type = container_type*;
   using container_ref_type = container_type&;
   using reentrance_flag    = std::atomic<bool>;
   using yield_function     = void(*)();

   /// Implements enqueue() operation.
   ///
   /// @see enqueue()
   ///
   /// @note Special value (wait_eternally) can be passed as timeout to wait
   /// without timeout (eternally).
   bool enqueue_impl(T* item, int timeout_ms)
   {
      if(item == nullptr)
      {
         throw std::invalid_argument("NULL items not allowed");
      }

      reentrance_guard re_guard(in_enqueue_);
      smart_ptr_type item_guard(item);

      const size_t count = count_;
      if(count == max_size_)
      {
         // Queue is full, wait for dequeue or exit on timeout
         if(!wait_for_count_change(max_size_, timeout_ms))
         {
            // Release item, we don't want to loose it. It's responsibility of caller to manage
            // it in case of timeout
            item_guard.release();
            return false;
         }
      }
      else if(count == 0)
      {
         // It's safe to write directly into consumer queue because we know
         // that consumer thread is waiting for count change or even not entered dequeue()
         consumer_queue_->push_back(item_guard.get());
         item_guard.release();
         ++count_;
         return true;
      }

      // Protect producer queue to ensure consumer will not swap queues while
      // we are updating producer queue.
      // Get the producer queue and swap it with null.
      container_ref_type queue = *producer_queue_.exchange(nullptr);

      // Update producer queue.
      queue.push_back(item_guard.get());
      item_guard.release();

      // Restore shared producer queue ptr and increase item counter.
      producer_queue_ = &queue;
      ++count_;
      return true;
   }

   /// Implements dequeue() operation.
   ///
   /// @see dequeue()
   ///
   /// @note Special value (wait_eternally) can be passed as timeout to wait
   /// without timeout (eternally).
   T* dequeue_impl(int timeout_ms)
   {
      reentrance_guard re_guard(in_dequeue_);
      if(count_ == 0)
      {
         // Queue is empty, wait for enqueue or exit on timeout.
         if(!wait_for_count_change(0, timeout_ms))
         {
            return nullptr;
         }
      }

      // Consumer queue isn't empty - no need to sync, just dequeue and return.
      if(!consumer_queue_->empty())
      {
         return dequeue_from_consumer_queue().release();
      }

      // Consumer queue is empty but there are items in producer queue (because count_ is > 0).
      // Let's swap consumer and producer queue in lock-free style
      const container_ptr_type init_producer_queue = get_opposite_queue(consumer_queue_);
      container_ptr_type producer_queue = init_producer_queue;
      while(!producer_queue_.compare_exchange_strong(producer_queue, consumer_queue_))
      {
         // producer_queue can be overriden by nullptr if producer is updating now,
         // restore expected value.
         producer_queue = init_producer_queue;
      }

      // Now we swapped queues successfully, continue with dequeing from new consumer queue.
      consumer_queue_ = init_producer_queue;
      return dequeue_from_consumer_queue().release();
   }

   /// Gets opposite queue - consumer one for producer one and vice-versa.
   ///
   /// @param[in] queue Source queue.
   ///
   /// @returns Queue opposite to source.
   container_ptr_type get_opposite_queue(container_ptr_type const queue)
   {
      BOOST_ASSERT(queue == &queue1_ || queue == &queue2_);
      return queue == &queue1_ ? &queue2_ : &queue1_;
   }

   static const int wait_eternally = -1; ///< Magic timeout value meaning no timeout shall expire.

   /// Waits until queue item count is changed.
   ///
   /// @param[in] cur_count Current item count.
   /// @param[in] timeout_ms Timeout to wait in milliseconds, wait_eternally to wait eternally.
   ///
   /// @returns 'true' if count has been changed, 'false' if not and timeout expired.
   bool wait_for_count_change(size_t cur_count, int timeout_ms) const
   {
      BOOST_ASSERT(timeout_ms > 0 || timeout_ms == wait_eternally);

      using namespace std::chrono;
      const auto start_time = std_ex::high_resolution_clock::now();
      while(count_ == cur_count)
      {
         yield_cpu_time_();
         if(timeout_ms != wait_eternally)
         {
            const auto end_time = std_ex::high_resolution_clock::now();
            const milliseconds timeout = duration_cast<milliseconds>(end_time - start_time);
            if(timeout.count() >= timeout_ms)
            {
               return count_ != cur_count; // Last chance to not fail
            }
         }
      }
      return true;
   }

   /// Dequeue one item from consumer queue. Queue must be non-empty.
   ///
   /// @returns Smart pointer owning dequeued item.
   smart_ptr_type dequeue_from_consumer_queue()
   {
      BOOST_ASSERT(!consumer_queue_->empty());
      smart_ptr_type item_guard(consumer_queue_->front());
      consumer_queue_->pop_front();
      --count_;
      return item_guard;
   }

   /// Destroys items remained in queue.
   ///
   /// @param[in,out] queue Queue object to destroy items from.
   void destroy_objects(container_ref_type queue)
   {
      for(auto p : queue)
      {
         // If we need to use memory pool with queued items, we can use standard Allocator idiom
         // (http://en.cppreference.com/w/cpp/memory/allocator) - parametrize queue
         // template with custom memory pool Allocator trait type and
         // use Allocator::destroy() / Allocator::deallocate() instead of raw delete.
         delete p;
      }
   }

   /// Simple RAII reentrance guard.
   struct reentrance_guard
   {
      /// Constructs reentrance guard and sets protection flag.
      ///
      /// @throws std::runtime_error if reentrance detected.
      reentrance_guard(reentrance_flag& reentranceFlag): reentrance_flag_(reentranceFlag)
      {
         if(reentrance_flag_)
         {
            throw std::runtime_error("Reentrance not allowed. Only one thread at time "
               "can call enqueue() or dequeue()");
         }
         reentrance_flag_ = true;
      }

      /// Destructs reentrance guard and resets protection flag.
      ~reentrance_guard()
      {
         reentrance_flag_ = false;
      }

      reentrance_guard(const reentrance_guard&) = delete;
      reentrance_guard& operator=(const reentrance_guard&) = delete;

      reentrance_flag& reentrance_flag_; ///< Protection flag reference.
   };

   /// Friend utility class giving UTs access to invariants checking.
   friend struct queue_ut::invariants_checker;

   /// Checks queue invariants, asserts if something is going wrong. Used in UTs only.
   void check_invariants() const
   {
      BOOST_ASSERT(!in_dequeue_);
      BOOST_ASSERT(!in_enqueue_);
      BOOST_ASSERT(count_ <= max_size_);
      BOOST_ASSERT(count_ == (queue1_.size() + queue2_.size()));
      BOOST_ASSERT((consumer_queue_ == &queue1_ && producer_queue_ == &queue2_) ||
         (consumer_queue_ == &queue2_ && producer_queue_ == &queue1_));
   }

   /// Cache line size & corresponding padding type (to ensure padded data members
   /// will not be false-shared).
   static const size_t cache_line_size = 64;
   typedef char cache_line_padding[cache_line_size];

   /// Aligns capacity of queues to cache line size (to ensure it will not be false-shared
   /// with another queue).
   ///
   /// @param size[in] Initial capacity (internal queue size in items).
   ///
   /// @returns Aligned capacity.
   static size_t align_capacity_to_cache_line(size_t size)
   {
      size *= sizeof(raw_ptr_type);
      return (size + cache_line_size - size % cache_line_size) / sizeof(raw_ptr_type);
   }

   cache_line_padding pad1_;
   const yield_function yield_cpu_time_; ///< Function to yield CPU time.
   const size_t max_size_;               ///< Maximal size of queue.

   cache_line_padding pad2_;
   std::atomic_size_t count_; ///< Current size of queue. Shared between threads.

   cache_line_padding pad3_;
   container_type queue1_; ///< First internal queue.

   cache_line_padding pad4_;
   container_type queue2_; ///< Second internal queue.

   cache_line_padding pad5_;
   std::atomic<container_ptr_type> producer_queue_; ///< Producer queue pointer. Shared between threads.

   cache_line_padding pad6_;
   container_ptr_type consumer_queue_; ///< Consumer queue pointer.

   cache_line_padding pad7_;
   reentrance_flag in_enqueue_; ///< Enqueue reentrance protection flag.

   cache_line_padding pad8_;
   reentrance_flag in_dequeue_; ///< Dequeue reentrance protection flag.

}; // class queue

} // namespace spsc

} // namespace xcon
<commit_msg>gcc warning fix.<commit_after>/// @file spsc/queue.hpp
/// @brief Contains definition of single producer/single consumer thread-safe
///        lock-free fixed-size shared queue.
/// @copyright Licensed under the MIT License
/// @author Rostislav Ostapenko (rostislav.ostapenko@gmail.com)
/// @date 6-Apr-2014

#pragma once

#include <atomic>
#include <chrono>
#include <memory>
#include <stdexcept>

#include <boost/assert.hpp>
#include <boost/circular_buffer.hpp>
#include <boost/version.hpp>

// Unfortunately on Windows std::high_resolution_clock is not accurate enough, so using another
// bicyle implementation from StackOverflow.
#include "high_res_clock.hpp"

// Check Boost version. We need at least 1.55 to run properly.
#if BOOST_VERSION < 105500
#error This code has been developed and tested with Boost 1.55. Not guaranteed to compile and run with earlier Boost versions.
#endif

namespace xcon
{

namespace spsc
{

// Fwd declaration of unit tests utility struct.
namespace queue_ut
{
   struct invariants_checker;
}

/// Thread-safe (single producer/single consumer) lock-free fixed-size queue.
///
/// @note The queue owns the elements that it contains.
///
///       Implementation idea:
///       Queue algorithm is lock-free and for several cases wait-free.
///       Selected solution is based on 2 independent preallocated queues - for producer and
///       consumer thread respectively. Each internal queue preallocates memory size equivalent
///       to queue size passed on construction.
///       Drawback is doubled memory consumption, but the benefit is good performance and
///       simplier algorithm. boost::circular_buffer is used for internal queues - it has
///       push_back()/pop_front() operations of constant time complexity.
///
///       Ideas to improve: parametrize with Allocator to be able to use custom allocators
///       with memory pools etc.
template <typename T> class queue
{
public:

   /// @enum YieldPolicy
   /// Describes how to yield CPU time in wait loop.
   enum yield_policy
   {
       spinning, ///< Don't yield CPU time in wait loop, just spin.
       yielding, ///< Yield CPU time in wait loop.
       sleeping  ///< Use sleep() in wait loop.
   };

   /// Constructor.
   ///
   /// @param[in] size Queue size.
   /// @throws std::length_error if provided size is too big or zero
   ///         std::bad_alloc if underlying container unable to allocate memory
   ///         block of requested size.
   explicit queue(size_t size, yield_policy policy = spinning)
      : yield_cpu_time_(
           policy == spinning ? yield_function([](){}) :
           (policy == yielding ? yield_function([](){ std::this_thread::yield(); }) :
              yield_function([](){ std::this_thread::sleep_for(std::chrono::milliseconds(1)); })))
      , max_size_(size)
      , count_(0)
      , queue1_(align_capacity_to_cache_line(size))
      , queue2_(align_capacity_to_cache_line(size))
      , producer_queue_(&queue1_)
      , consumer_queue_(&queue2_)
      , in_enqueue_(false)
      , in_dequeue_(false)
   {
      // Ensure queue storages are aligned to cache line size.
      BOOST_ASSERT(queue1_.capacity() * sizeof(raw_ptr_type) % cache_line_size == 0);
      BOOST_ASSERT(queue2_.capacity() * sizeof(raw_ptr_type) % cache_line_size == 0);

      if(size == 0)
      {
         throw std::length_error("Zero-sized queue is not allowed");
      }

      if(queue1_.capacity() < size)
      {
         // Oops, got overflow with alignment.
         throw std::length_error("Queue size is too big");
      }
   }

   /// Destructor.
   ///
   /// @note Destructor is NOT thread-safe, it's responsibility of caller to ensure no other
   ///       threads are accessing this object.
   ~queue()
   {
      destroy_objects(queue1_);
      destroy_objects(queue2_);
   }

   /// Prohibit copying.
   queue(const queue<T>&) = delete;
   queue<T>& operator=(const queue<T>&) = delete;

   /// Returns the number of elements contained in the Queue.
   size_t count() const
   {
      return count_;
   }

   /// Puts the item into the queue.
   ///
   /// @param[in] item Source item to put. Cannot be NULL.
   /// @note If the queue is full then this method blocks until there is the room
   ///       for the item again.
   ///       Item is owned by queue in case of success.
   ///       In case of failure item is not owned (not placed to queue) and it's responsibility
   ///       of caller to manage it.
   /// @throws std::invalid_argument if item is NULL.
   ///         std::runtime_error if reentrance is detected (more than one thread
   ///         called enqueue() for same object the same time).
   void enqueue(T* item)
   {
      enqueue_impl(item, wait_eternally);
   }

   /// Puts the item into the queue.
   ///
   /// @param[in] item Source item to put. Cannot be NULL.
   /// @param[in] milliseconds_timeout Number of milliseconds to wait.
   ///
   /// @returns 'true' if the operation was completed successfully, 'false'
   ///         if the operation timed out.
   ///
   /// @note If the queue is full then this method blocks until there is the room
   ///       for the item again or the operation timed out.
   ///       Item is owned by queue in case of success.
   ///       In case of failure item is not owned (not placed to queue) and it's responsibility
   ///       of caller to manage it.
   /// @throws std::invalid_argument if item is NULL.
   ///         std::runtime_error if reentrance is detected (more than one thread
   ///         called enqueue() for same object the same time).
   bool enqueue(T* item, int milliseconds_timeout)
   {
      return enqueue_impl(item, milliseconds_timeout);
   }

   /// Removes and returns the item at the beginning of the Queue.
   ///
   /// @note if the queue is empty then this method blocks until there is an item again.
   ///       Item is owned by caller in case of success.
   /// @throws std::runtime_error if reentrance is detected (more than one thread
   ///         called dequeue() for same object the same time).
   T* dequeue()
   {
      return dequeue_impl(wait_eternally);
   }

   /// Removes and returns the item at the beginning of the Queue.
   ///
   /// @param[in] milliseconds_timeout Number of milliseconds to wait.
   ///
   /// @returns The item at the betting of the Queue or NULL if the operation timed out.
   ///
   /// @note if the queue is empty then this method blocks until there is an item again
   ///       or the operation timed out.
   ///       Item is owned by caller in case of success.
   /// @throws std::runtime_error if reentrance is detected (more than one thread
   ///         called dequeue() for same object the same time).
   T* dequeue(int milliseconds_timeout)
   {
      return dequeue_impl(milliseconds_timeout);
   }

private:

   /// Internal types
   using value_type         = T;
   using raw_ptr_type       = T*;
   using smart_ptr_type     = std::unique_ptr<value_type>;
   using container_type     = boost::circular_buffer<raw_ptr_type>;
   using container_ptr_type = container_type*;
   using container_ref_type = container_type&;
   using reentrance_flag    = std::atomic<bool>;
   using yield_function     = void(*)();

   /// Implements enqueue() operation.
   ///
   /// @see enqueue()
   ///
   /// @note Special value (wait_eternally) can be passed as timeout to wait
   /// without timeout (eternally).
   bool enqueue_impl(T* item, int timeout_ms)
   {
      if(item == nullptr)
      {
         throw std::invalid_argument("NULL items not allowed");
      }

      reentrance_guard re_guard(in_enqueue_);
      smart_ptr_type item_guard(item);

      const size_t count = count_;
      if(count == max_size_)
      {
         // Queue is full, wait for dequeue or exit on timeout
         if(!wait_for_count_change(max_size_, timeout_ms))
         {
            // Release item, we don't want to loose it. It's responsibility of caller to manage
            // it in case of timeout
            item_guard.release();
            return false;
         }
      }
      else if(count == 0)
      {
         // It's safe to write directly into consumer queue because we know
         // that consumer thread is waiting for count change or even not entered dequeue()
         consumer_queue_->push_back(item_guard.get());
         item_guard.release();
         ++count_;
         return true;
      }

      // Protect producer queue to ensure consumer will not swap queues while
      // we are updating producer queue.
      // Get the producer queue and swap it with null.
      container_ref_type queue = *producer_queue_.exchange(nullptr);

      // Update producer queue.
      queue.push_back(item_guard.get());
      item_guard.release();

      // Restore shared producer queue ptr and increase item counter.
      producer_queue_ = &queue;
      ++count_;
      return true;
   }

   /// Implements dequeue() operation.
   ///
   /// @see dequeue()
   ///
   /// @note Special value (wait_eternally) can be passed as timeout to wait
   /// without timeout (eternally).
   T* dequeue_impl(int timeout_ms)
   {
      reentrance_guard re_guard(in_dequeue_);
      if(count_ == 0)
      {
         // Queue is empty, wait for enqueue or exit on timeout.
         if(!wait_for_count_change(0, timeout_ms))
         {
            return nullptr;
         }
      }

      // Consumer queue isn't empty - no need to sync, just dequeue and return.
      if(!consumer_queue_->empty())
      {
         return dequeue_from_consumer_queue().release();
      }

      // Consumer queue is empty but there are items in producer queue (because count_ is > 0).
      // Let's swap consumer and producer queue in lock-free style
      const container_ptr_type init_producer_queue = get_opposite_queue(consumer_queue_);
      container_ptr_type producer_queue = init_producer_queue;
      while(!producer_queue_.compare_exchange_strong(producer_queue, consumer_queue_))
      {
         // producer_queue can be overriden by nullptr if producer is updating now,
         // restore expected value.
         producer_queue = init_producer_queue;
      }

      // Now we swapped queues successfully, continue with dequeing from new consumer queue.
      consumer_queue_ = init_producer_queue;
      return dequeue_from_consumer_queue().release();
   }

   /// Gets opposite queue - consumer one for producer one and vice-versa.
   ///
   /// @param[in] queue Source queue.
   ///
   /// @returns Queue opposite to source.
   container_ptr_type get_opposite_queue(container_ptr_type const queue)
   {
      BOOST_ASSERT(queue == &queue1_ || queue == &queue2_);
      return queue == &queue1_ ? &queue2_ : &queue1_;
   }

   static const int wait_eternally = -1; ///< Magic timeout value meaning no timeout shall expire.

   /// Waits until queue item count is changed.
   ///
   /// @param[in] cur_count Current item count.
   /// @param[in] timeout_ms Timeout to wait in milliseconds, wait_eternally to wait eternally.
   ///
   /// @returns 'true' if count has been changed, 'false' if not and timeout expired.
   bool wait_for_count_change(size_t cur_count, int timeout_ms) const
   {
      BOOST_ASSERT(timeout_ms > 0 || timeout_ms == wait_eternally);

      using namespace std::chrono;
      const auto start_time = std_ex::high_resolution_clock::now();
      while(count_ == cur_count)
      {
         yield_cpu_time_();
         if(timeout_ms != wait_eternally)
         {
            const auto end_time = std_ex::high_resolution_clock::now();
            const milliseconds timeout = duration_cast<milliseconds>(end_time - start_time);
            if(timeout.count() >= timeout_ms)
            {
               return count_ != cur_count; // Last chance to not fail
            }
         }
      }
      return true;
   }

   /// Dequeue one item from consumer queue. Queue must be non-empty.
   ///
   /// @returns Smart pointer owning dequeued item.
   smart_ptr_type dequeue_from_consumer_queue()
   {
      BOOST_ASSERT(!consumer_queue_->empty());
      smart_ptr_type item_guard(consumer_queue_->front());
      consumer_queue_->pop_front();
      --count_;
      return item_guard;
   }

   /// Destroys items remained in queue.
   ///
   /// @param[in,out] queue Queue object to destroy items from.
   void destroy_objects(container_ref_type queue)
   {
      for(auto p : queue)
      {
         // If we need to use memory pool with queued items, we can use standard Allocator idiom
         // (http://en.cppreference.com/w/cpp/memory/allocator) - parametrize queue
         // template with custom memory pool Allocator trait type and
         // use Allocator::destroy() / Allocator::deallocate() instead of raw delete.
         delete p;
      }
   }

   /// Simple RAII reentrance guard.
   struct reentrance_guard
   {
      /// Constructs reentrance guard and sets protection flag.
      ///
      /// @throws std::runtime_error if reentrance detected.
      reentrance_guard(reentrance_flag& reentranceFlag): reentrance_flag_(reentranceFlag)
      {
         if(reentrance_flag_)
         {
            throw std::runtime_error("Reentrance not allowed. Only one thread at time "
               "can call enqueue() or dequeue()");
         }
         reentrance_flag_ = true;
      }

      /// Destructs reentrance guard and resets protection flag.
      ~reentrance_guard()
      {
         reentrance_flag_ = false;
      }

      reentrance_guard(const reentrance_guard&) = delete;
      reentrance_guard& operator=(const reentrance_guard&) = delete;

      reentrance_flag& reentrance_flag_; ///< Protection flag reference.
   };

   /// Friend utility class giving UTs access to invariants checking.
   friend struct queue_ut::invariants_checker;

   /// Checks queue invariants, asserts if something is going wrong. Used in UTs only.
   void check_invariants() const
   {
      BOOST_ASSERT(!in_dequeue_);
      BOOST_ASSERT(!in_enqueue_);
      BOOST_ASSERT(count_ <= max_size_);
      BOOST_ASSERT(count_ == (queue1_.size() + queue2_.size()));
      BOOST_ASSERT((consumer_queue_ == &queue1_ && producer_queue_ == &queue2_) ||
         (consumer_queue_ == &queue2_ && producer_queue_ == &queue1_));
   }

   /// Cache line size & corresponding padding type (to ensure padded data members
   /// will not be false-shared).
   static const size_t cache_line_size = 64;
   typedef char cache_line_padding[cache_line_size];

   /// Aligns capacity of queues to cache line size (to ensure it will not be false-shared
   /// with another queue).
   ///
   /// @param size[in] Initial capacity (internal queue size in items).
   ///
   /// @returns Aligned capacity.
   static size_t align_capacity_to_cache_line(size_t size)
   {
      size *= sizeof(raw_ptr_type);
      return (size + cache_line_size - size % cache_line_size) / sizeof(raw_ptr_type);
   }

   cache_line_padding pad1_;
   const yield_function yield_cpu_time_; ///< Function to yield CPU time.
   const size_t max_size_;               ///< Maximal size of queue.

   cache_line_padding pad2_;
   std::atomic_size_t count_; ///< Current size of queue. Shared between threads.

   cache_line_padding pad3_;
   container_type queue1_; ///< First internal queue.

   cache_line_padding pad4_;
   container_type queue2_; ///< Second internal queue.

   cache_line_padding pad5_;
   std::atomic<container_ptr_type> producer_queue_; ///< Producer queue pointer. Shared between threads.

   cache_line_padding pad6_;
   container_ptr_type consumer_queue_; ///< Consumer queue pointer.

   cache_line_padding pad7_;
   reentrance_flag in_enqueue_; ///< Enqueue reentrance protection flag.

   cache_line_padding pad8_;
   reentrance_flag in_dequeue_; ///< Dequeue reentrance protection flag.

}; // class queue

} // namespace spsc

} // namespace xcon
<|endoftext|>


<commit_before>/*
 * mudObjects.cpp
 *   Functions that work on mudObjects etc
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
// Mud Includes
#include "mud.h"
#include "version.h"
#include "commands.h"
#include "effects.h"
#include "specials.h"
#include "calendar.h"
#include "quests.h"
#include "guilds.h"
#include "property.h"

// C++ includes
#include <sstream>
#include <iomanip>
#include <locale>
//#include <c++/4.3.3/bits/stl_list.h>
#include <list>

void MudObject::setName(bstring newName) {
	removeFromSet();
	name = newName;
	addToSet();
}

const bstring& MudObject::getName() const {
	return(name);
}
const char* MudObject::getCName() const {
	return(name.c_str());
}

void MudObject::removeFromSet() {

}
void MudObject::addToSet() {

}

bool PlayerPtrLess::operator()(const Player* lhs, const Player* rhs) const {
    return *lhs < *rhs;
}

bool MonsterPtrLess::operator()(const Monster* lhs, const Monster* rhs) const {
    bstring lhsStr = lhs->getName() + lhs->id;
    bstring rhsStr = rhs->getName() + rhs->id;
    return(lhsStr.compare(rhsStr) < 0);
}

bool ObjectPtrLess::operator()(const Object* lhs, const Object* rhs) const {
	return *lhs < *rhs;
}

//***********************************************************************
//						moReset
//***********************************************************************

void MudObject::moReset() {
	id = "-1";
	name = "";
}

//*********************************************************************
//						moCopy
//*********************************************************************

void MudObject::moCopy(const MudObject& mo) {
	name = mo.getName();

	hooks = mo.hooks;
	hooks.setParent(this);
}

//***********************************************************************
//						moDestroy
//***********************************************************************

void MudObject::moDestroy() {
	// If it's a valid ID and it's not a player, unregister their ID.  Players
	// are unregistered by the server
	if(!id.equals("-1") && !getAsPlayer()) {
		gServer->unRegisterMudObject(this);
	}
	moReset();
}

//***********************************************************************
//						getMonster
//***********************************************************************

Monster* MudObject::getAsMonster() {
    return(dynamic_cast<Monster*>(this));
}

//***********************************************************************
//						getCreature
//***********************************************************************

Creature* MudObject::getAsCreature() {
    return(dynamic_cast<Creature*>(this));
}

//***********************************************************************
//						getPlayer
//***********************************************************************

Player* MudObject::getAsPlayer() {
	return(dynamic_cast<Player*>(this));
}

//***********************************************************************
//						getObject
//***********************************************************************

Object* MudObject::getAsObject() {
	return(dynamic_cast<Object*>(this));
}

//***********************************************************************
//						getUniqueRoom
//***********************************************************************

UniqueRoom* MudObject::getAsUniqueRoom() {
	return(dynamic_cast<UniqueRoom*>(this));
}

//***********************************************************************
//						getAreaRoom
//***********************************************************************

AreaRoom* MudObject::getAsAreaRoom() {
	return(dynamic_cast<AreaRoom*>(this));
}

//***********************************************************************
//						getRoom
//***********************************************************************

BaseRoom* MudObject::getAsRoom() {
	return(dynamic_cast<BaseRoom*>(this));
}

//***********************************************************************
//						getExit
//***********************************************************************

Exit* MudObject::getAsExit() {
	return(dynamic_cast<Exit*>(this));
}

//***********************************************************************
//						getConstMonster
//***********************************************************************

const Monster* MudObject::getAsConstMonster() const {
	return(dynamic_cast<const Monster*>(this));
}

//***********************************************************************
//						getConstPlayer
//***********************************************************************

const Player* MudObject::getAsConstPlayer() const {
	return(dynamic_cast<const Player*>(this));
}

//***********************************************************************
//						getConstCreature
//***********************************************************************

const Creature* MudObject::getAsConstCreature() const {
	return(dynamic_cast<const Creature*>(this));
}

//***********************************************************************
//						getConstObject
//***********************************************************************

const Object* MudObject::getAsConstObject() const {
	return(dynamic_cast<const Object*>(this));
}

//***********************************************************************
//						getConstUniqueRoom
//***********************************************************************

const UniqueRoom* MudObject::getAsConstUniqueRoom() const {
	return(dynamic_cast<const UniqueRoom*>(this));
}

//***********************************************************************
//						getConstAreaRoom
//***********************************************************************

const AreaRoom* MudObject::getAsConstAreaRoom() const {
	return(dynamic_cast<const AreaRoom*>(this));
}

//***********************************************************************
//						getConstRoom
//***********************************************************************

const BaseRoom* MudObject::getAsConstRoom() const {
	return(dynamic_cast<const BaseRoom*>(this));
}

//***********************************************************************
//						getConstExit
//***********************************************************************

const Exit* MudObject::getAsConstExit() const {
	return(dynamic_cast<const Exit*>(this));
}

//***********************************************************************
//						equals
//***********************************************************************

bool MudObject::equals(MudObject* other) {
    return(this == other);
}

void MudObject::setId(bstring newId) {
	if(!id.equals("-1") && !newId.equals("-1")) {
	    throw std::runtime_error(bstring("Error, re-setting ID:") + getName() + ":" + getId() + ":" + newId);
	}
	bool handleParentSet = true;

	if(id.equals("-1"))
		handleParentSet = false;
	if(!newId.equals("")) {
		if(handleParentSet) removeFromSet();
		id = newId;
		if(handleParentSet) addToSet();
		// Register the ID with the server if we're not a player, handle player registration
		// when adding the player to the server's list of players
		if(!getAsPlayer()) {
			if(getAsCreature() && gServer->lookupCrtId(newId)) {
				// We already have a creature with this registered ID, so this creature needs a new ID
				id = gServer->getNextMonsterId();
				std::cout << "Changing ID for " << this->getName() << " from "<< newId << " to " << id << std::endl;
			} else if(getAsObject() && gServer->lookupObjId(newId)) {
				throw std::runtime_error("Duplicate Object ");

			}
			gServer->registerMudObject(this);
		}
	}
}

const bstring& MudObject::getId() const {
    return(id);
}

// Return for Python which doesn't like bstring&
bstring MudObject::getIdPython() const {
	return(id);
}

bool MudObject::isRoom() const {
	return(this &&
	  	  (typeid(*this) == typeid(BaseRoom) ||
		   typeid(*this) == typeid(UniqueRoom) ||
		   typeid(*this) == typeid(AreaRoom)));
}
bool MudObject::isUniqueRoom() const {
	return(this && typeid(*this) == typeid(UniqueRoom));
}
bool MudObject::isAreaRoom() const {
	return(this && typeid(*this) == typeid(AreaRoom));
}
bool MudObject::isObject() const {
	return(this && typeid(*this) == typeid(Object));
}
bool MudObject::isPlayer() const {
	return(this && typeid(*this) == typeid(Player));
}
bool MudObject::isMonster() const {
	return(this && typeid(*this) == typeid(Monster));
}
bool MudObject::isCreature() const {
	return(this &&
		  (typeid(*this) == typeid(Creature) ||
		   typeid(*this) == typeid(Player) ||
		   typeid(*this) == typeid(Monster)));
}
bool MudObject::isExit() const {
    return(this && typeid(*this) == typeid(Exit));
}

<commit_msg>Bugfix setId<commit_after>/*
 * mudObjects.cpp
 *   Functions that work on mudObjects etc
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
// Mud Includes
#include "mud.h"
#include "version.h"
#include "commands.h"
#include "effects.h"
#include "specials.h"
#include "calendar.h"
#include "quests.h"
#include "guilds.h"
#include "property.h"

// C++ includes
#include <sstream>
#include <iomanip>
#include <locale>
//#include <c++/4.3.3/bits/stl_list.h>
#include <list>

void MudObject::setName(bstring newName) {
	removeFromSet();
	name = newName;
	addToSet();
}

const bstring& MudObject::getName() const {
	return(name);
}
const char* MudObject::getCName() const {
	return(name.c_str());
}

void MudObject::removeFromSet() {

}
void MudObject::addToSet() {

}

bool PlayerPtrLess::operator()(const Player* lhs, const Player* rhs) const {
    return *lhs < *rhs;
}

bool MonsterPtrLess::operator()(const Monster* lhs, const Monster* rhs) const {
    bstring lhsStr = lhs->getName() + lhs->id;
    bstring rhsStr = rhs->getName() + rhs->id;
    return(lhsStr.compare(rhsStr) < 0);
}

bool ObjectPtrLess::operator()(const Object* lhs, const Object* rhs) const {
	return *lhs < *rhs;
}

//***********************************************************************
//						moReset
//***********************************************************************

void MudObject::moReset() {
	id = "-1";
	name = "";
}

//*********************************************************************
//						moCopy
//*********************************************************************

void MudObject::moCopy(const MudObject& mo) {
	name = mo.getName();

	hooks = mo.hooks;
	hooks.setParent(this);
}

//***********************************************************************
//						moDestroy
//***********************************************************************

void MudObject::moDestroy() {
	// If it's a valid ID and it's not a player, unregister their ID.  Players
	// are unregistered by the server
	if(!id.equals("-1") && !getAsPlayer()) {
		gServer->unRegisterMudObject(this);
	}
	moReset();
}

//***********************************************************************
//						getMonster
//***********************************************************************

Monster* MudObject::getAsMonster() {
    return(dynamic_cast<Monster*>(this));
}

//***********************************************************************
//						getCreature
//***********************************************************************

Creature* MudObject::getAsCreature() {
    return(dynamic_cast<Creature*>(this));
}

//***********************************************************************
//						getPlayer
//***********************************************************************

Player* MudObject::getAsPlayer() {
	return(dynamic_cast<Player*>(this));
}

//***********************************************************************
//						getObject
//***********************************************************************

Object* MudObject::getAsObject() {
	return(dynamic_cast<Object*>(this));
}

//***********************************************************************
//						getUniqueRoom
//***********************************************************************

UniqueRoom* MudObject::getAsUniqueRoom() {
	return(dynamic_cast<UniqueRoom*>(this));
}

//***********************************************************************
//						getAreaRoom
//***********************************************************************

AreaRoom* MudObject::getAsAreaRoom() {
	return(dynamic_cast<AreaRoom*>(this));
}

//***********************************************************************
//						getRoom
//***********************************************************************

BaseRoom* MudObject::getAsRoom() {
	return(dynamic_cast<BaseRoom*>(this));
}

//***********************************************************************
//						getExit
//***********************************************************************

Exit* MudObject::getAsExit() {
	return(dynamic_cast<Exit*>(this));
}

//***********************************************************************
//						getConstMonster
//***********************************************************************

const Monster* MudObject::getAsConstMonster() const {
	return(dynamic_cast<const Monster*>(this));
}

//***********************************************************************
//						getConstPlayer
//***********************************************************************

const Player* MudObject::getAsConstPlayer() const {
	return(dynamic_cast<const Player*>(this));
}

//***********************************************************************
//						getConstCreature
//***********************************************************************

const Creature* MudObject::getAsConstCreature() const {
	return(dynamic_cast<const Creature*>(this));
}

//***********************************************************************
//						getConstObject
//***********************************************************************

const Object* MudObject::getAsConstObject() const {
	return(dynamic_cast<const Object*>(this));
}

//***********************************************************************
//						getConstUniqueRoom
//***********************************************************************

const UniqueRoom* MudObject::getAsConstUniqueRoom() const {
	return(dynamic_cast<const UniqueRoom*>(this));
}

//***********************************************************************
//						getConstAreaRoom
//***********************************************************************

const AreaRoom* MudObject::getAsConstAreaRoom() const {
	return(dynamic_cast<const AreaRoom*>(this));
}

//***********************************************************************
//						getConstRoom
//***********************************************************************

const BaseRoom* MudObject::getAsConstRoom() const {
	return(dynamic_cast<const BaseRoom*>(this));
}

//***********************************************************************
//						getConstExit
//***********************************************************************

const Exit* MudObject::getAsConstExit() const {
	return(dynamic_cast<const Exit*>(this));
}

//***********************************************************************
//						equals
//***********************************************************************

bool MudObject::equals(MudObject* other) {
    return(this == other);
}

void MudObject::setId(bstring newId) {
	if(!id.equals("-1") && !newId.equals("-1")) {
	    throw std::runtime_error(bstring("Error, re-setting ID:") + getName() + ":" + getId() + ":" + newId);
	}
	bool handleParentSet = true;

	if(id.equals("-1"))
		handleParentSet = false;
	if(!newId.equals("")) {
		if(handleParentSet) removeFromSet();
		id = newId;
		if(handleParentSet) addToSet();
		// Register the ID with the server if we're not a player, handle player registration
		// when adding the player to the server's list of players
		if(!getAsPlayer()) {
			if(getAsCreature() && gServer->lookupCrtId(newId)) {
				// We already have a creature with this registered ID, so this creature needs a new ID
				id = gServer->getNextMonsterId();
				std::cout << "Changing ID for " << this->getName() << " from "<< newId << " to " << id << std::endl;
			} else if(getAsObject() && gServer->lookupObjId(newId)) {
				throw std::runtime_error("Duplicate Object ");

			}
			gServer->registerMudObject(this);
		}
	}
}

const bstring& MudObject::getId() const {
    return(id);n
}

// Return for Python which doesn't like bstring&
bstring MudObject::getIdPython() const {
	return(id);
}

bool MudObject::isRoom() const {
	return(this &&
	  	  (typeid(*this) == typeid(BaseRoom) ||
		   typeid(*this) == typeid(UniqueRoom) ||
		   typeid(*this) == typeid(AreaRoom)));
}
bool MudObject::isUniqueRoom() const {
	return(this && typeid(*this) == typeid(UniqueRoom));
}
bool MudObject::isAreaRoom() const {
	return(this && typeid(*this) == typeid(AreaRoom));
}
bool MudObject::isObject() const {
	return(this && typeid(*this) == typeid(Object));
}
bool MudObject::isPlayer() const {
	return(this && typeid(*this) == typeid(Player));
}
bool MudObject::isMonster() const {
	return(this && typeid(*this) == typeid(Monster));
}
bool MudObject::isCreature() const {
	return(this &&
		  (typeid(*this) == typeid(Creature) ||
		   typeid(*this) == typeid(Player) ||
		   typeid(*this) == typeid(Monster)));
}
bool MudObject::isExit() const {
    return(this && typeid(*this) == typeid(Exit));
}

<|endoftext|>


<commit_before>// Copyright (c) 2015 Andrew Sutton
// All rights reserved

#ifndef LINGO_MEMORY_HPP
#define LINGO_MEMORY_HPP

#include "lingo/debug.hpp"

#include <list>
#include <set>
#include <unordered_map>
#include <vector>

namespace lingo
{

class Integer;
class String_view;
class Token;

// -------------------------------------------------------------------------- //
//                            Factories

// A unique factory will only allocate new objects if
// they have not been previously created. A strict weak
// order is used to determine the uniqueness of objects.
template<typename T, typename C>
struct Unique_factory : std::set<T, C>
{
  // Create an object of type T.
  template<typename... Args>
  T* make(Args&&... args)
  {
    auto ins = this->emplace(std::forward<Args>(args)...);
    return const_cast<T*>(&*ins.first);
  }
};

// A singleton factory will only allocate a single
// object of the given type.
template<typename T>
struct Singleton_factory
{
  Singleton_factory()
    : object(nullptr)
  { }

  // Create an object of type T. If T has already been
  // created, the arguments given here must match those
  // given previously (or be empty).
  template<typename... Args>
  T* make(Args&&... args)
  {
    if (!object)
      object = new T(std::forward<Args>(args)...);
    return object;
  }

  T* object;
};

// -------------------------------------------------------------------------- //
//                          Garbage collector

class Collecting_factory;
class Reach;

// Every object allocated through the garbage collector is
// an instance of the collectable type.
struct Collectable
{
  Collectable()
    : marked(false)
  { }

  virtual ~Collectable() { }

  virtual void reach() = 0;

  bool marked;
};

// The following functions support the mark and sweep
// garbage collection algorithm. We define mark() for a
// large number of types, especially nodes. This provides
// a generic facility for traversing the common structure
// of those nodes.

// Catch node pointers that do not have overloads.
inline void
mark(void const*) { }

// Do not mark arithmetic values.
template<typename T>
inline typename std::enable_if<std::is_arithmetic<T>::value>::type
mark(T) { }

// Do not mark enumerations.
template<typename T>
inline typename std::enable_if<std::is_enum<T>::value>::type
mark(T) { }

// Do not mark integers.
inline void
mark(Integer const&) { }

// Do not mark strings.
inline void
mark(String_view const&) { }

// Do not mark tokens.
inline void
mark(Token const&) { }

// Mark an empty node.
template<typename T>
inline void
mark_nullary(T const* t)
{
  T* nc = const_cast<T*>(t);
  if (Collectable* c = dynamic_cast<Collectable*>(nc))
    c->marked = true;
}

// Mark a unary node.
template<typename T>
inline void
mark_unary(T const* t)
{
  mark_nullary(t);
  mark(t->first);
}

// Mark a binary node.
template<typename T>
inline void
mark_binary(T const* t)
{
  mark_unary(t);
  mark(t->second);
}

// Mark a ternary node.
template<typename T>
inline void
mark_ternary(T const* t)
{
  mark_binary(t);
  mark(t->third);
}

// The garbage factory class tracks each object that it allocates.
// At various phases of execution, the collect() function can be
// used to reclaim unreferenced memory.
//
// The garbage factory is designed to collect nodes.
//
// Note that the garbage collector is a global resource.
//
// TODO: The root set is not especially efficient since we have
// to traverse it (it's an unordered map).
class Collecting_factory
{
  // Make every T collectable.
  template<typename T>
  struct Collectable_type : T, Collectable
  {
    template<typename... Args>
    Collectable_type(Args&&... args)
      : T(std::forward<Args>(args)...), Collectable()
    { }

    void reach() override
    {
      using lingo::mark;
      mark(this);
    }
  };

public:
  // Create an object of type T. Note that T must be a node type.
  template<typename T, typename... Args>
  T* make(Args&&... args)
  {
    Collectable_type<T>* obj = new Collectable_type<T>(std::forward<Args>(args)...);
    objects.push_back(obj);
    return obj;
  }

  // Root management.
  void declare(Reach*);
  void undeclare(Reach*);

  void collect();

private:
  void mark();
  void sweep();

private:
  using Object_list = std::list<Collectable*>;
  using Root_set = std::unordered_map<Reach*, Reach*>;

  Object_list objects;
  Root_set    roots;
};

Collecting_factory& gc();

// The reach class declares a new root into the garbage
// collector. When this object is destroyed, it is no
// longer a collection root.
//
// Note that a GC root will only store references to
// collectable objects.
class Reach : std::vector<Collectable*>
{
  friend class Collecting_factory;
public:
  Reach()
  {
    gc().declare(this);
  }

  // Declare a new garbage collection root, initialized
  // with the given object.
  template<typename T>
  Reach(T* t)
    : Reach()
  {
    declare(t);
  }

  // Declare a new garbage collection root, initialized
  // with the given sequence of objects.
  template<typename T, typename... Args>
  Reach(T* t, Args*... args)
    : Reach(args...)
  {
    declare(t);
  }

  ~Reach()
  {
    gc().undeclare(this);
  }

  // Declare `u` to be reachable.
  template<typename T>
  void operator()(T* t)
  {
    declare(t);
  }

private:
  // Declare `t` as reachable. Note that this is only defined
  // for pointers to objects of polymorphic type. Otherwise,
  // don't track references.
  template<typename T>
  typename std::enable_if<std::is_polymorphic<T>::value>::type
  declare(T* t)
  {
    if (Collectable* obj = dynamic_cast<Collectable*>(t))
      push_back(obj);
  }

  template<typename T>
  typename std::enable_if<!std::is_polymorphic<T>::value>::type
  declare(T* t)
  {
  }

  // Mark all nodes reachable from this root with
  // the given value. This is used by the garbage
  // collector.
  void reach()
  {
    for (Collectable* r : *this)
      r->reach();
  }
};

// -------------------------------------------------------------------------- //
//                            Algorithms

// This function deletes the object pointed at by p sets it
// to nullptr, returning that value. This is primarily used
// to reduce reduntant code on certain error conditions during
// parsing and elaboration.
template<typename T>
inline T*
reset(T*& p)
{
  delete p;
  return p = nullptr;
}

} // namespace lingo

#endif
<commit_msg>Add mark algorithm for k-ary nodes.<commit_after>// Copyright (c) 2015 Andrew Sutton
// All rights reserved

#ifndef LINGO_MEMORY_HPP
#define LINGO_MEMORY_HPP

#include "lingo/debug.hpp"

#include <list>
#include <set>
#include <unordered_map>
#include <vector>

namespace lingo
{

class Integer;
class String_view;
class Token;

// -------------------------------------------------------------------------- //
//                            Factories

// A unique factory will only allocate new objects if
// they have not been previously created. A strict weak
// order is used to determine the uniqueness of objects.
template<typename T, typename C>
struct Unique_factory : std::set<T, C>
{
  // Create an object of type T.
  template<typename... Args>
  T* make(Args&&... args)
  {
    auto ins = this->emplace(std::forward<Args>(args)...);
    return const_cast<T*>(&*ins.first);
  }
};

// A singleton factory will only allocate a single
// object of the given type.
template<typename T>
struct Singleton_factory
{
  Singleton_factory()
    : object(nullptr)
  { }

  // Create an object of type T. If T has already been
  // created, the arguments given here must match those
  // given previously (or be empty).
  template<typename... Args>
  T* make(Args&&... args)
  {
    if (!object)
      object = new T(std::forward<Args>(args)...);
    return object;
  }

  T* object;
};

// -------------------------------------------------------------------------- //
//                          Garbage collector

class Collecting_factory;
class Reach;

// Every object allocated through the garbage collector is
// an instance of the collectable type.
struct Collectable
{
  Collectable()
    : marked(false)
  { }

  virtual ~Collectable() { }

  virtual void reach() = 0;

  bool marked;
};

// The following functions support the mark and sweep
// garbage collection algorithm. We define mark() for a
// large number of types, especially nodes. This provides
// a generic facility for traversing the common structure
// of those nodes.

// Catch node pointers that do not have overloads.
inline void
mark(void const*) { }

// Do not mark arithmetic values.
template<typename T>
inline typename std::enable_if<std::is_arithmetic<T>::value>::type
mark(T) { }

// Do not mark enumerations.
template<typename T>
inline typename std::enable_if<std::is_enum<T>::value>::type
mark(T) { }

// Do not mark integers.
inline void
mark(Integer const&) { }

// Do not mark strings.
inline void
mark(String_view const&) { }

// Do not mark tokens.
inline void
mark(Token const&) { }

// Mark an empty node.
template<typename T>
inline void
mark_nullary(T const* t)
{
  T* nc = const_cast<T*>(t);
  if (Collectable* c = dynamic_cast<Collectable*>(nc))
    c->marked = true;
}

// Mark a unary node.
template<typename T>
inline void
mark_unary(T const* t)
{
  mark_nullary(t);
  mark(t->first);
}

// Mark a binary node.
template<typename T>
inline void
mark_binary(T const* t)
{
  mark_unary(t);
  mark(t->second);
}

// Mark a ternary node.
template<typename T>
inline void
mark_ternary(T const* t)
{
  mark_binary(t);
  mark(t->third);
}

// Mark a k-ary node.
template<typename T>
inline void
mark_kary(T const* t)
{
  mark_nullary(t);
  for (auto const* p : *t)
    mark(p);
}

// The garbage factory class tracks each object that it allocates.
// At various phases of execution, the collect() function can be
// used to reclaim unreferenced memory.
//
// The garbage factory is designed to collect nodes.
//
// Note that the garbage collector is a global resource.
//
// TODO: The root set is not especially efficient since we have
// to traverse it (it's an unordered map).
class Collecting_factory
{
  // Make every T collectable.
  template<typename T>
  struct Collectable_type : T, Collectable
  {
    template<typename... Args>
    Collectable_type(Args&&... args)
      : T(std::forward<Args>(args)...), Collectable()
    { }

    void reach() override
    {
      using lingo::mark;
      mark(this);
    }
  };

public:
  // Create an object of type T. Note that T must be a node type.
  template<typename T, typename... Args>
  T* make(Args&&... args)
  {
    Collectable_type<T>* obj = new Collectable_type<T>(std::forward<Args>(args)...);
    objects.push_back(obj);
    return obj;
  }

  // Root management.
  void declare(Reach*);
  void undeclare(Reach*);

  void collect();

private:
  void mark();
  void sweep();

private:
  using Object_list = std::list<Collectable*>;
  using Root_set = std::unordered_map<Reach*, Reach*>;

  Object_list objects;
  Root_set    roots;
};

Collecting_factory& gc();

// The reach class declares a new root into the garbage
// collector. When this object is destroyed, it is no
// longer a collection root.
//
// Note that a GC root will only store references to
// collectable objects.
class Reach : std::vector<Collectable*>
{
  friend class Collecting_factory;
public:
  Reach()
  {
    gc().declare(this);
  }

  // Declare a new garbage collection root, initialized
  // with the given object.
  template<typename T>
  Reach(T* t)
    : Reach()
  {
    declare(t);
  }

  // Declare a new garbage collection root, initialized
  // with the given sequence of objects.
  template<typename T, typename... Args>
  Reach(T* t, Args*... args)
    : Reach(args...)
  {
    declare(t);
  }

  ~Reach()
  {
    gc().undeclare(this);
  }

  // Declare `u` to be reachable.
  template<typename T>
  void operator()(T* t)
  {
    declare(t);
  }

private:
  // Declare `t` as reachable. Note that this is only defined
  // for pointers to objects of polymorphic type. Otherwise,
  // don't track references.
  template<typename T>
  typename std::enable_if<std::is_polymorphic<T>::value>::type
  declare(T* t)
  {
    if (Collectable* obj = dynamic_cast<Collectable*>(t))
      push_back(obj);
  }

  template<typename T>
  typename std::enable_if<!std::is_polymorphic<T>::value>::type
  declare(T* t)
  {
  }

  // Mark all nodes reachable from this root with
  // the given value. This is used by the garbage
  // collector.
  void reach()
  {
    for (Collectable* r : *this)
      r->reach();
  }
};

// -------------------------------------------------------------------------- //
//                            Algorithms

// This function deletes the object pointed at by p sets it
// to nullptr, returning that value. This is primarily used
// to reduce reduntant code on certain error conditions during
// parsing and elaboration.
template<typename T>
inline T*
reset(T*& p)
{
  delete p;
  return p = nullptr;
}

} // namespace lingo

#endif
<|endoftext|>