<commit_before>/*
** Author(s):
**  - Cedric GESTES <gestes@aldebaran-robotics.com>
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <gtest/gtest.h>
#include <qi/signal.hpp>
#include <qi/future.hpp>
#include <qi/signalspy.hpp>
#include <qi/anyobject.hpp>
#include <qi/application.hpp>
#include <qi/type/objecttypebuilder.hpp>
#include <qi/type/dynamicobjectbuilder.hpp>

qiLogCategory("test");

class Foo
{
public:
  void func(int)               { }
  void func1(qi::Atomic<int>* r, int)
  {
    // hackish sleep so that asynchronous trigger detection is safer
    qi::os::msleep(1); ++*r;
  }
  void func2(qi::Atomic<int>* r, int, int) { ++*r; }
};
void foo(qi::Atomic<int>* r, int, int)     { ++*r; }
void foo2(qi::Atomic<int>* r, char, char)  { ++*r; }
void foo3(qi::Atomic<int>* r, Foo *)       { ++*r; }
void foolast(int, qi::Promise<void> prom, qi::Atomic<int>* r) { prom.setValue(0); ++*r; }

TEST(TestSignal, TestCompilation)
{
  qi::Atomic<int>        res{0};
  qi::Signal<int> s;
  Foo*                   f = (Foo*)1;
  qi::Promise<void>      prom;

  //do not count
  s.connect(qi::AnyFunction::from(&Foo::func, f));
  s.connect(boost::bind<void>(&Foo::func, f, _1));

  s.connect(boost::bind(&foo, &res, 12, _1));
  s.connect(boost::bind(&foo2, &res, 12, _1));
  s.connect(boost::bind<void>(&Foo::func1, f, &res, _1));
  s.connect(boost::bind<void>(&Foo::func2, f, &res, 5, _1));
  s.connect(boost::bind(&foo3, &res, f));
  s.connect(boost::bind(&foolast, _1, prom, &res));

  s(42);

  while (res.load() != 6)
    qi::os::msleep(10);

  ASSERT_EQ(6, res.load());
  ASSERT_TRUE(prom.future().isFinished());
  ASSERT_FALSE(prom.future().hasError());
}

void write42(boost::shared_ptr<int> ptr)
{
  *ptr = 42;
}

TEST(TestSignal, SharedPtr)
{
  // Redundant with Copy test, but just to be sure, check that shared_ptr
  // is correctly transmited.
  qi::Signal<boost::shared_ptr<int> > sig;
  sig.connect(qi::AnyFunction::from(&write42)).setCallType(qi::MetaCallType_Queued);
  {
    boost::shared_ptr<int> ptr(new int(12));
    sig(ptr);
  };
}

void byRef(int& i, bool* done)
{
  qiLogDebug() <<"byRef " << &i << ' ' << done;
  i = 12;
  *done = true;
}

TEST(TestSignal, AutoDisconnect)
{
  // Test automatic disconnection when passing shared_ptrs
  qi::Atomic<int> r{0};
  boost::shared_ptr<Foo> foo(new Foo());
  qi::Signal<qi::Atomic<int>*, int> sig;
  sig.connect(&Foo::func1, boost::weak_ptr<Foo>(foo), _1, _2).setCallType(qi::MetaCallType_Direct);
  sig(&r, 0);
  ASSERT_EQ(1, r.load());
  foo.reset();
  sig(&r, 0);
  ASSERT_EQ(1, r.load());
}

void waitFuture(qi::Atomic<int>& cnt, qi::Promise<void> start, qi::Future<void> f)
{
  if (++cnt == 2)
    start.setValue(0);
  f.wait();

  // force disconnection, may trigger a segfault
  throw qi::PointerLockException();
}

TEST(TestSignal, NonBlockingDestroy)
{
  // disconnect is blocking, but signal destruction should not be

  qi::Promise<void> start;
  qi::Promise<void> finish;
  qi::Atomic<int> cnt{0};

  {
    qi::Signal<void> sig;
    sig.connect(boost::bind(waitFuture, boost::ref(cnt), start, finish.future()));

    sig();
    sig();

    start.future().wait();
    // now that the callback is running, destroy the signal
  }

  // all went well, unblock the callback
  finish.setValue(0);
}

TEST(TestSignal, BadArity)
{
  // Test runtime detection of arity errors
  qi::Signal<> s;
  // avoid using s.connect() which will catch the problem at compile-time
  EXPECT_ANY_THROW(s.connect(qi::SignalSubscriber(qi::AnyFunction::from(&foo))));
  EXPECT_ANY_THROW(s.connect(qi::SignalSubscriber(qi::AnyFunction::from(&foo2))));
  EXPECT_ANY_THROW(s.connect(qi::AnyFunction::from((boost::function<void(qi::Atomic<int>*, int)>)boost::bind(&Foo::func1, (Foo*)0, _1, _2))));
}

void lol(int v, int& target)
{
  target = v;
}
TEST(TestSignal, SignalSignal)
{
  qi::SignalF<void (int)> sig1;
  qi::SignalF<void (int)> *sig2 = new  qi::SignalF<void (int)>();
  sig1.connect(*sig2);
  qi::SignalSpy spy(*sig2);
  sig1(10);
  ASSERT_TRUE(spy.waitUntil(1, qi::MilliSeconds(300)));
  ASSERT_EQ(10, spy.record(0).arg<int>(0));

  // Test autodisconnect
  delete sig2;
  sig1(20);
  ASSERT_FALSE(spy.waitUntil(2, qi::MilliSeconds(300)));
}

class SignalTest {
public:
  qi::Signal<int> sig;
  qi::Signal<int> sig2;
};

TEST(TestSignal, SignalSignal2)
{
  SignalTest st;
  qi::SignalSpy spy(st.sig2);
  st.sig.connect(st.sig2);
  qiLogDebug() << "sigptrs are " << &st.sig << " " << &st.sig2;
  st.sig(4242);
  ASSERT_TRUE(spy.waitUntil(1, qi::MilliSeconds(300)));
  assert(spy.recordCount() == 1u);
  EXPECT_EQ(4242, spy.lastRecord().arg<int>(0));
}

TEST(TestSignal, SignalN)
{
  qi::Signal<int> sig;
  qi::SignalSpy spy(sig);
  sig(5);
  ASSERT_TRUE(spy.waitUntil(1, qi::MilliSeconds(300)));
  ASSERT_EQ(5, spy.lastRecord().arg<int>(0));
}

class SigHolder
{
public:
  qi::Signal<> s0;
  qi::Signal<int> s1;
  qi::Signal<int, int> s2;
  void fire0() { s0();}
  void fire1(int i) { s1(i); }
  void fire2(int i, int j) { s2(i, j);}
};

QI_REGISTER_OBJECT(SigHolder, s0, s1, s2, fire0, fire1, fire2)

TEST(TestSignal, SignalNBind)
{
  int res = 0;
  boost::shared_ptr<SigHolder> so(new SigHolder);
  qi::AnyObject op = qi::AnyReference::from(so).to<qi::AnyObject>();
  qi::detail::printMetaObject(std::cerr, op.metaObject());
  op.connect("s1", (boost::function<void(int)>)boost::bind<void>(&lol, _1, boost::ref(res)));
  op.post("s1", 2);
  for (unsigned i=0; i<30 && res!=2; ++i) qi::os::msleep(10);
  ASSERT_EQ(2, res);
  so->s1(3);
  for (unsigned i=0; i<30 && res!=3; ++i) qi::os::msleep(10);
  ASSERT_EQ(3, res);
  so->s0.connect( boost::bind<void>(&lol, 42, boost::ref(res)));
  op.post("s0");
  for (unsigned i=0; i<30 && res!=42; ++i) qi::os::msleep(10);
  ASSERT_EQ(42, res);
}

void foothrow() {
  throw 42;
}

TEST(TestSignal, SignalThrow)
{
  qi::Signal<> sig;
  sig.connect(boost::bind<void>(&foothrow));
  ASSERT_NO_THROW(sig());
  qi::os::msleep(50);
}

void dynTest0(int& tgt)                     { tgt |= 1;}
void dynTest1(int& tgt, int)                { tgt |= 2;}
void dynTest2(int& tgt, int, int)           { tgt |= 4;}
void dynTest3(int& tgt, int, std::string)   { tgt |= 8;}
void dynTestN(int& tgt, const qi::AnyArguments& a)   { tgt |= 32;}
qi::AnyReference dynTestN2(int& tgt, const std::vector<qi::AnyReference>& a)   { tgt |= 16; return qi::AnyReference();}

TEST(TestSignal, Dynamic)
{
  qi::SignalBase s(qi::Signature("m"));
  int trig = 0;
  s.setCallType(qi::MetaCallType_Direct);
  s.connect((boost::function<void()>) boost::bind(&dynTest0, boost::ref(trig)));
  s.connect((boost::function<void(int)>) boost::bind(&dynTest1, boost::ref(trig), _1));
  s.connect((boost::function<void(int, int)>) boost::bind(&dynTest2, boost::ref(trig), _1, _2));
  s.connect((boost::function<void(int, std::string)>) boost::bind(&dynTest3, boost::ref(trig), _1, _2));
  s.connect(qi::AnyFunction::fromDynamicFunction(boost::bind(&dynTestN2, boost::ref(trig), _1)));
  s.connect((boost::function<void(const qi::AnyArguments&)>)(boost::bind(&dynTestN, boost::ref(trig), _1)));
  int a, b;
  std::string c;
  qi::GenericFunctionParameters params;
  s.trigger(params);
  EXPECT_EQ(49, trig);
  params.push_back(qi::AnyReference::from(a));
  trig = 0;
  s.trigger(params);
  EXPECT_EQ(50, trig);
  params.push_back(qi::AnyReference::from(b));
  trig = 0;
  s.trigger(params);
  EXPECT_EQ(52, trig);
  params[1] = qi::AnyReference::from(c);
  trig = 0;
  s.trigger(params);
  EXPECT_EQ(56, trig);
}

void onSubs(std::atomic<bool>& var, bool subs)
{
  var = subs;
}

void callback(int i)
{}

TEST(TestSignal, OnSubscriber)
{
  std::atomic<bool> subscribers(false);

  qi::Signal<int> sig(boost::bind(onSubs, boost::ref(subscribers), _1));
  ASSERT_FALSE(subscribers);
  qi::SignalLink l = sig.connect(&callback);
  ASSERT_TRUE(subscribers);
  ASSERT_TRUE(sig.disconnect(l));
  ASSERT_FALSE(subscribers);
}

void store2(qi::Promise<int> variable1, qi::Promise<int> variable2, int value1, int value2)
{
  variable1.setValue(value1);
  variable2.setValue(value2);
}

TEST(TestSignal, SignalToSignalWithExtraArgument)
{
  qi::Promise<int> target1, target2;
  qi::Signal<int> signal1;
  qi::Signal<int, int> signal2;
  signal1.connect(boost::bind(boost::ref(signal2), _1, 42));
  signal2.connect(store2, target1, target2, _1, _2);
  signal1(12);
  EXPECT_EQ(12, target1.future().value());
  EXPECT_EQ(42, target2.future().value());
}

TEST(TestSignalSpy, Counter)
{
  qi::Signal<int> sig;
  qi::SignalSpy sp(sig);
  QI_EMIT sig(1);
  QI_EMIT sig(1);
  ASSERT_TRUE(sp.waitUntil(2, qi::MilliSeconds(300)));
  ASSERT_EQ(sp.recordCount(), 2u);

  qi::DynamicObjectBuilder ob;
  ob.advertiseSignal("signal", &sig);
  qi::AnyObject obj(ob.object());
  qi::SignalSpy sp2(obj, "signal");
  QI_EMIT sig(1);
  QI_EMIT sig(1);
  ASSERT_TRUE(sp2.waitUntil(2, qi::MilliSeconds(300)));
  ASSERT_EQ(sp2.recordCount(), 2u);
}

TEST(TestSignalSpy, Async)
{
  qi::Signal<int> sig;
  qi::SignalSpy sp(sig);
  qi::async(boost::bind(boost::ref(sig), 1));
  qi::async(boost::bind(boost::ref(sig), 1));
  ASSERT_TRUE(sp.waitUntil(2, qi::Seconds(1)));
  ASSERT_EQ(sp.recordCount(), 2u);
}

TEST(TestSignalSpy, StoringTypedValueRecords)
{
  const std::vector<int> ints{ 1, 42, 13, 2016 };
  const std::vector<std::string> strings{ "poil", "slip", "banane", "pancréas" };
  qi::Signal<int, std::string> signal;
  qi::SignalSpy spy(signal);

  signal(ints[0], strings[0]);
  ASSERT_EQ(1u, spy.recordCount());
  auto record = spy.record(0);
  ASSERT_EQ(ints[0], record.arg<int>(0));
  ASSERT_EQ(strings[0], record.arg<std::string>(1));

  for(auto i = 1u; i < ints.size(); ++i)
  {
    signal(ints[i], strings[i]);
  }
  spy.waitUntil(ints.size(), qi::MilliSeconds(300));

  for(auto i = 1u; i < ints.size(); ++i)
  {
    record = spy.record(i);
    EXPECT_EQ(ints[i], record.arg<int>(0));
    EXPECT_EQ(strings[i], record.arg<std::string>(1));
  }

  auto records = spy.allRecords();
  ASSERT_EQ(ints.size(), records.size());
  for(auto i = 0u; i < records.size(); ++i)
  {
    EXPECT_EQ(ints[i], records[i].arg<int>(0));
    EXPECT_EQ(strings[i], records[i].arg<std::string>(1));
  }
}

int main(int argc, char **argv) {
  qi::Application app(argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>Signal: debug logs in test, always<commit_after>/*
** Author(s):
**  - Cedric GESTES <gestes@aldebaran-robotics.com>
**
** Copyright (C) 2012 Aldebaran Robotics
*/

#include <gtest/gtest.h>
#include <qi/signal.hpp>
#include <qi/future.hpp>
#include <qi/signalspy.hpp>
#include <qi/anyobject.hpp>
#include <qi/application.hpp>
#include <qi/type/objecttypebuilder.hpp>
#include <qi/type/dynamicobjectbuilder.hpp>

qiLogCategory("test");

class Foo
{
public:
  void func(int)               { }
  void func1(qi::Atomic<int>* r, int)
  {
    // hackish sleep so that asynchronous trigger detection is safer
    qi::os::msleep(1); ++*r;
  }
  void func2(qi::Atomic<int>* r, int, int) { ++*r; }
};
void foo(qi::Atomic<int>* r, int, int)     { ++*r; }
void foo2(qi::Atomic<int>* r, char, char)  { ++*r; }
void foo3(qi::Atomic<int>* r, Foo *)       { ++*r; }
void foolast(int, qi::Promise<void> prom, qi::Atomic<int>* r) { prom.setValue(0); ++*r; }

TEST(TestSignal, TestCompilation)
{
  qi::Atomic<int>        res{0};
  qi::Signal<int> s;
  Foo*                   f = (Foo*)1;
  qi::Promise<void>      prom;

  //do not count
  s.connect(qi::AnyFunction::from(&Foo::func, f));
  s.connect(boost::bind<void>(&Foo::func, f, _1));

  s.connect(boost::bind(&foo, &res, 12, _1));
  s.connect(boost::bind(&foo2, &res, 12, _1));
  s.connect(boost::bind<void>(&Foo::func1, f, &res, _1));
  s.connect(boost::bind<void>(&Foo::func2, f, &res, 5, _1));
  s.connect(boost::bind(&foo3, &res, f));
  s.connect(boost::bind(&foolast, _1, prom, &res));

  s(42);

  while (res.load() != 6)
    qi::os::msleep(10);

  ASSERT_EQ(6, res.load());
  ASSERT_TRUE(prom.future().isFinished());
  ASSERT_FALSE(prom.future().hasError());
}

void write42(boost::shared_ptr<int> ptr)
{
  *ptr = 42;
}

TEST(TestSignal, SharedPtr)
{
  // Redundant with Copy test, but just to be sure, check that shared_ptr
  // is correctly transmited.
  qi::Signal<boost::shared_ptr<int> > sig;
  sig.connect(qi::AnyFunction::from(&write42)).setCallType(qi::MetaCallType_Queued);
  {
    boost::shared_ptr<int> ptr(new int(12));
    sig(ptr);
  };
}

void byRef(int& i, bool* done)
{
  qiLogDebug() <<"byRef " << &i << ' ' << done;
  i = 12;
  *done = true;
}

TEST(TestSignal, AutoDisconnect)
{
  // Test automatic disconnection when passing shared_ptrs
  qi::Atomic<int> r{0};
  boost::shared_ptr<Foo> foo(new Foo());
  qi::Signal<qi::Atomic<int>*, int> sig;
  sig.connect(&Foo::func1, boost::weak_ptr<Foo>(foo), _1, _2).setCallType(qi::MetaCallType_Direct);
  sig(&r, 0);
  ASSERT_EQ(1, r.load());
  foo.reset();
  sig(&r, 0);
  ASSERT_EQ(1, r.load());
}

void waitFuture(qi::Atomic<int>& cnt, qi::Promise<void> start, qi::Future<void> f)
{
  if (++cnt == 2)
    start.setValue(0);
  f.wait();

  // force disconnection, may trigger a segfault
  throw qi::PointerLockException();
}

TEST(TestSignal, NonBlockingDestroy)
{
  // disconnect is blocking, but signal destruction should not be

  qi::Promise<void> start;
  qi::Promise<void> finish;
  qi::Atomic<int> cnt{0};

  {
    qi::Signal<void> sig;
    sig.connect(boost::bind(waitFuture, boost::ref(cnt), start, finish.future()));

    sig();
    sig();

    start.future().wait();
    // now that the callback is running, destroy the signal
  }

  // all went well, unblock the callback
  finish.setValue(0);
}

TEST(TestSignal, BadArity)
{
  // Test runtime detection of arity errors
  qi::Signal<> s;
  // avoid using s.connect() which will catch the problem at compile-time
  EXPECT_ANY_THROW(s.connect(qi::SignalSubscriber(qi::AnyFunction::from(&foo))));
  EXPECT_ANY_THROW(s.connect(qi::SignalSubscriber(qi::AnyFunction::from(&foo2))));
  EXPECT_ANY_THROW(s.connect(qi::AnyFunction::from((boost::function<void(qi::Atomic<int>*, int)>)boost::bind(&Foo::func1, (Foo*)0, _1, _2))));
}

void lol(int v, int& target)
{
  target = v;
}
TEST(TestSignal, SignalSignal)
{
  qi::SignalF<void (int)> sig1;
  qi::SignalF<void (int)> *sig2 = new  qi::SignalF<void (int)>();
  sig1.connect(*sig2);
  qi::SignalSpy spy(*sig2);
  sig1(10);
  ASSERT_TRUE(spy.waitUntil(1, qi::MilliSeconds(300)));
  ASSERT_EQ(10, spy.record(0).arg<int>(0));

  // Test autodisconnect
  delete sig2;
  sig1(20);
  ASSERT_FALSE(spy.waitUntil(2, qi::MilliSeconds(300)));
}

class SignalTest {
public:
  qi::Signal<int> sig;
  qi::Signal<int> sig2;
};

TEST(TestSignal, SignalSignal2)
{
  SignalTest st;
  qi::SignalSpy spy(st.sig2);
  st.sig.connect(st.sig2);
  qiLogDebug() << "sigptrs are " << &st.sig << " " << &st.sig2;
  st.sig(4242);
  ASSERT_TRUE(spy.waitUntil(1, qi::MilliSeconds(300)));
  assert(spy.recordCount() == 1u);
  EXPECT_EQ(4242, spy.lastRecord().arg<int>(0));
}

TEST(TestSignal, SignalN)
{
  qi::Signal<int> sig;
  qi::SignalSpy spy(sig);
  sig(5);
  ASSERT_TRUE(spy.waitUntil(1, qi::MilliSeconds(300)));
  ASSERT_EQ(5, spy.lastRecord().arg<int>(0));
}

class SigHolder
{
public:
  qi::Signal<> s0;
  qi::Signal<int> s1;
  qi::Signal<int, int> s2;
  void fire0() { s0();}
  void fire1(int i) { s1(i); }
  void fire2(int i, int j) { s2(i, j);}
};

QI_REGISTER_OBJECT(SigHolder, s0, s1, s2, fire0, fire1, fire2)

TEST(TestSignal, SignalNBind)
{
  int res = 0;
  boost::shared_ptr<SigHolder> so(new SigHolder);
  qi::AnyObject op = qi::AnyReference::from(so).to<qi::AnyObject>();
  qi::detail::printMetaObject(std::cerr, op.metaObject());
  op.connect("s1", (boost::function<void(int)>)boost::bind<void>(&lol, _1, boost::ref(res)));
  op.post("s1", 2);
  for (unsigned i=0; i<30 && res!=2; ++i) qi::os::msleep(10);
  ASSERT_EQ(2, res);
  so->s1(3);
  for (unsigned i=0; i<30 && res!=3; ++i) qi::os::msleep(10);
  ASSERT_EQ(3, res);
  so->s0.connect( boost::bind<void>(&lol, 42, boost::ref(res)));
  op.post("s0");
  for (unsigned i=0; i<30 && res!=42; ++i) qi::os::msleep(10);
  ASSERT_EQ(42, res);
}

void foothrow() {
  throw 42;
}

TEST(TestSignal, SignalThrow)
{
  qi::Signal<> sig;
  sig.connect(boost::bind<void>(&foothrow));
  ASSERT_NO_THROW(sig());
  qi::os::msleep(50);
}

void dynTest0(int& tgt)                     { tgt |= 1;}
void dynTest1(int& tgt, int)                { tgt |= 2;}
void dynTest2(int& tgt, int, int)           { tgt |= 4;}
void dynTest3(int& tgt, int, std::string)   { tgt |= 8;}
void dynTestN(int& tgt, const qi::AnyArguments& a)   { tgt |= 32;}
qi::AnyReference dynTestN2(int& tgt, const std::vector<qi::AnyReference>& a)   { tgt |= 16; return qi::AnyReference();}

TEST(TestSignal, Dynamic)
{
  qi::SignalBase s(qi::Signature("m"));
  int trig = 0;
  s.setCallType(qi::MetaCallType_Direct);
  s.connect((boost::function<void()>) boost::bind(&dynTest0, boost::ref(trig)));
  s.connect((boost::function<void(int)>) boost::bind(&dynTest1, boost::ref(trig), _1));
  s.connect((boost::function<void(int, int)>) boost::bind(&dynTest2, boost::ref(trig), _1, _2));
  s.connect((boost::function<void(int, std::string)>) boost::bind(&dynTest3, boost::ref(trig), _1, _2));
  s.connect(qi::AnyFunction::fromDynamicFunction(boost::bind(&dynTestN2, boost::ref(trig), _1)));
  s.connect((boost::function<void(const qi::AnyArguments&)>)(boost::bind(&dynTestN, boost::ref(trig), _1)));
  int a, b;
  std::string c;
  qi::GenericFunctionParameters params;
  s.trigger(params);
  EXPECT_EQ(49, trig);
  params.push_back(qi::AnyReference::from(a));
  trig = 0;
  s.trigger(params);
  EXPECT_EQ(50, trig);
  params.push_back(qi::AnyReference::from(b));
  trig = 0;
  s.trigger(params);
  EXPECT_EQ(52, trig);
  params[1] = qi::AnyReference::from(c);
  trig = 0;
  s.trigger(params);
  EXPECT_EQ(56, trig);
}

void onSubs(std::atomic<bool>& var, bool subs)
{
  var = subs;
}

void callback(int i)
{}

TEST(TestSignal, OnSubscriber)
{
  std::atomic<bool> subscribers(false);

  qi::Signal<int> sig(boost::bind(onSubs, boost::ref(subscribers), _1));
  ASSERT_FALSE(subscribers);
  qi::SignalLink l = sig.connect(&callback);
  ASSERT_TRUE(subscribers);
  ASSERT_TRUE(sig.disconnect(l));
  ASSERT_FALSE(subscribers);
}

void store2(qi::Promise<int> variable1, qi::Promise<int> variable2, int value1, int value2)
{
  variable1.setValue(value1);
  variable2.setValue(value2);
}

TEST(TestSignal, SignalToSignalWithExtraArgument)
{
  qi::Promise<int> target1, target2;
  qi::Signal<int> signal1;
  qi::Signal<int, int> signal2;
  signal1.connect(boost::bind(boost::ref(signal2), _1, 42));
  signal2.connect(store2, target1, target2, _1, _2);
  signal1(12);
  EXPECT_EQ(12, target1.future().value());
  EXPECT_EQ(42, target2.future().value());
}

TEST(TestSignalSpy, Counter)
{
  qi::Signal<int> sig;
  qi::SignalSpy sp(sig);
  QI_EMIT sig(1);
  QI_EMIT sig(1);
  ASSERT_TRUE(sp.waitUntil(2, qi::MilliSeconds(300)));
  ASSERT_EQ(sp.recordCount(), 2u);

  qi::DynamicObjectBuilder ob;
  ob.advertiseSignal("signal", &sig);
  qi::AnyObject obj(ob.object());
  qi::SignalSpy sp2(obj, "signal");
  QI_EMIT sig(1);
  QI_EMIT sig(1);
  ASSERT_TRUE(sp2.waitUntil(2, qi::MilliSeconds(300)));
  ASSERT_EQ(sp2.recordCount(), 2u);
}

TEST(TestSignalSpy, Async)
{
  qi::Signal<int> sig;
  qi::SignalSpy sp(sig);
  qi::async(boost::bind(boost::ref(sig), 1));
  qi::async(boost::bind(boost::ref(sig), 1));
  ASSERT_TRUE(sp.waitUntil(2, qi::Seconds(1)));
  ASSERT_EQ(sp.recordCount(), 2u);
}

TEST(TestSignalSpy, StoringTypedValueRecords)
{
  const std::vector<int> ints{ 1, 42, 13, 2016 };
  const std::vector<std::string> strings{ "poil", "slip", "banane", "pancréas" };
  qi::Signal<int, std::string> signal;
  qi::SignalSpy spy(signal);

  signal(ints[0], strings[0]);
  ASSERT_EQ(1u, spy.recordCount());
  auto record = spy.record(0);
  ASSERT_EQ(ints[0], record.arg<int>(0));
  ASSERT_EQ(strings[0], record.arg<std::string>(1));

  for(auto i = 1u; i < ints.size(); ++i)
  {
    signal(ints[i], strings[i]);
  }
  spy.waitUntil(ints.size(), qi::MilliSeconds(300));

  for(auto i = 1u; i < ints.size(); ++i)
  {
    record = spy.record(i);
    EXPECT_EQ(ints[i], record.arg<int>(0));
    EXPECT_EQ(strings[i], record.arg<std::string>(1));
  }

  auto records = spy.allRecords();
  ASSERT_EQ(ints.size(), records.size());
  for(auto i = 0u; i < records.size(); ++i)
  {
    EXPECT_EQ(ints[i], records[i].arg<int>(0));
    EXPECT_EQ(strings[i], records[i].arg<std::string>(1));
  }
}

int main(int argc, char **argv) {
  qi::Application app(argc, argv);
  ::testing::InitGoogleTest(&argc, argv);
  qi::log::addFilter("*", qi::LogLevel_Debug);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: index.hxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: hr $ $Date: 2006-08-14 15:25:27 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _INDEX_HXX
#define _INDEX_HXX

#include <limits.h>

#ifndef _SOLAR_H
#include <tools/solar.h>
#endif

#ifndef _RTTI_HXX
#include <tools/rtti.hxx>               // for RTTI of SwIndexReg
#endif
#ifndef _STRING_HXX
#include <tools/string.hxx>             // for xub_StrLen
#endif

#define INVALID_INDEX STRING_NOTFOUND

// Maximale Anzahl von Indizies im IndexArray (zum Abtesten auf Ueberlaeufe)
class SwIndex;
class SwIndexReg;
struct SwPosition;

#ifdef PRODUCT
#define INLINE inline
#else
#define INLINE
#endif

class SwIndex
{
    friend class SwIndexReg;

#ifndef PRODUCT
    static int nSerial;
    int MySerial;
#endif

    xub_StrLen  nIndex;
    SwIndexReg* pArray;
    SwIndex *pNext, *pPrev;

    SwIndex& ChgValue( const SwIndex& rIdx, xub_StrLen nNewValue );
    void Remove();                  // Ausketten

public:
    SwIndex( SwIndexReg * pReg, xub_StrLen nIdx = 0 );
    SwIndex( const SwIndex & );
    SwIndex( const SwIndex &, short nDiff );
    ~SwIndex() { Remove(); }

    INLINE xub_StrLen operator++();
    INLINE xub_StrLen operator--();
#ifndef CFRONT
    INLINE xub_StrLen operator++(int);
    INLINE xub_StrLen operator--(int);
#endif

    INLINE xub_StrLen operator+=( xub_StrLen );
    INLINE xub_StrLen operator-=( xub_StrLen );
    INLINE xub_StrLen operator+=( const SwIndex& );
    INLINE xub_StrLen operator-=( const SwIndex& );

    INLINE BOOL operator<( const SwIndex& ) const;
    INLINE BOOL operator<=( const SwIndex& ) const;
    INLINE BOOL operator>( const SwIndex& ) const;
    INLINE BOOL operator>=( const SwIndex& ) const;
    BOOL operator==( const SwIndex& rSwIndex ) const
    { return (nIndex == rSwIndex.nIndex) &&  (pArray == rSwIndex.pArray); }

    BOOL operator!=( const SwIndex& rSwIndex ) const
    { return (nIndex != rSwIndex.nIndex) ||  (pArray != rSwIndex.pArray); }

    BOOL operator<( xub_StrLen nWert ) const    { return nIndex <  nWert; }
    BOOL operator<=( xub_StrLen nWert ) const   { return nIndex <= nWert; }
    BOOL operator>( xub_StrLen nWert ) const    { return nIndex >  nWert; }
    BOOL operator>=( xub_StrLen nWert ) const   { return nIndex >= nWert; }
    BOOL operator==( xub_StrLen nWert ) const   { return nIndex == nWert; }
    BOOL operator!=( xub_StrLen nWert ) const   { return nIndex != nWert; }

    INLINE SwIndex& operator=( xub_StrLen );
    SwIndex& operator=( const SwIndex & );

    // gebe den Wert vom Index als xub_StrLen zurueck
    xub_StrLen GetIndex() const { return nIndex; }

    // ermoeglicht Zuweisungen ohne Erzeugen eines temporaeren
    // Objektes
    SwIndex &Assign(SwIndexReg *,xub_StrLen);

        // Herausgabe des Pointers auf das IndexArray,
        // (fuers RTTI am SwIndexReg)
    const SwIndexReg* GetIdxReg() const { return pArray; }
};

#undef INLINE

class SwIndexReg
{
    friend class SwIndex;
    friend bool lcl_PosOk(const SwPosition & aPos);

    const SwIndex *pFirst, *pLast, *pMiddle;

    // ein globales Array, in das Indizies verschoben werden, die mal
    // temporaer "ausgelagert" werden muessen; oder die zum Zeitpunkt des
    // anlegens kein gueltiges Array kennen (SwPaM/SwPosition!)
    friend void _InitCore();
    friend void _FinitCore();
    static SwIndexReg* pEmptyIndexArray;

protected:
    virtual void Update( const SwIndex & aPos, xub_StrLen nLen,
                         BOOL bNegativ = FALSE, BOOL bDelete = FALSE );

    void ChkArr();

    BOOL HasAnyIndex() const { return 0 != pFirst; }

public:
    SwIndexReg();
#ifndef PRODUCT
    ~SwIndexReg();
#endif

    // rtti, abgeleitete moegens gleichtun oder nicht. Wenn sie es gleichtun
    // kann ueber das SwIndexReg typsicher gecastet werden.
    TYPEINFO();

    void MoveTo( SwIndexReg& rArr );
};

#ifdef PRODUCT

inline xub_StrLen SwIndex::operator++()
{
    return ChgValue( *this, nIndex+1 ).nIndex;
}
inline xub_StrLen SwIndex::operator--()
{
    return ChgValue( *this, nIndex-1 ).nIndex;
}
#ifndef CFRONT
inline xub_StrLen SwIndex::operator++(int)
{
    xub_StrLen nOldIndex = nIndex;
    ChgValue( *this, nIndex+1 );
    return nOldIndex;
}
inline xub_StrLen SwIndex::operator--(int)
{
    xub_StrLen nOldIndex = nIndex;
    ChgValue( *this, nIndex-1 );
    return nOldIndex;
}
#endif

inline xub_StrLen SwIndex::operator+=( xub_StrLen nWert )
{
    return ChgValue( *this, nIndex + nWert ).nIndex;
}
inline xub_StrLen SwIndex::operator-=( xub_StrLen nWert )
{
    return ChgValue( *this, nIndex - nWert ).nIndex;
}
inline xub_StrLen SwIndex::operator+=( const  SwIndex& rIndex )
{
    return ChgValue( *this, nIndex + rIndex.nIndex ).nIndex;
}
inline xub_StrLen SwIndex::operator-=( const SwIndex& rIndex )
{
    return ChgValue( *this, nIndex - rIndex.nIndex ).nIndex;
}

inline BOOL SwIndex::operator<( const SwIndex& rIndex ) const
{
    return nIndex < rIndex.nIndex;
}
inline BOOL SwIndex::operator<=( const SwIndex& rIndex ) const
{
    return nIndex <= rIndex.nIndex;
}
inline BOOL SwIndex::operator>( const SwIndex& rIndex ) const
{
    return nIndex > rIndex.nIndex;
}
inline BOOL SwIndex::operator>=( const SwIndex& rIndex ) const
{
    return nIndex >= rIndex.nIndex;
}
inline SwIndex& SwIndex::operator=( xub_StrLen nWert )
{
    if( nIndex != nWert )
        ChgValue( *this, nWert );
    return *this;
}

#endif // PRODUCT

#endif
<commit_msg>INTEGRATION: CWS swwarnings (1.8.242); FILE MERGED 2007/03/12 08:48:17 fme 1.8.242.1: #i69287# Warning free code<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: index.hxx,v $
 *
 *  $Revision: 1.9 $
 *
 *  last change: $Author: hr $ $Date: 2007-09-27 08:05:13 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _INDEX_HXX
#define _INDEX_HXX

#include <limits.h>

#ifndef _SOLAR_H
#include <tools/solar.h>
#endif

#ifndef _RTTI_HXX
#include <tools/rtti.hxx>               // for RTTI of SwIndexReg
#endif
#ifndef _STRING_HXX
#include <tools/string.hxx>             // for xub_StrLen
#endif

#define INVALID_INDEX STRING_NOTFOUND

// Maximale Anzahl von Indizies im IndexArray (zum Abtesten auf Ueberlaeufe)
class SwIndex;
class SwIndexReg;
struct SwPosition;

#ifdef PRODUCT
#define INLINE inline
#else
#define INLINE
#endif

class SwIndex
{
    friend class SwIndexReg;

#ifndef PRODUCT
    static int nSerial;
    int MySerial;
#endif

    xub_StrLen  nIndex;
    SwIndexReg* pArray;
    SwIndex *pNext, *pPrev;

    SwIndex& ChgValue( const SwIndex& rIdx, xub_StrLen nNewValue );
    void Remove();                  // Ausketten

public:
    SwIndex( SwIndexReg * pReg, xub_StrLen nIdx = 0 );
    SwIndex( const SwIndex & );
    SwIndex( const SwIndex &, short nDiff );
    ~SwIndex() { Remove(); }

    INLINE xub_StrLen operator++();
    INLINE xub_StrLen operator--();
#ifndef CFRONT
    INLINE xub_StrLen operator++(int);
    INLINE xub_StrLen operator--(int);
#endif

    INLINE xub_StrLen operator+=( xub_StrLen );
    INLINE xub_StrLen operator-=( xub_StrLen );
    INLINE xub_StrLen operator+=( const SwIndex& );
    INLINE xub_StrLen operator-=( const SwIndex& );

    INLINE BOOL operator<( const SwIndex& ) const;
    INLINE BOOL operator<=( const SwIndex& ) const;
    INLINE BOOL operator>( const SwIndex& ) const;
    INLINE BOOL operator>=( const SwIndex& ) const;
    BOOL operator==( const SwIndex& rSwIndex ) const
    { return (nIndex == rSwIndex.nIndex) &&  (pArray == rSwIndex.pArray); }

    BOOL operator!=( const SwIndex& rSwIndex ) const
    { return (nIndex != rSwIndex.nIndex) ||  (pArray != rSwIndex.pArray); }

    BOOL operator<( xub_StrLen nWert ) const    { return nIndex <  nWert; }
    BOOL operator<=( xub_StrLen nWert ) const   { return nIndex <= nWert; }
    BOOL operator>( xub_StrLen nWert ) const    { return nIndex >  nWert; }
    BOOL operator>=( xub_StrLen nWert ) const   { return nIndex >= nWert; }
    BOOL operator==( xub_StrLen nWert ) const   { return nIndex == nWert; }
    BOOL operator!=( xub_StrLen nWert ) const   { return nIndex != nWert; }

    INLINE SwIndex& operator=( xub_StrLen );
    SwIndex& operator=( const SwIndex & );

    // gebe den Wert vom Index als xub_StrLen zurueck
    xub_StrLen GetIndex() const { return nIndex; }

    // ermoeglicht Zuweisungen ohne Erzeugen eines temporaeren
    // Objektes
    SwIndex &Assign(SwIndexReg *,xub_StrLen);

        // Herausgabe des Pointers auf das IndexArray,
        // (fuers RTTI am SwIndexReg)
    const SwIndexReg* GetIdxReg() const { return pArray; }
};

#undef INLINE

class SwIndexReg
{
    friend class SwIndex;
    friend bool lcl_PosOk(const SwPosition & aPos);

    const SwIndex *pFirst, *pLast, *pMiddle;

    // ein globales Array, in das Indizies verschoben werden, die mal
    // temporaer "ausgelagert" werden muessen; oder die zum Zeitpunkt des
    // anlegens kein gueltiges Array kennen (SwPaM/SwPosition!)
    friend void _InitCore();
    friend void _FinitCore();
    static SwIndexReg* pEmptyIndexArray;

protected:
    virtual void Update( const SwIndex & aPos, xub_StrLen nLen,
                         BOOL bNegativ = FALSE, BOOL bDelete = FALSE );

    void ChkArr();

    BOOL HasAnyIndex() const { return 0 != pFirst; }

public:
    SwIndexReg();
    virtual ~SwIndexReg();

    // rtti, abgeleitete moegens gleichtun oder nicht. Wenn sie es gleichtun
    // kann ueber das SwIndexReg typsicher gecastet werden.
    TYPEINFO();

    void MoveTo( SwIndexReg& rArr );
};

#ifdef PRODUCT

inline xub_StrLen SwIndex::operator++()
{
    return ChgValue( *this, nIndex+1 ).nIndex;
}
inline xub_StrLen SwIndex::operator--()
{
    return ChgValue( *this, nIndex-1 ).nIndex;
}
#ifndef CFRONT
inline xub_StrLen SwIndex::operator++(int)
{
    xub_StrLen nOldIndex = nIndex;
    ChgValue( *this, nIndex+1 );
    return nOldIndex;
}
inline xub_StrLen SwIndex::operator--(int)
{
    xub_StrLen nOldIndex = nIndex;
    ChgValue( *this, nIndex-1 );
    return nOldIndex;
}
#endif

inline xub_StrLen SwIndex::operator+=( xub_StrLen nWert )
{
    return ChgValue( *this, nIndex + nWert ).nIndex;
}
inline xub_StrLen SwIndex::operator-=( xub_StrLen nWert )
{
    return ChgValue( *this, nIndex - nWert ).nIndex;
}
inline xub_StrLen SwIndex::operator+=( const  SwIndex& rIndex )
{
    return ChgValue( *this, nIndex + rIndex.nIndex ).nIndex;
}
inline xub_StrLen SwIndex::operator-=( const SwIndex& rIndex )
{
    return ChgValue( *this, nIndex - rIndex.nIndex ).nIndex;
}

inline BOOL SwIndex::operator<( const SwIndex& rIndex ) const
{
    return nIndex < rIndex.nIndex;
}
inline BOOL SwIndex::operator<=( const SwIndex& rIndex ) const
{
    return nIndex <= rIndex.nIndex;
}
inline BOOL SwIndex::operator>( const SwIndex& rIndex ) const
{
    return nIndex > rIndex.nIndex;
}
inline BOOL SwIndex::operator>=( const SwIndex& rIndex ) const
{
    return nIndex >= rIndex.nIndex;
}
inline SwIndex& SwIndex::operator=( xub_StrLen nWert )
{
    if( nIndex != nWert )
        ChgValue( *this, nWert );
    return *this;
}

#endif // PRODUCT

#endif
<|endoftext|>


<commit_before><commit_msg>Remove redundant inline<commit_after><|endoftext|>


<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */

/*
 * Copyright (C) 2020 ScyllaDB.
 */

#include <chrono>

#include <seastar/testing/test_case.hh>
#include <seastar/testing/thread_test_case.hh>
#include <seastar/core/do_with.hh>
#include <seastar/core/sleep.hh>
#include <seastar/core/rwlock.hh>
#include <boost/range/irange.hpp>

using namespace seastar;
using namespace std::chrono_literals;

SEASTAR_THREAD_TEST_CASE(test_rwlock) {
    rwlock l;

    l.for_write().lock().get();
    BOOST_REQUIRE(!l.try_write_lock());
    BOOST_REQUIRE(!l.try_read_lock());
    l.for_write().unlock();

    l.for_read().lock().get();
    BOOST_REQUIRE(!l.try_write_lock());
    BOOST_REQUIRE(l.try_read_lock());
    l.for_read().lock().get();
    l.for_read().unlock();
    l.for_read().unlock();
    l.for_read().unlock();

    BOOST_REQUIRE(l.try_write_lock());
    l.for_write().unlock();
}

SEASTAR_TEST_CASE(test_rwlock_exclusive) {
    return do_with(rwlock(), unsigned(0), [] (rwlock& l, unsigned& counter) {
        return parallel_for_each(boost::irange(0, 10), [&l, &counter] (int idx) {
            return with_lock(l.for_write(), [&counter] {
                BOOST_REQUIRE_EQUAL(counter, 0u);
                ++counter;
                return sleep(1ms).then([&counter] {
                    --counter;
                    BOOST_REQUIRE_EQUAL(counter, 0u);
                });
            });
        });
    });
}

SEASTAR_TEST_CASE(test_rwlock_shared) {
    return do_with(rwlock(), unsigned(0), unsigned(0), [] (rwlock& l, unsigned& counter, unsigned& max) {
        return parallel_for_each(boost::irange(0, 10), [&l, &counter, &max] (int idx) {
            return with_lock(l.for_read(), [&counter, &max] {
                ++counter;
                max = std::max(max, counter);
                return sleep(1ms).then([&counter] {
                    --counter;
                });
            });
        }).finally([&counter, &max] {
            BOOST_REQUIRE_EQUAL(counter, 0u);
            BOOST_REQUIRE_NE(max, 0u);
        });
    });
}

SEASTAR_THREAD_TEST_CASE(test_rwlock_failed_func) {
    rwlock l;

    // verify that the rwlock is unlocked when func fails
    future<> fut = with_lock(l.for_read(), [] {
        throw std::runtime_error("injected");
    });
    BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);

    fut = with_lock(l.for_write(), [] {
        throw std::runtime_error("injected");
    });
    BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);

    BOOST_REQUIRE(l.try_write_lock());
    l.for_write().unlock();
}

SEASTAR_THREAD_TEST_CASE(test_failed_with_lock) {
    struct test_lock {
        future<> lock() noexcept {
            return make_exception_future<>(std::runtime_error("injected"));
        }
        void unlock() noexcept {
            BOOST_REQUIRE(false);
        }
    };

    test_lock l;

    // if l.lock() fails neither the function nor l.unlock()
    // should be called.
    BOOST_REQUIRE_THROW(with_lock(l, [] {
        BOOST_REQUIRE(false);
    }).get(), std::runtime_error);
}
<commit_msg>test: locking_test: basic shared_mutex tests<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */

/*
 * Copyright (C) 2020 ScyllaDB.
 */

#include <chrono>

#include <seastar/testing/test_case.hh>
#include <seastar/testing/thread_test_case.hh>
#include <seastar/core/do_with.hh>
#include <seastar/core/sleep.hh>
#include <seastar/core/rwlock.hh>
#include <seastar/core/shared_mutex.hh>
#include <boost/range/irange.hpp>

using namespace seastar;
using namespace std::chrono_literals;

SEASTAR_THREAD_TEST_CASE(test_rwlock) {
    rwlock l;

    l.for_write().lock().get();
    BOOST_REQUIRE(!l.try_write_lock());
    BOOST_REQUIRE(!l.try_read_lock());
    l.for_write().unlock();

    l.for_read().lock().get();
    BOOST_REQUIRE(!l.try_write_lock());
    BOOST_REQUIRE(l.try_read_lock());
    l.for_read().lock().get();
    l.for_read().unlock();
    l.for_read().unlock();
    l.for_read().unlock();

    BOOST_REQUIRE(l.try_write_lock());
    l.for_write().unlock();
}

SEASTAR_TEST_CASE(test_rwlock_exclusive) {
    return do_with(rwlock(), unsigned(0), [] (rwlock& l, unsigned& counter) {
        return parallel_for_each(boost::irange(0, 10), [&l, &counter] (int idx) {
            return with_lock(l.for_write(), [&counter] {
                BOOST_REQUIRE_EQUAL(counter, 0u);
                ++counter;
                return sleep(1ms).then([&counter] {
                    --counter;
                    BOOST_REQUIRE_EQUAL(counter, 0u);
                });
            });
        });
    });
}

SEASTAR_TEST_CASE(test_rwlock_shared) {
    return do_with(rwlock(), unsigned(0), unsigned(0), [] (rwlock& l, unsigned& counter, unsigned& max) {
        return parallel_for_each(boost::irange(0, 10), [&l, &counter, &max] (int idx) {
            return with_lock(l.for_read(), [&counter, &max] {
                ++counter;
                max = std::max(max, counter);
                return sleep(1ms).then([&counter] {
                    --counter;
                });
            });
        }).finally([&counter, &max] {
            BOOST_REQUIRE_EQUAL(counter, 0u);
            BOOST_REQUIRE_NE(max, 0u);
        });
    });
}

SEASTAR_THREAD_TEST_CASE(test_rwlock_failed_func) {
    rwlock l;

    // verify that the rwlock is unlocked when func fails
    future<> fut = with_lock(l.for_read(), [] {
        throw std::runtime_error("injected");
    });
    BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);

    fut = with_lock(l.for_write(), [] {
        throw std::runtime_error("injected");
    });
    BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);

    BOOST_REQUIRE(l.try_write_lock());
    l.for_write().unlock();
}

SEASTAR_THREAD_TEST_CASE(test_failed_with_lock) {
    struct test_lock {
        future<> lock() noexcept {
            return make_exception_future<>(std::runtime_error("injected"));
        }
        void unlock() noexcept {
            BOOST_REQUIRE(false);
        }
    };

    test_lock l;

    // if l.lock() fails neither the function nor l.unlock()
    // should be called.
    BOOST_REQUIRE_THROW(with_lock(l, [] {
        BOOST_REQUIRE(false);
    }).get(), std::runtime_error);
}

SEASTAR_THREAD_TEST_CASE(test_shared_mutex) {
    shared_mutex sm;

    sm.lock().get();
    BOOST_REQUIRE(!sm.try_lock());
    BOOST_REQUIRE(!sm.try_lock_shared());
    sm.unlock();

    sm.lock_shared().get();
    BOOST_REQUIRE(!sm.try_lock());
    BOOST_REQUIRE(sm.try_lock_shared());
    sm.lock_shared().get();
    sm.unlock_shared();
    sm.unlock_shared();
    sm.unlock_shared();

    BOOST_REQUIRE(sm.try_lock());
    sm.unlock();
}

SEASTAR_TEST_CASE(test_shared_mutex_exclusive) {
    return do_with(shared_mutex(), unsigned(0), [] (shared_mutex& sm, unsigned& counter) {
        return parallel_for_each(boost::irange(0, 10), [&sm, &counter] (int idx) {
            return with_lock(sm, [&counter] {
                BOOST_REQUIRE_EQUAL(counter, 0u);
                ++counter;
                return sleep(1ms).then([&counter] {
                    --counter;
                    BOOST_REQUIRE_EQUAL(counter, 0u);
                });
            });
        });
    });
}

SEASTAR_TEST_CASE(test_shared_mutex_shared) {
    return do_with(shared_mutex(), unsigned(0), unsigned(0), [] (shared_mutex& sm, unsigned& counter, unsigned& max) {
        return parallel_for_each(boost::irange(0, 10), [&sm, &counter, &max] (int idx) {
            return with_shared(sm, [&counter, &max] {
                ++counter;
                max = std::max(max, counter);
                return sleep(1ms).then([&counter] {
                    --counter;
                });
            });
        }).finally([&counter, &max] {
            BOOST_REQUIRE_EQUAL(counter, 0u);
            BOOST_REQUIRE_NE(max, 0u);
        });
    });
}

SEASTAR_THREAD_TEST_CASE(test_shared_mutex_failed_func) {
    shared_mutex sm;

    // verify that the shared_mutex is unlocked when func fails
    future<> fut = with_shared(sm, [] {
        throw std::runtime_error("injected");
    });
    BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);

    fut = with_lock(sm, [] {
        throw std::runtime_error("injected");
    });
    BOOST_REQUIRE_THROW(fut.get(), std::runtime_error);

    BOOST_REQUIRE(sm.try_lock());
    sm.unlock();
}
<|endoftext|>


<commit_before>/*------------------------------------------------------------------------
* (The MIT License)
*
* Copyright (c) 2008-2011 Rhomobile, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* http://rhomobile.com
*------------------------------------------------------------------------*/

#include "pch_rhoruntime.h"
#include "rhoruntime.h"

//#include "../../shared/sqlite/sqlite3.h"
//#include "logging/RhoLogConf.h"
#include "common/RhoStd.h"
#include "common/RhodesApp.h"
#include "common/RhoFilePath.h"
#include "rubyext/WebView.h"

using namespace rhoruntime;
using namespace Platform;

CRhoRuntime^ CRhoRuntime::m_instance = nullptr;

CRhoRuntime::CRhoRuntime(IMainPage^ mainPage):
	m_MainPage(mainPage)
{
}

CRhoRuntime^ CRhoRuntime::getInstance(IMainPage^ mainPage)
{
	if (m_instance == nullptr)
		m_instance = ref new CRhoRuntime(mainPage);
    return m_instance;
}

CRhoRuntime^ CRhoRuntime::getInstance()
{
    return m_instance;
}

IMainPage^ CRhoRuntime::getMainPage()
{
	return m_MainPage;
}

// rhodes executed in a separate thread
void CRhoRuntime::Execute()
{
	rho::String m_strRootPath = rho_native_rhopath(), m_logPort, m_strRuntimePath;
    rho_logconf_Init(m_strRootPath.c_str(), m_strRootPath.c_str(), m_logPort.c_str());

    rho::common::CRhodesApp::Create(m_strRootPath, m_strRootPath, m_strRuntimePath);
    //RHODESAPP().setExtManager( &m_oExtManager );

	//Create Main window
	createCustomMenu();

	RHODESAPP().startApp();

	// wait for 5 seconds
	//m_MainPage->DoWait(10000);
	//m_MainPage->fullscreenCommand(1);
	//m_MainPage->exitCommand();
}

// *** CALLBACKS from MainPage object ***

void CRhoRuntime::updateSizeProperties(int width, int height)
{
	// TODO: implement
}

void CRhoRuntime::onActivate(int active)
{
	//rho_webview_navigate("system/uicreated", 0); //HOTFIX, should remove after threadqueue fix
	RHODESAPP().callUiCreatedCallback();
    //rho_rhodesapp_callAppActiveCallback(active);
    //if (!active)
    //    rho_geoimpl_turngpsoff();
}

void CRhoRuntime::logEvent(::Platform::String^ message)
{
	LOG(INFO) + rho::common::convertToStringW(message->Data());
}

void CRhoRuntime::createCustomMenu()
{
	IMainPage^ mainPage = CRhoRuntime::getInstance()->getMainPage();
	rho::Vector<rho::common::CAppMenuItem> m_arAppMenuItems;
    RHODESAPP().getAppMenu().copyMenuItems(m_arAppMenuItems);
#ifdef ENABLE_DYNAMIC_RHOBUNDLE
	rho::String index = "index"RHO_ERB_EXT;
    rho::String strIndexPage = rho::common::CFilePath::join(RHODESAPP().getStartUrl(), index);
    if ( RHODESAPP().getCurrentUrl().compare(RHODESAPP().getStartUrl()) == 0 ||
         RHODESAPP().getCurrentUrl().compare(strIndexPage) == 0 )
        m_arAppMenuItems.addElement(rho::common::CAppMenuItem("Reload RhoBundle","reload_rhobundle"));
#endif //ENABLE_DYNAMIC_RHOBUNDLE

    //update UI with custom menu items
	mainPage->menuClear();
    for ( unsigned int i = 0; i < m_arAppMenuItems.size(); i++)
    {
        rho::common::CAppMenuItem& oItem = m_arAppMenuItems.elementAt(i);
        if (oItem.m_eType == rho::common::CAppMenuItem::emtSeparator)
            mainPage->menuAddSeparator();
        else {
			rho::StringW labelW;
			rho::common::convertToStringW(oItem.m_strLabel.c_str(), labelW);
			mainPage->menuAddAction((oItem.m_eType == rho::common::CAppMenuItem::emtClose ? "Exit" : ref new Platform::String(labelW.c_str())), i);
        }
    }
}

void CRhoRuntime::onCustomMenuItemCommand(int nItemPos)
{
	rho::Vector<rho::common::CAppMenuItem> m_arAppMenuItems;
    RHODESAPP().getAppMenu().copyMenuItems(m_arAppMenuItems);

	if ( nItemPos < 0 || nItemPos >= (int)m_arAppMenuItems.size() )
        return;

    rho::common::CAppMenuItem& oMenuItem = m_arAppMenuItems.elementAt(nItemPos);
    if ( oMenuItem.m_eType == rho::common::CAppMenuItem::emtUrl )
    {
		// TODO: implement ReloadRhoBundle
        if ( oMenuItem.m_strLink == "reload_rhobundle" )
        {
			#ifdef ENABLE_DYNAMIC_RHOBUNDLE
            //if ( RHODESAPP().getRhobundleReloadUrl().length()>0 ) {
            //    CAppManager::ReloadRhoBundle(m_hWnd,RHODESAPP().getRhobundleReloadUrl().c_str(), NULL);
            //} else {
            //    MessageBox(_T("Path to the bundle is not defined."),_T("Information"), MB_OK | MB_ICONINFORMATION );
            //}
			#endif
			return;
        }
    }
    oMenuItem.processCommand();
}

void CRhoRuntime::onToolbarAction(::Platform::String^ action)
{
	if ( action->Equals("forward") )
		rho_webview_navigate_forward();
	else
		RHODESAPP().loadUrl(rho::common::convertToStringA(action->Data()));
}

void CRhoRuntime::onWindowClose(void)
{
	// TODO: implement
}

void CRhoRuntime::onWebViewUrlChanged(::Platform::String^ url)
{
	RHODESAPP().keepLastVisitedUrl(rho::common::convertToStringA(url->Data()));
}

bool CRhoRuntime::onBackKeyPress()
{
	// TODO: implement check for history size (return false if there's no history)
	RHODESAPP().navigateBack();
	return true;
}

// *** PUBLIC METHODS ***

bool CRhoRuntime::Initialize(::Platform::String^ title)
{
	return true;
}

void CRhoRuntime::DestroyUi(void)
{
	rho_rhodesapp_callUiDestroyedCallback();
}

extern "C" void rho_sys_app_exit()
{
	// exit application
	CRhoRuntime::getInstance()->getMainPage()->exitCommand();
}

#ifdef OS_WP8
extern "C" void rho_net_impl_network_indicator(int active)
{
	 //TODO: rho_net_ping_network
    RAWLOGC_INFO("rhoruntime.cpp", "rho_net_impl_network_indicator() has no implementation on WP8.");
}

extern "C" int rho_net_ping_network(const char* szHost)
{
    //TODO: rho_net_ping_network
    RAWLOGC_INFO("rhoruntime.cpp", "rho_net_ping_network() has no implementation on WP8.");
    return 1;
}
#endif<commit_msg>wp8: special handling for 'Back' menu item<commit_after>/*------------------------------------------------------------------------
* (The MIT License)
*
* Copyright (c) 2008-2011 Rhomobile, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* http://rhomobile.com
*------------------------------------------------------------------------*/

#include "pch_rhoruntime.h"
#include "rhoruntime.h"

//#include "../../shared/sqlite/sqlite3.h"
//#include "logging/RhoLogConf.h"
#include "common/RhoStd.h"
#include "common/RhodesApp.h"
#include "common/RhoFilePath.h"
#include "rubyext/WebView.h"

using namespace rhoruntime;
using namespace Platform;

CRhoRuntime^ CRhoRuntime::m_instance = nullptr;

CRhoRuntime::CRhoRuntime(IMainPage^ mainPage):
	m_MainPage(mainPage)
{
}

CRhoRuntime^ CRhoRuntime::getInstance(IMainPage^ mainPage)
{
	if (m_instance == nullptr)
		m_instance = ref new CRhoRuntime(mainPage);
    return m_instance;
}

CRhoRuntime^ CRhoRuntime::getInstance()
{
    return m_instance;
}

IMainPage^ CRhoRuntime::getMainPage()
{
	return m_MainPage;
}

// rhodes executed in a separate thread
void CRhoRuntime::Execute()
{
	rho::String m_strRootPath = rho_native_rhopath(), m_logPort, m_strRuntimePath;
    rho_logconf_Init(m_strRootPath.c_str(), m_strRootPath.c_str(), m_logPort.c_str());

    rho::common::CRhodesApp::Create(m_strRootPath, m_strRootPath, m_strRuntimePath);
    //RHODESAPP().setExtManager( &m_oExtManager );

	//Create Main window
	createCustomMenu();

	RHODESAPP().startApp();

	// wait for 5 seconds
	//m_MainPage->DoWait(10000);
	//m_MainPage->fullscreenCommand(1);
	//m_MainPage->exitCommand();
}

// *** CALLBACKS from MainPage object ***

void CRhoRuntime::updateSizeProperties(int width, int height)
{
	// TODO: implement
}

void CRhoRuntime::onActivate(int active)
{
	//rho_webview_navigate("system/uicreated", 0); //HOTFIX, should remove after threadqueue fix
	RHODESAPP().callUiCreatedCallback();
    //rho_rhodesapp_callAppActiveCallback(active);
    //if (!active)
    //    rho_geoimpl_turngpsoff();
}

void CRhoRuntime::logEvent(::Platform::String^ message)
{
	LOG(INFO) + rho::common::convertToStringW(message->Data());
}

void CRhoRuntime::createCustomMenu()
{
	IMainPage^ mainPage = CRhoRuntime::getInstance()->getMainPage();
	rho::Vector<rho::common::CAppMenuItem> m_arAppMenuItems;
    RHODESAPP().getAppMenu().copyMenuItems(m_arAppMenuItems);
#ifdef ENABLE_DYNAMIC_RHOBUNDLE
	rho::String index = "index"RHO_ERB_EXT;
    rho::String strIndexPage = rho::common::CFilePath::join(RHODESAPP().getStartUrl(), index);
    if ( RHODESAPP().getCurrentUrl().compare(RHODESAPP().getStartUrl()) == 0 ||
         RHODESAPP().getCurrentUrl().compare(strIndexPage) == 0 )
        m_arAppMenuItems.addElement(rho::common::CAppMenuItem("Reload RhoBundle","reload_rhobundle"));
#endif //ENABLE_DYNAMIC_RHOBUNDLE

    //update UI with custom menu items
	mainPage->menuClear();
	RHODESAPP().setAppBackUrl("");
    for ( unsigned int i = 0; i < m_arAppMenuItems.size(); i++)
    {
        rho::common::CAppMenuItem& oItem = m_arAppMenuItems.elementAt(i);
        if (oItem.m_eType == rho::common::CAppMenuItem::emtSeparator)
            mainPage->menuAddSeparator();
        else {
 			rho::StringW labelW;
			rho::common::convertToStringW(oItem.m_strLabel.c_str(), labelW);
			if ((oItem.m_eType == rho::common::CAppMenuItem::emtBack) && (oItem.m_strLink.compare("back") != 0))
				RHODESAPP().setAppBackUrl(oItem.m_strLink);
			mainPage->menuAddAction((oItem.m_eType == rho::common::CAppMenuItem::emtClose ? "Exit" : ref new Platform::String(labelW.c_str())), i);
        }
    }
}

void CRhoRuntime::onCustomMenuItemCommand(int nItemPos)
{
	rho::Vector<rho::common::CAppMenuItem> m_arAppMenuItems;
    RHODESAPP().getAppMenu().copyMenuItems(m_arAppMenuItems);

	if ( nItemPos < 0 || nItemPos >= (int)m_arAppMenuItems.size() )
        return;

    rho::common::CAppMenuItem& oMenuItem = m_arAppMenuItems.elementAt(nItemPos);
    if ( oMenuItem.m_eType == rho::common::CAppMenuItem::emtUrl )
    {
		// TODO: implement ReloadRhoBundle
        if ( oMenuItem.m_strLink == "reload_rhobundle" )
        {
			#ifdef ENABLE_DYNAMIC_RHOBUNDLE
            //if ( RHODESAPP().getRhobundleReloadUrl().length()>0 ) {
            //    CAppManager::ReloadRhoBundle(m_hWnd,RHODESAPP().getRhobundleReloadUrl().c_str(), NULL);
            //} else {
            //    MessageBox(_T("Path to the bundle is not defined."),_T("Information"), MB_OK | MB_ICONINFORMATION );
            //}
			#endif
			return;
        }
    }
    oMenuItem.processCommand();
}

void CRhoRuntime::onToolbarAction(::Platform::String^ action)
{
	if ( action->Equals("forward") )
		rho_webview_navigate_forward();
	else
		RHODESAPP().loadUrl(rho::common::convertToStringA(action->Data()));
}

void CRhoRuntime::onWindowClose(void)
{
	// TODO: implement
}

void CRhoRuntime::onWebViewUrlChanged(::Platform::String^ url)
{
	RHODESAPP().keepLastVisitedUrl(rho::common::convertToStringA(url->Data()));
}

bool CRhoRuntime::onBackKeyPress()
{
	// TODO: implement check for history size (return false if there's no history)
	RHODESAPP().navigateBack();
	return true;
}

// *** PUBLIC METHODS ***

bool CRhoRuntime::Initialize(::Platform::String^ title)
{
	return true;
}

void CRhoRuntime::DestroyUi(void)
{
	rho_rhodesapp_callUiDestroyedCallback();
}

extern "C" void rho_sys_app_exit()
{
	// exit application
	CRhoRuntime::getInstance()->getMainPage()->exitCommand();
}

#ifdef OS_WP8
extern "C" void rho_net_impl_network_indicator(int active)
{
	 //TODO: rho_net_ping_network
    RAWLOGC_INFO("rhoruntime.cpp", "rho_net_impl_network_indicator() has no implementation on WP8.");
}

extern "C" int rho_net_ping_network(const char* szHost)
{
    //TODO: rho_net_ping_network
    RAWLOGC_INFO("rhoruntime.cpp", "rho_net_ping_network() has no implementation on WP8.");
    return 1;
}
#endif<|endoftext|>


<commit_before>//
// Created by Brad Bazemore on 10/29/15.
//
#include <ros/ros.h>
#include <std_msgs/UInt16MultiArray.h>
#include <std_msgs/ByteMultiArray.h>
#include <modbus/modbus.h>

class plc_modbus_manager{
public:
    plc_modbus_manager();
private:
    ros::NodeHandle node;

    ros::Publisher regs_read;
    ros::Subscriber regs_write;
    ros::Publisher coils_read;
    ros::Subscriber coils_write;

    std::vector<int> regs_addr;
    std::vector<int> coils_addr;

    std_msgs::UInt16MultiArray regs_val;
    std_msgs::ByteMultiArray coils_val;

    modbus_t *plc;

    std::string ip_address;
    int port;

    void regs_callBack(const std_msgs::UInt16MultiArray::ConstPtr &regs_data);
    void coils_callBack(const std_msgs::ByteMultiArray::ConstPtr &coils_data);
};

plc_modbus_manager::plc_modbus_manager() {

    regs_read = node.advertise<std_msgs::UInt16MultiArray>("modbus/regs_read",100);
    regs_write = node.subscribe<std_msgs::UInt16MultiArray>("modbus/regs_write", 100, &plc_modbus_manager::regs_callBack, this);
    coils_read = node.advertise<std_msgs::ByteMultiArray>("modbus/coils_read",100);
    coils_write = node.subscribe<std_msgs::ByteMultiArray>("modbus/coils_write",100, &plc_modbus_manager::coils_callBack, this);

    node.param<std::string>("modbus/ip",ip_address,"192.168.0.100");
    node.param("modbus/port",port,502);

    if(!node.getParam("plc_modbus_node/regs_addr",regs_addr)){
        ROS_WARN("No reg addrs given!");
    }
    if(!node.getParam("plc_modbus_node/coils_addr",coils_addr)){
        ROS_WARN("No coil addrs given!");
    }

    ROS_INFO("Connecting to modbus device on %s/%d",ip_address.c_str(),port);
    plc = modbus_new_tcp(ip_address.c_str(),port);
    if (plc == NULL) {
        ROS_FATAL("Unable to allocate libmodbus context\n");
        return;
    }
    if(modbus_connect(plc)==-1){
        ROS_FATAL("Failed to connect to modbus device!!!");
        ROS_FATAL("%s",modbus_strerror(errno));
        modbus_free(plc);
        return;
    }else{
        ROS_INFO("Connection to modbus device established");
    }

    ros::Rate loop_rate(10);

    while(ros::ok()){
        regs_val.data.clear();
        coils_val.data.clear();
            //test
        for(int i=0;i<regs_addr.size();i++){
            if(modbus_read_registers(plc, regs_addr.at(i), 1, (uint16_t *) regs_val.data.at(i)) == -1){
                ROS_ERROR("Unable to read reg addr:%d",regs_addr.at(i));
                ROS_ERROR("%s",modbus_strerror(errno));
            }
        }
        if(regs_val.data.size()>0) {
            regs_read.publish(regs_val);
        }

        for(int i=0;i<coils_addr.size();i++){
            uint8_t temp[1]={0};
            if(modbus_read_bits(plc, coils_addr.at(i), 1, temp)==-1){
                ROS_ERROR("Unable to read coil addr:%d",regs_addr.at(i));
                ROS_ERROR("%s",modbus_strerror(errno));
            }else{
                coils_val.data.push_back(temp[0]);
            }
        }
        if(coils_val.data.size()>0) {
            coils_read.publish(coils_val);
        }

        ros::spinOnce();
        loop_rate.sleep();
    }

    modbus_close(plc);
    modbus_free(plc);
    return;
}

void plc_modbus_manager::regs_callBack(const std_msgs::UInt16MultiArray::ConstPtr &regs_data){
    if(regs_data->data.size()!=regs_addr.size()){
        ROS_ERROR("Number of regs to write to number of values provided do not match");
        return;
    }
    for(int i=0;i<regs_data->data.size();i++){
        ROS_DEBUG("regs_out[%d]:%u",i,regs_data->data.at(i));
        if(modbus_write_registers(plc, regs_addr.at(i), 1, (const uint16_t *) regs_data->data.at(i)) == -1){
            ROS_ERROR("Modbus reg write failed at addr:%d with value:%u",regs_addr.at(i),regs_data->data.at(i));
            ROS_ERROR("%s",modbus_strerror(errno));
        }
    }
}

void plc_modbus_manager::coils_callBack(const std_msgs::ByteMultiArray::ConstPtr &coils_data){
    if(coils_data->data.size()!=coils_addr.size()){
        ROS_ERROR("Number of coils to write to number of values provided do not match");
        return;
    }
    for(int i=0;i<coils_data->data.size();i++){
        ROS_DEBUG("regs_out[%d]:%u",i,coils_data->data.at(i));
        if(modbus_write_bits(plc, coils_addr.at(i), 1, (const uint8_t *) coils_data->data.at(i)) == -1){
            ROS_ERROR("Modbus coil write failed at addr:%d with value:%u",coils_addr.at(i),coils_data->data.at(i));
            ROS_ERROR("%s",modbus_strerror(errno));
        }
    }
}

int main(int argc, char **argv){
    ros::init(argc,argv,"ros_plc_modbus");
    plc_modbus_manager mm;
    return 0;
}
<commit_msg>fixed null pointer<commit_after>//
// Created by Brad Bazemore on 10/29/15.
//
#include <ros/ros.h>
#include <std_msgs/UInt16MultiArray.h>
#include <std_msgs/ByteMultiArray.h>
#include <modbus/modbus.h>

class plc_modbus_manager{
public:
    plc_modbus_manager();
private:
    ros::NodeHandle node;

    ros::Publisher regs_read;
    ros::Subscriber regs_write;
    ros::Publisher coils_read;
    ros::Subscriber coils_write;

    std::vector<int> regs_addr;
    std::vector<int> coils_addr;

    std_msgs::UInt16MultiArray regs_val;
    std_msgs::ByteMultiArray coils_val;

    modbus_t *plc;

    std::string ip_address;
    int port;

    void regs_callBack(const std_msgs::UInt16MultiArray::ConstPtr &regs_data);
    void coils_callBack(const std_msgs::ByteMultiArray::ConstPtr &coils_data);
};

plc_modbus_manager::plc_modbus_manager() {

    regs_read = node.advertise<std_msgs::UInt16MultiArray>("modbus/regs_read",100);
    regs_write = node.subscribe<std_msgs::UInt16MultiArray>("modbus/regs_write", 100, &plc_modbus_manager::regs_callBack, this);
    coils_read = node.advertise<std_msgs::ByteMultiArray>("modbus/coils_read",100);
    coils_write = node.subscribe<std_msgs::ByteMultiArray>("modbus/coils_write",100, &plc_modbus_manager::coils_callBack, this);

    node.param<std::string>("modbus/ip",ip_address,"192.168.0.100");
    node.param("modbus/port",port,502);

    if(!node.getParam("plc_modbus_node/regs_addr",regs_addr)){
        ROS_WARN("No reg addrs given!");
    }
    if(!node.getParam("plc_modbus_node/coils_addr",coils_addr)){
        ROS_WARN("No coil addrs given!");
    }

    ROS_INFO("Connecting to modbus device on %s/%d",ip_address.c_str(),port);
    plc = modbus_new_tcp(ip_address.c_str(),port);
    if (plc == NULL) {
        ROS_FATAL("Unable to allocate libmodbus context\n");
        return;
    }
    if(modbus_connect(plc)==-1){
        ROS_FATAL("Failed to connect to modbus device!!!");
        ROS_FATAL("%s",modbus_strerror(errno));
        modbus_free(plc);
        return;
    }else{
        ROS_INFO("Connection to modbus device established");
    }

    ros::Rate loop_rate(10);

    while(ros::ok()){
        regs_val.data.clear();
        coils_val.data.clear();

        for(int i=0;i<regs_addr.size();i++){
            uint16_t temp[1]={0};
            if(modbus_read_registers(plc, regs_addr.at(i), 1, temp) == -1){
                ROS_ERROR("Unable to read reg addr:%d",regs_addr.at(i));
                ROS_ERROR("%s",modbus_strerror(errno));
            }else{
                regs_val.data.push_back(temp[0]);
            }
        }
        if(regs_val.data.size()>0) {
            regs_read.publish(regs_val);
        }

        for(int i=0;i<coils_addr.size();i++){
            uint8_t temp[1]={0};
            if(modbus_read_bits(plc, coils_addr.at(i), 1, temp)==-1){
                ROS_ERROR("Unable to read coil addr:%d",regs_addr.at(i));
                ROS_ERROR("%s",modbus_strerror(errno));
            }else{
                coils_val.data.push_back(temp[0]);
            }
        }
        if(coils_val.data.size()>0) {
            coils_read.publish(coils_val);
        }

        ros::spinOnce();
        loop_rate.sleep();
    }

    modbus_close(plc);
    modbus_free(plc);
    return;
}

void plc_modbus_manager::regs_callBack(const std_msgs::UInt16MultiArray::ConstPtr &regs_data){
    if(regs_data->data.size()!=regs_addr.size()){
        ROS_ERROR("Number of regs to write to number of values provided do not match");
        return;
    }
    for(int i=0;i<regs_data->data.size();i++){
        ROS_DEBUG("regs_out[%d]:%u",i,regs_data->data.at(i));
        if(modbus_write_registers(plc, regs_addr.at(i), 1, (const uint16_t *) regs_data->data.at(i)) == -1){
            ROS_ERROR("Modbus reg write failed at addr:%d with value:%u",regs_addr.at(i),regs_data->data.at(i));
            ROS_ERROR("%s",modbus_strerror(errno));
        }
    }
}

void plc_modbus_manager::coils_callBack(const std_msgs::ByteMultiArray::ConstPtr &coils_data){
    if(coils_data->data.size()!=coils_addr.size()){
        ROS_ERROR("Number of coils to write to number of values provided do not match");
        return;
    }
    for(int i=0;i<coils_data->data.size();i++){
        ROS_DEBUG("regs_out[%d]:%u",i,coils_data->data.at(i));
        if(modbus_write_bits(plc, coils_addr.at(i), 1, (const uint8_t *) coils_data->data.at(i)) == -1){
            ROS_ERROR("Modbus coil write failed at addr:%d with value:%u",coils_addr.at(i),coils_data->data.at(i));
            ROS_ERROR("%s",modbus_strerror(errno));
        }
    }
}

int main(int argc, char **argv){
    ros::init(argc,argv,"ros_plc_modbus");
    plc_modbus_manager mm;
    return 0;
}
<|endoftext|>


<commit_before>#ifndef _A_STAR_HPP_
#define _A_STAR_HPP_

#include <cassert>
#include <vector>

#include <boost/none.hpp>
#include <boost/optional.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/unordered_map.hpp>
#include <boost/utility.hpp>

#include "search/Constants.hpp"
#include "search/BucketPriorityQueue.hpp"
#include "util/PointerOps.hpp"

template <
  class DomainT,
  class NodeT
  >
class AStar : boost::noncopyable
{
public:
  typedef DomainT Domain;
  typedef NodeT Node;

private:
  typedef BucketPriorityQueue<Node> Open;
  typedef boost::optional<typename Open::ItemPointer> MaybeItemPointer;

  typedef boost::unordered_map<
    Node *,
    MaybeItemPointer,
    PointerHash<Node>,
    PointerEq<Node>
    , boost::fast_pool_allocator< std::pair<Node * const, MaybeItemPointer > >
    > Closed;

  typedef typename Closed::iterator ClosedIterator;
  typedef typename Closed::const_iterator ClosedConstIterator;

private:
  Open open;
  Closed closed;

  const Node * goal;
  bool searched;

  Domain &domain;

  unsigned num_expanded;
  unsigned num_generated;

  boost::pool<> node_pool;

public:
  AStar(Domain &domain)
    : open()
    , closed(INITIAL_CLOSED_SET_SIZE)
    , goal(NULL)
    , searched(false)
    , domain(domain)
    , num_expanded(0)
    , num_generated(0)
    , node_pool(sizeof(Node))
  {
  }

  ~AStar()
  {
  }

  void search()
  {
    if (searched)
      return;
    searched = true;

    std::vector<Node *> succs;    // re-use a stack-allocated vector
                                  // for successor nodes, thus
                                  // avoiding repeated heap
                                  // allocation.

    {
      assert(all_closed_item_ptrs_valid());
      Node *start_node = new (node_pool.malloc()) Node(domain.get_start_state(),
                                                       0,
                                                       0,
                                                       NULL);
      domain.compute_heuristic(*start_node);
      MaybeItemPointer open_ptr = open.push(start_node);
      assert(open_ptr);
      closed[start_node] = open_ptr;
      assert(closed.find(start_node) != closed.end());
      assert(open.size() == 1);
      assert(closed.size() == 1);
      assert(all_closed_item_ptrs_valid());
    }

    while (!open.empty())
    {
#ifdef OUTPUT_SEARCH_PROGRESS
      if (get_num_expanded() % 1000000 == 0) {
        std::cerr << get_num_expanded() << " total nodes expanded" << std::endl
                  << get_num_generated() << " total nodes generated" << std::endl;
      }
#endif

      Node *n = open.top();
      open.pop();
      assert(closed.find(n) != closed.end());
      closed[n] = boost::none;
      assert(all_closed_item_ptrs_valid());

      if (domain.is_goal(n->get_state())) {
        goal = n;
        return;
      }

      domain.compute_successors(*n, succs, node_pool);
      num_expanded += 1;
      num_generated += succs.size();

      for (unsigned succ_i = 0; succ_i < succs.size(); succ_i += 1)
      {
        process_child(n, succs[succ_i]);
      } /* end for */
    } /* end while */
  }

  const Node * get_goal() const
  {
    return goal;
  }

  const Domain & get_domain() const
  {
    return domain;
  }

  unsigned get_num_generated() const
  {
    return num_generated;
  }

  unsigned get_num_expanded() const
  {
    return num_expanded;
  }

  void output_statistics(std::ostream &o) const
  {
    o << open.size() << " nodes in open at end of search" << std::endl
      << closed.size() << " nodes in closed at end of search" << std::endl;
  }

private:
  void process_child(Node *parent, Node *child)
  {
    domain.compute_heuristic(*parent, *child);
    assert(open.size() <= closed.size());
    assert(all_closed_item_ptrs_valid());

    ClosedIterator closed_it = closed.find(child);
    if (closed_it == closed.end()) {
      // The child has not been generated before.
      closed[child] = open.push(child);
    }
    else if (closed_it->second && child->get_f() < closed_it->first->get_f()) {
      // A worse version of the child is in the open list.
      open.erase(*closed_it->second);  // remove old one from the open list
      // free the old, worse copy
      node_pool.free(closed_it->first);
      closed.erase(closed_it);

      closed[child] = open.push(child);  // insert better version of child
    }
    else {
      // The child has either already been expanded, or is worse
      // than the version in the open list.
      node_pool.free(child);
    }

    assert(all_closed_item_ptrs_valid());
    assert(open.invariants_satisfied());
  }

  bool all_closed_item_ptrs_valid() const
  {
#ifdef CHECK_ALL_CLOSED_ITEM_PTRS_VALID
    std::cerr << "checking if item pointers are valid" << std::endl
              << "  " << closed.size() << " pointers to check" << std::endl;
    for (ClosedConstIterator closed_it = closed.begin();
         closed_it != closed.end();
         ++closed_it) {
      if ( closed_it->second && !open.valid_item_pointer(*closed_it->second) )
        return false;
    }
    return true;
#else
    return true;
#endif
  }
};

#endif /* !_A_STAR_HPP_ */
<commit_msg>Lots of comments for A*; more statistic reporting.<commit_after>#ifndef _A_STAR_HPP_
#define _A_STAR_HPP_

#include <cassert>
#include <vector>

#include <boost/none.hpp>
#include <boost/optional.hpp>
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
#include <boost/unordered_map.hpp>
#include <boost/utility.hpp>

#include "search/Constants.hpp"
#include "search/BucketPriorityQueue.hpp"
#include "util/PointerOps.hpp"

template <
  class DomainT,
  class NodeT
  >
class AStar : boost::noncopyable
{
public:
  // The problem domain type.  A domain is responsible for computing
  // successors, computing heuristic estimates, goal testing, and
  // giving the start state for the problem.
  typedef DomainT Domain;
  typedef NodeT Node;

private:
  // The priority queue type for the open list.
  typedef BucketPriorityQueue<Node> Open;
  typedef boost::optional<typename Open::ItemPointer> MaybeItemPointer;

  // The `closed set' type.  This is a misnomer, as this
  // implementation of A* does pruning at node generation rather than
  // at node expansion, causing the `closed set' to contain nodes
  // whose states have been generated before, but not expanded.
  typedef boost::unordered_map<
    Node *,
    MaybeItemPointer,
    PointerHash<Node>,
    PointerEq<Node>
    , boost::fast_pool_allocator< std::pair<Node * const, MaybeItemPointer > >
    > Closed;

  typedef typename Closed::iterator ClosedIterator;
  typedef typename Closed::const_iterator ClosedConstIterator;

private:
  // An f-ordered priority queue of nodes to expand.  Invariant: for
  // any given state, at most one node with that state will be on the
  // open list.
  Open open;
  // The `closed set', containing all the nodes whose states have been
  // generated but not yet expanded.  `closed' maps nodes to possible
  // pointers into the open list, used to maintain the at-most-one
  // invariant of the open list.
  Closed closed;

  // The goal node.  NULL if no solution found or if the search has
  // not yet been performed.
  const Node * goal;
  // Has the search been performed yet?
  bool searched;

  // The problem domain.
  Domain &domain;

  // Search statistic for number of nodes expanded.
  unsigned num_expanded;
  // Search statistic for number of nodes generated.
  unsigned num_generated;

  // A memory pool to allow fast node allocation and deallocation.
  boost::pool<> node_pool;

public:
  AStar(Domain &domain)
    : open()
    , closed(INITIAL_CLOSED_SET_SIZE)
    , goal(NULL)
    , searched(false)
    , domain(domain)
    , num_expanded(0)
    , num_generated(0)
    , node_pool(sizeof(Node))
  {
  }

  ~AStar()
  {
  }

  void search()
  {
    if (searched)
      return;
    searched = true;

    std::vector<Node *> succs;    // re-use a stack-allocated vector
                                  // for successor nodes, thus
                                  // avoiding repeated heap
                                  // allocation.

    {
      assert(all_closed_item_ptrs_valid());
      Node *start_node = new (node_pool.malloc()) Node(domain.get_start_state(),
                                                       0,
                                                       0,
                                                       NULL);
      domain.compute_heuristic(*start_node);
      MaybeItemPointer open_ptr = open.push(start_node);
      assert(open_ptr);
      closed[start_node] = open_ptr;
      assert(closed.find(start_node) != closed.end());
      assert(open.size() == 1);
      assert(closed.size() == 1);
      assert(all_closed_item_ptrs_valid());
    }

    while (!open.empty())
    {
#ifdef OUTPUT_SEARCH_PROGRESS
      if (get_num_expanded() % 1000000 == 0) {
        std::cerr << get_num_expanded() << " total nodes expanded" << std::endl
                  << get_num_generated() << " total nodes generated" << std::endl;
      }
#endif

      Node *n = open.top();
      open.pop();
      assert(closed.find(n) != closed.end());
      closed[n] = boost::none;     // eliminate the pointer into the open list
      assert(all_closed_item_ptrs_valid());

      if (domain.is_goal(n->get_state())) {
        goal = n;
        return;
      }

      domain.compute_successors(*n, succs, node_pool);
      num_expanded += 1;
      num_generated += succs.size();

      for (unsigned succ_i = 0; succ_i < succs.size(); succ_i += 1)
      {
        process_child(n, succs[succ_i]);
      } /* end for */
    } /* end while */
  }

  const Node * get_goal() const
  {
    return goal;
  }

  const Domain & get_domain() const
  {
    return domain;
  }

  unsigned get_num_generated() const
  {
    return num_generated;
  }

  unsigned get_num_expanded() const
  {
    return num_expanded;
  }

  void output_statistics(std::ostream &o) const
  {
    o << open.size() << " nodes in open at end of search" << std::endl
      << closed.size() << " nodes in closed at end of search" << std::endl;

    if (get_goal() != NULL) {
      const typename Node::Cost goal_f = get_goal()->get_f();
      unsigned num_expanded_less_than_goal_f = 0;
      for (ClosedConstIterator closed_it = closed.begin();
           closed_it != closed.end();
           closed_it++)
      {
        if (closed_it->first->get_f() < goal_f) {
          assert(!closed_it->second);
          num_expanded_less_than_goal_f += 1;
        }
      }

      o << "goal f-value is " << goal_f << std::endl;
      o << num_expanded_less_than_goal_f
        << " nodes expanded with f-value less than goal's" << std::endl;
    }
  }

private:
  void process_child(Node *parent, Node *child)
  {
    domain.compute_heuristic(*parent, *child);
    assert(open.size() <= closed.size());
    assert(all_closed_item_ptrs_valid());

    ClosedIterator closed_it = closed.find(child);
    if (closed_it == closed.end()) {
      // The child has not been generated before.
      closed[child] = open.push(child);
    }
    else if (closed_it->second && child->get_f() < closed_it->first->get_f()) {
      // A worse version of the child is in the open list.
      open.erase(*closed_it->second);  // remove old one from the open list
      // free the old, worse copy
      node_pool.free(closed_it->first);
      closed.erase(closed_it);

      closed[child] = open.push(child);  // insert better version of child
    }
    else {
      // The child has either already been expanded, or is worse
      // than the version in the open list.
      node_pool.free(child);
    }

    assert(all_closed_item_ptrs_valid());
    assert(open.invariants_satisfied());
  }

  bool all_closed_item_ptrs_valid() const
  {
#ifdef CHECK_ALL_CLOSED_ITEM_PTRS_VALID
    std::cerr << "checking if item pointers are valid" << std::endl
              << "  " << closed.size() << " pointers to check" << std::endl;
    for (ClosedConstIterator closed_it = closed.begin();
         closed_it != closed.end();
         ++closed_it) {
      if ( closed_it->second && !open.valid_item_pointer(*closed_it->second) )
        return false;
    }
    return true;
#else
    return true;
#endif
  }
};

#endif /* !_A_STAR_HPP_ */
<|endoftext|>