core: Replace SystemCondition class with STL

This commit is contained in:
Eduardo Almeida
2021-10-24 01:33:08 +01:00
parent 85ad3961fc
commit f4d296baaa
6 changed files with 37 additions and 407 deletions

View File

@@ -33,8 +33,9 @@
#include "ns3/core-module.h"
#include <ctime>
#include <chrono>
#include <iomanip>
#include <thread>
#include <string>
@@ -92,15 +93,12 @@ public:
int64x64_t ratio = (Simulator::Now () / m_interval) / 10;
bool even = (ratio.GetHigh () % 2);
Time work = m_wait * (even ? 3 : 1);
m_condition.TimedWait ( work.GetNanoSeconds () );
std::this_thread::sleep_for (std::chrono::nanoseconds (work.GetNanoSeconds ()));
}
private:
/** The random number generator for the interval between events. */
Ptr<RandomVariableStream> m_rng;
/** Timer to represent workload. */
SystemCondition m_condition;
/** Mean inter-event time. */
Time m_wait;
/** Time between switching workloads. */
@@ -145,4 +143,5 @@ main (int argc, char ** argv)
Simulator::Run ();
Simulator::Destroy ();
return 0;
}

View File

@@ -1,126 +0,0 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2008 University of Washington
*
* 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
*/
#ifndef SYSTEM_CONDITION_H
#define SYSTEM_CONDITION_H
#include "ptr.h"
/**
* @file
* @ingroup thread
* ns3::SystemCondition declaration.
*/
namespace ns3 {
/**
* @ingroup system
* @defgroup thread Threading and Signaling.
*
* System-independent interfaces to threads, signal conditions, and mutex.
*/
class SystemConditionPrivate;
/**
* @ingroup thread
* @brief A class which provides a relatively platform-independent
* conditional-wait thread synchronization primitive.
*
* It is often desirable to have a mechanism by which a thread can suspend its
* execution and relinquish the process until some condition to becomes true.
* We provide platform-independent access to this OS-dependent capability with
* the SystemCondition class.
*
* There are two ways to tell the underlying primitive that the condition has
* become true: Signal and Broadcast. Signal will only wake up one thread
* waiting on the condition (according to the OS scheduling policy);
* Broadcast will wake up all of the threads waiting on the condition
* (cf. "The Thundering Herd").
*
* In order to wait for the underlying condition, you also have two
* alternatives: Wait and TimedWait. The Wait call will wait forever for the
* condition to become true; but the TimedWait has a timeout.
*
* The condition underlying this class is a simple boolean variable. It is
* set to false in each call to Wait and TimedWait. It is set to true in each
* call to Signal and Broadcast. This is a fairly simple-minded condition
* designed for
*
* A typical use case will be to call Wait() or TimedWait() in one thread
* context and put the processor to sleep until an event happens somewhere
* else that
*/
// The last two paragraphs are truncated in the earliest commit of this code:
// 2008-07-15 Craig Dowell add system threads and synchronization primitives
class SystemCondition
{
public:
SystemCondition ();
~SystemCondition ();
/**
* Set the value of the underlying condition.
* @param [in] condition value
*/
void SetCondition (bool condition);
/**
* Get the value of the underlying condition.
* \returns The state of the condition
*/
bool GetCondition (void);
/**
* Release one thread if waiting for the condition to be true. If you want
* a waiting thread to return, you should have done a SetCondition (true)
* prior to calling.
*/
void Signal (void);
/**
* Release all threads waiting for the condition to be true. If you want
* all waiting threads to return, you should have done a SetCondition (true)
* prior to calling.
*/
void Broadcast (void);
/**
* Wait, possibly forever, for the condition to be true.
*/
void Wait (void);
/**
* Wait a maximum of ns nanoseconds for the condition to be true. If the
* wait times out, return true else return false.
* @param [in] ns maximum of nanoseconds to wait
* @returns \c true if the timer expired, otherwise return \c false.
*/
bool TimedWait (uint64_t ns);
private:
/** The (system-dependent) implementation. */
SystemConditionPrivate * m_priv;
};
} // namespace ns3
#endif /* SYSTEM_CONDITION_H */

View File

@@ -1,263 +0,0 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2008 University of Washington
*
* 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
*/
#include <pthread.h>
#include <cerrno> // for ETIMEDOUT
#include <time.h> // for timespec
#include <sys/time.h> // for timeval, gettimeofday
#include "fatal-error.h"
#include "system-condition.h"
#include "log.h"
/**
* \file
* \ingroup thread
* ns3::SystemCondition and ns3::SystemConditionPrivate implementations.
*/
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("SystemCondition");
/**
* \ingroup thread
* Implementation of SystemCondition for Unix-like systems.
*/
class SystemConditionPrivate
{
public:
/** Conversion from ns to s. */
static const uint64_t NS_PER_SEC = (uint64_t)1000000000;
/** Constructor. */
SystemConditionPrivate ();
/** Destructor. */
~SystemConditionPrivate ();
/**
* Set the condition.
*
* \param [in] condition The new condition value.
*/
void SetCondition (bool condition);
/**
* Get the condition value.
*
* \returns The condition value.
*/
bool GetCondition (void);
/** Signal the condition. */
void Signal (void);
/** Broadcast the condition. */
void Broadcast (void);
/**
* Unset the condition, then wait for another thread
* to set it with SetCondition. */
void Wait (void);
/**
* Unset the condition, then wait for a limited amount of wall-clock
* time for another thread to set it with SetCondition.
*
* \param [in] ns Maximum time to wait, in ns.
* \returns \c true if the condition timed out; \c false if the other
* thread set it.
*/
bool TimedWait (uint64_t ns);
private:
/** Mutex controlling access to the condition. */
pthread_mutex_t m_mutex;
/** The pthread condition variable. */
pthread_cond_t m_cond;
/** The condition state. */
bool m_condition;
};
SystemConditionPrivate::SystemConditionPrivate ()
{
NS_LOG_FUNCTION (this);
m_condition = false;
pthread_mutexattr_t mAttr;
pthread_mutexattr_init (&mAttr);
//
// Linux and OS X (at least) have, of course chosen different names for the
// error checking flags just to make life difficult.
//
#if defined (PTHREAD_MUTEX_ERRORCHECK_NP)
pthread_mutexattr_settype (&mAttr, PTHREAD_MUTEX_ERRORCHECK_NP);
#else
pthread_mutexattr_settype (&mAttr, PTHREAD_MUTEX_ERRORCHECK);
#endif
pthread_mutex_init (&m_mutex, &mAttr);
pthread_condattr_t cAttr;
pthread_condattr_init (&cAttr);
pthread_condattr_setpshared (&cAttr, PTHREAD_PROCESS_PRIVATE);
pthread_cond_init (&m_cond, &cAttr);
}
SystemConditionPrivate::~SystemConditionPrivate ()
{
NS_LOG_FUNCTION (this);
pthread_mutex_destroy (&m_mutex);
pthread_cond_destroy (&m_cond);
}
void
SystemConditionPrivate::SetCondition (bool condition)
{
NS_LOG_FUNCTION (this << condition);
m_condition = condition;
}
bool
SystemConditionPrivate::GetCondition (void)
{
NS_LOG_FUNCTION (this);
return m_condition;
}
void
SystemConditionPrivate::Signal (void)
{
NS_LOG_FUNCTION (this);
pthread_mutex_lock (&m_mutex);
pthread_cond_signal (&m_cond);
pthread_mutex_unlock (&m_mutex);
}
void
SystemConditionPrivate::Broadcast (void)
{
NS_LOG_FUNCTION (this);
pthread_mutex_lock (&m_mutex);
pthread_cond_broadcast (&m_cond);
pthread_mutex_unlock (&m_mutex);
}
void
SystemConditionPrivate::Wait (void)
{
NS_LOG_FUNCTION (this);
pthread_mutex_lock (&m_mutex);
m_condition = false;
while (m_condition == false)
{
pthread_cond_wait (&m_cond, &m_mutex);
}
pthread_mutex_unlock (&m_mutex);
}
bool
SystemConditionPrivate::TimedWait (uint64_t ns)
{
NS_LOG_FUNCTION (this << ns);
struct timespec ts;
ts.tv_sec = ns / NS_PER_SEC;
ts.tv_nsec = ns % NS_PER_SEC;
struct timeval tv;
gettimeofday (&tv, NULL);
ts.tv_sec += tv.tv_sec;
ts.tv_nsec += tv.tv_usec * 1000;
if (ts.tv_nsec > (int64_t)NS_PER_SEC)
{
++ts.tv_sec;
ts.tv_nsec %= NS_PER_SEC;
}
int rc;
pthread_mutex_lock (&m_mutex);
while (m_condition == false)
{
rc = pthread_cond_timedwait (&m_cond, &m_mutex, &ts);
if (rc == ETIMEDOUT)
{
pthread_mutex_unlock (&m_mutex);
return true;
}
}
pthread_mutex_unlock (&m_mutex);
return false;
}
SystemCondition::SystemCondition ()
: m_priv (new SystemConditionPrivate ())
{
NS_LOG_FUNCTION (this);
}
SystemCondition::~SystemCondition ()
{
NS_LOG_FUNCTION (this);
delete m_priv;
}
void
SystemCondition::SetCondition (bool condition)
{
NS_LOG_FUNCTION (this << condition);
m_priv->SetCondition (condition);
}
bool
SystemCondition::GetCondition (void)
{
NS_LOG_FUNCTION (this);
return m_priv->GetCondition ();
}
void
SystemCondition::Signal (void)
{
NS_LOG_FUNCTION (this);
m_priv->Signal ();
}
void
SystemCondition::Broadcast (void)
{
NS_LOG_FUNCTION (this);
m_priv->Broadcast ();
}
void
SystemCondition::Wait (void)
{
NS_LOG_FUNCTION (this);
m_priv->Wait ();
}
bool
SystemCondition::TimedWait (uint64_t ns)
{
NS_LOG_FUNCTION (this << ns);
return m_priv->TimedWait (ns);
}
} // namespace ns3

View File

@@ -16,16 +16,17 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <ctime> // clock_t
#include <sys/time.h> // gettimeofday
// clock_getres: glibc < 2.17, link with librt
#include "log.h"
#include "system-condition.h"
#include "wall-clock-synchronizer.h"
#include <condition_variable>
#include <chrono>
#include <mutex>
/**
* \file
* \ingroup realtime
@@ -286,15 +287,21 @@ WallClockSynchronizer::DoSignal (void)
{
NS_LOG_FUNCTION (this);
m_condition.SetCondition (true);
m_condition.Signal ();
std::unique_lock<std::mutex> lock (m_mutex);
m_condition = true;
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details).
// Reference: https://en.cppreference.com/w/cpp/thread/condition_variable
lock.unlock ();
m_conditionVariable.notify_one ();
}
void
WallClockSynchronizer::DoSetCondition (bool cond)
{
NS_LOG_FUNCTION (this << cond);
m_condition.SetCondition (cond);
m_condition = cond;
}
void
@@ -323,7 +330,7 @@ WallClockSynchronizer::SpinWait (uint64_t ns)
{
return true;
}
if (m_condition.GetCondition ())
if (m_condition)
{
return false;
}
@@ -336,7 +343,14 @@ bool
WallClockSynchronizer::SleepWait (uint64_t ns)
{
NS_LOG_FUNCTION (this << ns);
return m_condition.TimedWait (ns);
std::unique_lock<std::mutex> lock (m_mutex);
bool finishedWaiting = m_conditionVariable.wait_for (
lock,
std::chrono::nanoseconds (ns), // Timeout
[this](){ return m_condition; }); // Wait condition
return finishedWaiting;
}
uint64_t

View File

@@ -19,9 +19,11 @@
#ifndef WALL_CLOCK_CLOCK_SYNCHRONIZER_H
#define WALL_CLOCK_CLOCK_SYNCHRONIZER_H
#include "system-condition.h"
#include "synchronizer.h"
#include <condition_variable>
#include <mutex>
/**
* @file
* @ingroup realtime
@@ -114,7 +116,8 @@ protected:
* scheduled event might be before the time we are waiting until, so we have
* to break out of both the SleepWait and the following SpinWait to go back
* and reschedule/resynchronize taking the new event into account. The
* SystemCondition we have saved in m_condition takes care of this for us.
* condition we have saved in m_condition, along with the condition variable
* m_conditionVariable take care of this for us.
*
* This call will return if the timeout expires OR if the condition is
* set @c true by a call to SetCondition (true) followed by a call to
@@ -194,8 +197,12 @@ protected:
/** Time recorded by DoEventStart. */
uint64_t m_nsEventStart;
/** Thread synchronizer. */
SystemCondition m_condition;
/** Condition variable for thread synchronizer. */
std::condition_variable m_conditionVariable;
/** Mutex controlling access to the condition variable. */
std::mutex m_mutex;
/** The condition state. */
bool m_condition;
};
} // namespace ns3

View File

@@ -31,7 +31,6 @@
#include "ns3/node.h"
#include "ns3/packet.h"
#include "ns3/ptr.h"
#include "ns3/system-condition.h"
#include "ns3/traced-callback.h"
#include "ns3/unix-fd-reader.h"