/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 INRIA
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* 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 General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Claudio Freire <claudio-daniel.freire@inria.fr>
*/
#include "ns3/test.h"
#include "ns3/simulator.h"
#include "ns3/list-scheduler.h"
#include "ns3/heap-scheduler.h"
#include "ns3/map-scheduler.h"
#include "ns3/calendar-scheduler.h"
#include "ns3/config.h"
#include "ns3/string.h"
#include "ns3/system-thread.h"
#include <ctime>
#include <list>
#include <utility>
namespace ns3 {
#define MAXTHREADS 64
class ThreadedSimulatorEventsTestCase : public TestCase
{
public:
ThreadedSimulatorEventsTestCase (ObjectFactory schedulerFactory, const std::string &simulatorType, unsigned int threads);
void A (int a);
void B (int b);
void C (int c);
void D (int d);
void DoNothing (unsigned int threadno);
static void SchedulingThread (std::pair<ThreadedSimulatorEventsTestCase *, unsigned int> context);
void End (void);
uint64_t m_b;
uint64_t m_a;
uint64_t m_c;
uint64_t m_d;
unsigned int m_threads;
bool m_threadWaiting[MAXTHREADS];
bool m_stop;
ObjectFactory m_schedulerFactory;
std::string m_simulatorType;
std::string m_error;
std::list<Ptr<SystemThread> > m_threadlist;
private:
virtual void DoSetup (void);
virtual void DoRun (void);
virtual void DoTeardown (void);
};
ThreadedSimulatorEventsTestCase::ThreadedSimulatorEventsTestCase (ObjectFactory schedulerFactory, const std::string &simulatorType, unsigned int threads)
: TestCase ("Check that threaded event handling is working with " +
schedulerFactory.GetTypeId ().GetName () + " in " + simulatorType),
m_threads (threads),
m_schedulerFactory (schedulerFactory),
m_simulatorType (simulatorType)
{
}
void
ThreadedSimulatorEventsTestCase::End (void)
{
m_stop = true;
for (std::list<Ptr<SystemThread> >::iterator it2 = m_threadlist.begin(); it2 != m_threadlist.end(); ++it2)
{
(*it2)->Join();
}
}
void
ThreadedSimulatorEventsTestCase::SchedulingThread (std::pair<ThreadedSimulatorEventsTestCase *, unsigned int> context)
{
ThreadedSimulatorEventsTestCase *me = context.first;
unsigned int threadno = context.second;
while (!me->m_stop)
{
me->m_threadWaiting[threadno] = true;
Simulator::ScheduleWithContext (uint32_t (-1),
MicroSeconds (1),
&ThreadedSimulatorEventsTestCase::DoNothing, me, threadno);
while (!me->m_stop && me->m_threadWaiting[threadno])
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 500;
nanosleep (&ts, NULL);
}
}
}
void
ThreadedSimulatorEventsTestCase::DoNothing (unsigned int threadno)
{
if (!m_error.empty())
{
m_error = "Bad threaded scheduling";
}
m_threadWaiting[threadno] = false;
}
void
ThreadedSimulatorEventsTestCase::A (int a)
{
if (m_a != m_b || m_a != m_c || m_a != m_d)
{
m_error = "Bad scheduling";
Simulator::Stop();
};
++m_a;
Simulator::Schedule (MicroSeconds (10),
&ThreadedSimulatorEventsTestCase::B, this, a+1);
}
void
ThreadedSimulatorEventsTestCase::B (int b)
{
if (m_a != (m_b+1) || m_a != (m_c+1) || m_a != (m_d+1))
{
m_error = "Bad scheduling";
Simulator::Stop();
};
++m_b;
Simulator::Schedule (MicroSeconds (10),
&ThreadedSimulatorEventsTestCase::C, this, b+1);
}
void
ThreadedSimulatorEventsTestCase::C (int c)
{
if (m_a != m_b || m_a != (m_c+1) || m_a != (m_d+1))
{
m_error = "Bad scheduling";
Simulator::Stop();
};
++m_c;
Simulator::Schedule (MicroSeconds (10),
&ThreadedSimulatorEventsTestCase::D, this, c+1);
}
void
ThreadedSimulatorEventsTestCase::D (int d)
{
if (m_a != m_b || m_a != m_c || m_a != (m_d+1))
{
m_error = "Bad scheduling";
Simulator::Stop();
};
++m_d;
if (m_stop)
{
Simulator::Stop();
}
else
{
Simulator::Schedule (MicroSeconds (10),
&ThreadedSimulatorEventsTestCase::A, this, d+1);
}
}
void
ThreadedSimulatorEventsTestCase::DoSetup (void)
{
if (!m_simulatorType.empty())
{
Config::SetGlobal ("SimulatorImplementationType", StringValue (m_simulatorType));
}
m_error = "";
m_a =
m_b =
m_c =
m_d = 0;
for (unsigned int i=0; i < m_threads; ++i)
{
m_threadlist.push_back(
Create<SystemThread> (MakeBoundCallback (
&ThreadedSimulatorEventsTestCase::SchedulingThread,
std::pair<ThreadedSimulatorEventsTestCase *, unsigned int>(this,i) )) );
}
}
void
ThreadedSimulatorEventsTestCase::DoTeardown (void)
{
m_threadlist.clear();
Config::SetGlobal ("SimulatorImplementationType", StringValue ("ns3::DefaultSimulatorImpl"));
}
void
ThreadedSimulatorEventsTestCase::DoRun (void)
{
m_stop = false;
Simulator::SetScheduler (m_schedulerFactory);
Simulator::Schedule (MicroSeconds (10), &ThreadedSimulatorEventsTestCase::A, this, 1);
Simulator::Schedule (Seconds (1), &ThreadedSimulatorEventsTestCase::End, this);
for (std::list<Ptr<SystemThread> >::iterator it = m_threadlist.begin(); it != m_threadlist.end(); ++it)
{
(*it)->Start();
}
Simulator::Run ();
Simulator::Destroy ();
NS_TEST_EXPECT_MSG_EQ (m_error.empty(), true, m_error.c_str());
NS_TEST_EXPECT_MSG_EQ (m_a, m_b, "Bad scheduling");
NS_TEST_EXPECT_MSG_EQ (m_a, m_c, "Bad scheduling");
NS_TEST_EXPECT_MSG_EQ (m_a, m_d, "Bad scheduling");
}
class ThreadedSimulatorTestSuite : public TestSuite
{
public:
ThreadedSimulatorTestSuite ()
: TestSuite ("threaded-simulator")
{
std::string simulatorTypes[] = {
#ifdef HAVE_RT
"ns3::RealtimeSimulatorImpl",
#endif
"ns3::DefaultSimulatorImpl"
};
std::string schedulerTypes[] = {
"ns3::ListScheduler",
"ns3::HeapScheduler",
"ns3::MapScheduler",
"ns3::CalendarScheduler"
};
unsigned int threadcounts[] = {
0,
2,
10,
20
};
ObjectFactory factory;
for (unsigned int i=0; i < (sizeof(simulatorTypes) / sizeof(simulatorTypes[0])); ++i)
{
for (unsigned int j=0; j < (sizeof(threadcounts) / sizeof(threadcounts[0])); ++j)
{
for (unsigned int k=0; k < (sizeof(schedulerTypes) / sizeof(schedulerTypes[0])); ++k)
{
factory.SetTypeId(schedulerTypes[k]);
AddTestCase (new ThreadedSimulatorEventsTestCase (factory, simulatorTypes[i], threadcounts[j]));
}
}
}
}
} g_threadedSimulatorTestSuite;
} // namespace ns3