323 lines
9.0 KiB
C++
323 lines
9.0 KiB
C++
/*
|
|
* 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/calendar-scheduler.h"
|
|
#include "ns3/config.h"
|
|
#include "ns3/heap-scheduler.h"
|
|
#include "ns3/list-scheduler.h"
|
|
#include "ns3/map-scheduler.h"
|
|
#include "ns3/simulator.h"
|
|
#include "ns3/string.h"
|
|
#include "ns3/test.h"
|
|
|
|
#include <chrono> // seconds, milliseconds
|
|
#include <ctime>
|
|
#include <list>
|
|
#include <thread> // sleep_for
|
|
#include <utility>
|
|
|
|
using namespace ns3;
|
|
|
|
/// Maximum number of threads.
|
|
constexpr int MAXTHREADS = 64;
|
|
|
|
/**
|
|
* \file
|
|
* \ingroup threaded-tests
|
|
* Threaded events test suite
|
|
*/
|
|
|
|
/**
|
|
* \ingroup core-tests
|
|
* \defgroup threaded-tests Threaded events tests
|
|
*/
|
|
|
|
/**
|
|
* \ingroup threaded-tests
|
|
*
|
|
* \brief Check threaded event handling with various thread number, schedulers, and simulator
|
|
* types.
|
|
*/
|
|
class ThreadedSimulatorEventsTestCase : public TestCase
|
|
{
|
|
public:
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* \param schedulerFactory The scheduler factory.
|
|
* \param simulatorType The simulator type.
|
|
* \param threads The number of threads.
|
|
*/
|
|
ThreadedSimulatorEventsTestCase(ObjectFactory schedulerFactory,
|
|
const std::string& simulatorType,
|
|
unsigned int threads);
|
|
/**
|
|
* Event A
|
|
* \param a The Event parameter.
|
|
*/
|
|
void EventA(int a);
|
|
/**
|
|
* Event B
|
|
* \param b The Event parameter.
|
|
*/
|
|
void EventB(int b);
|
|
/**
|
|
* Event C
|
|
* \param c The Event parameter.
|
|
*/
|
|
void EventC(int c);
|
|
/**
|
|
* Event D
|
|
* \param d The Event parameter.
|
|
*/
|
|
void EventD(int d);
|
|
/**
|
|
* No-op function, records the thread that called it.
|
|
* \param threadno The thread number.
|
|
*/
|
|
void DoNothing(unsigned int threadno);
|
|
/**
|
|
* Schedule a thread.
|
|
* \param context The context.
|
|
*/
|
|
static void SchedulingThread(std::pair<ThreadedSimulatorEventsTestCase*, unsigned int> context);
|
|
/**
|
|
* End the thread execution.
|
|
*/
|
|
void End();
|
|
uint64_t m_a; //!< The value incremented when EventA is called.
|
|
uint64_t m_b; //!< The value incremented when EventB is called.
|
|
uint64_t m_c; //!< The value incremented when EventC is called.
|
|
uint64_t m_d; //!< The value incremented when EventD is called.
|
|
unsigned int m_threads; //!< The number of threads.
|
|
bool m_threadWaiting[MAXTHREADS]; //!< Threads waiting to be scheduled.
|
|
bool m_stop; //!< Stop variable.
|
|
ObjectFactory m_schedulerFactory; //!< Scheduler factory.
|
|
std::string m_simulatorType; //!< Simulator type.
|
|
std::string m_error; //!< Error condition.
|
|
std::list<std::thread> m_threadlist; //!< Thread list.
|
|
|
|
private:
|
|
void DoSetup() override;
|
|
void DoRun() override;
|
|
void DoTeardown() override;
|
|
};
|
|
|
|
ThreadedSimulatorEventsTestCase::ThreadedSimulatorEventsTestCase(ObjectFactory schedulerFactory,
|
|
const std::string& simulatorType,
|
|
unsigned int threads)
|
|
: TestCase("Check threaded event handling with " + std::to_string(threads) + " threads, " +
|
|
schedulerFactory.GetTypeId().GetName() + " scheduler, in " + simulatorType),
|
|
m_threads(threads),
|
|
m_schedulerFactory(schedulerFactory),
|
|
m_simulatorType(simulatorType)
|
|
{
|
|
}
|
|
|
|
void
|
|
ThreadedSimulatorEventsTestCase::End()
|
|
{
|
|
m_stop = true;
|
|
for (auto& thread : m_threadlist)
|
|
{
|
|
if (thread.joinable())
|
|
{
|
|
thread.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(threadno,
|
|
MicroSeconds(1),
|
|
&ThreadedSimulatorEventsTestCase::DoNothing,
|
|
me,
|
|
threadno);
|
|
while (!me->m_stop && me->m_threadWaiting[threadno])
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::nanoseconds(500));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ThreadedSimulatorEventsTestCase::DoNothing(unsigned int threadno)
|
|
{
|
|
if (!m_error.empty())
|
|
{
|
|
m_error = "Bad threaded scheduling";
|
|
}
|
|
m_threadWaiting[threadno] = false;
|
|
}
|
|
|
|
void
|
|
ThreadedSimulatorEventsTestCase::EventA(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::EventB, this, a + 1);
|
|
}
|
|
|
|
void
|
|
ThreadedSimulatorEventsTestCase::EventB(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::EventC, this, b + 1);
|
|
}
|
|
|
|
void
|
|
ThreadedSimulatorEventsTestCase::EventC(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::EventD, this, c + 1);
|
|
}
|
|
|
|
void
|
|
ThreadedSimulatorEventsTestCase::EventD(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::EventA,
|
|
this,
|
|
d + 1);
|
|
}
|
|
}
|
|
|
|
void
|
|
ThreadedSimulatorEventsTestCase::DoSetup()
|
|
{
|
|
if (!m_simulatorType.empty())
|
|
{
|
|
Config::SetGlobal("SimulatorImplementationType", StringValue(m_simulatorType));
|
|
}
|
|
|
|
m_error = "";
|
|
|
|
m_a = m_b = m_c = m_d = 0;
|
|
}
|
|
|
|
void
|
|
ThreadedSimulatorEventsTestCase::DoTeardown()
|
|
{
|
|
m_threadlist.clear();
|
|
|
|
Config::SetGlobal("SimulatorImplementationType", StringValue("ns3::DefaultSimulatorImpl"));
|
|
}
|
|
|
|
void
|
|
ThreadedSimulatorEventsTestCase::DoRun()
|
|
{
|
|
m_stop = false;
|
|
Simulator::SetScheduler(m_schedulerFactory);
|
|
|
|
Simulator::Schedule(MicroSeconds(10), &ThreadedSimulatorEventsTestCase::EventA, this, 1);
|
|
Simulator::Schedule(Seconds(1), &ThreadedSimulatorEventsTestCase::End, this);
|
|
|
|
for (unsigned int i = 0; i < m_threads; ++i)
|
|
{
|
|
m_threadlist.emplace_back(
|
|
&ThreadedSimulatorEventsTestCase::SchedulingThread,
|
|
std::pair<ThreadedSimulatorEventsTestCase*, unsigned int>(this, i));
|
|
}
|
|
|
|
Simulator::Run();
|
|
Simulator::Destroy();
|
|
|
|
NS_TEST_EXPECT_MSG_EQ(m_error.empty(), true, m_error);
|
|
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");
|
|
}
|
|
|
|
/**
|
|
* \ingroup threaded-tests
|
|
*
|
|
* \brief The threaded simulator Test Suite.
|
|
*/
|
|
class ThreadedSimulatorTestSuite : public TestSuite
|
|
{
|
|
public:
|
|
ThreadedSimulatorTestSuite()
|
|
: TestSuite("threaded-simulator")
|
|
{
|
|
std::string simulatorTypes[] = {
|
|
"ns3::RealtimeSimulatorImpl",
|
|
"ns3::DefaultSimulatorImpl",
|
|
};
|
|
std::string schedulerTypes[] = {
|
|
"ns3::ListScheduler",
|
|
"ns3::HeapScheduler",
|
|
"ns3::MapScheduler",
|
|
"ns3::CalendarScheduler",
|
|
};
|
|
unsigned int threadCounts[] = {0, 2, 10, 20};
|
|
ObjectFactory factory;
|
|
|
|
for (auto& simulatorType : simulatorTypes)
|
|
{
|
|
for (auto& schedulerType : schedulerTypes)
|
|
{
|
|
for (auto& threadCount : threadCounts)
|
|
{
|
|
factory.SetTypeId(schedulerType);
|
|
AddTestCase(
|
|
new ThreadedSimulatorEventsTestCase(factory, simulatorType, threadCount),
|
|
TestCase::QUICK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/// Static variable for test initialization.
|
|
static ThreadedSimulatorTestSuite g_threadedSimulatorTestSuite;
|