src/simulator/simulator.cc
author Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
Sat, 28 Oct 2006 21:21:34 +0200
changeset 143 552ff26b4224
parent 129 c6c58f17dd01
child 144 f07f6fb0f660
permissions -rw-r--r--
new Time implementation

/* -*- Mode:NS3; -*- */
/*
 * Copyright (c) 2005,2006 INRIA
 * All rights reserved.
 *
 * 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: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
 */

#include "simulator.h"
#include "scheduler.h"
#include "event-impl.h"

#include <math.h>
#include <cassert>
#include <fstream>
#include <list>
#include <vector>
#include <iostream>

#define noTRACE_SIMU 1

#ifdef TRACE_SIMU
#include <iostream>
# define TRACE(x) \
std::Cout << "SIMU TRACE " << Simulator::nowS () << " " << x << std::endl;
# define TRACE_S(x) \
std::cout << "SIMU TRACE " << x << std::endl;
#else /* TRACE_SIMU */
# define TRACE(format,...)
# define TRACE_S(format,...)
#endif /* TRACE_SIMU */


namespace ns3 {


class SimulatorPrivate {
public:
    SimulatorPrivate (Scheduler *events);
    ~SimulatorPrivate ();

    void EnableLogTo (char const *filename);

    bool IsFinished (void) const;
    Time Next (void) const;
    void Stop (void);
    void StopAt (Time const &time);
    EventId Schedule (Time const &time, EventImpl *event);
    void Remove (EventId ev);
    void Cancel (EventId ev);
    bool IsExpired (EventId ev);
    void Run (void);
    Time Now (void) const;

private:
    void ProcessOneEvent (void);
    uint64_t NextNs (void) const;

    typedef std::list<std::pair<EventImpl *,uint32_t> > Events;
    Events m_destroy;
    uint64_t m_stopAt;
    bool m_stop;
    Scheduler *m_events;
    uint32_t m_uid;
    uint32_t m_currentUid;
    uint64_t m_currentNs;
    std::ofstream m_log;
    std::ifstream m_inputLog;
    bool m_logEnable;
};




SimulatorPrivate::SimulatorPrivate (Scheduler *events)
{
    m_stop = false;
    m_stopAt = 0;
    m_events = events;
    m_uid = 0;    
    m_logEnable = false;
    m_currentNs = 0;
}

SimulatorPrivate::~SimulatorPrivate ()
{
    while (!m_destroy.empty ()) 
      {
        EventImpl *ev = m_destroy.front ().first;
        m_destroy.pop_front ();
        TRACE ("handle destroy " << ev);
        ev->Invoke ();
        delete ev;
      }
    delete m_events;
    m_events = (Scheduler *)0xdeadbeaf;
}


void
SimulatorPrivate::EnableLogTo (char const *filename)
{
    m_log.open (filename);
    m_logEnable = true;
}

void
SimulatorPrivate::ProcessOneEvent (void)
{
    EventImpl *nextEv = m_events->PeekNext ();
    Scheduler::EventKey nextKey = m_events->PeekNextKey ();
    m_events->RemoveNext ();
    TRACE ("handle " << nextEv);
    m_currentNs = nextKey.m_ns;
    m_currentUid = nextKey.m_uid;
    if (m_logEnable) 
      {
        m_log << "e "<<nextKey.m_uid << " " << nextKey.m_ns << std::endl;
      }
    nextEv->Invoke ();
    delete nextEv;
}

bool 
SimulatorPrivate::IsFinished (void) const
{
    return m_events->IsEmpty ();
}
uint64_t
SimulatorPrivate::NextNs (void) const
{
    assert (!m_events->IsEmpty ());
    Scheduler::EventKey nextKey = m_events->PeekNextKey ();
    return nextKey.m_ns;
}
Time
SimulatorPrivate::Next (void) const
{
    return NanoSeconds (NextNs ());
}


void
SimulatorPrivate::Run (void)
{

    while (!m_events->IsEmpty () && !m_stop && 
           (m_stopAt == 0 || m_stopAt > NextNs ())) 
      {
        ProcessOneEvent ();
      }
    m_log.close ();
}


void 
SimulatorPrivate::Stop (void)
{
    m_stop = true;
}
void 
SimulatorPrivate::StopAt (Time const &at)
{
    m_stopAt = at.ApproximateToNanoSeconds ();
}
EventId
SimulatorPrivate::Schedule (Time const &time, EventImpl *event)
{
    assert (time.ApproximateToNanoSeconds () >= m_currentNs);
    Scheduler::EventKey key = {time.ApproximateToNanoSeconds (), m_uid};
    if (m_logEnable) 
      {
        m_log << "i "<<m_currentUid<<" "<<m_currentNs<<" "
              <<m_uid<<" "<<time.ApproximateToNanoSeconds () << std::endl;
      }
    m_uid++;
    return m_events->Insert (event, key);
}
Time
SimulatorPrivate::Now (void) const
{
    return NanoSeconds (m_currentNs);
}

void
SimulatorPrivate::Remove (EventId ev)
{
    Scheduler::EventKey key;
    EventImpl *impl = m_events->Remove (ev, &key);
    delete impl;
    if (m_logEnable) 
      {
        m_log << "r " << m_currentUid << " " << m_currentNs << " "
              << key.m_uid << " " << key.m_ns << std::endl;
      }
}

void
SimulatorPrivate::Cancel (EventId id)
{
    assert (m_events->IsValid (id));
    EventImpl *ev = id.GetEventImpl ();
    ev->Cancel ();
}

bool
SimulatorPrivate::IsExpired (EventId ev)
{
    if (ev.GetEventImpl () != 0 &&
        ev.GetNs () <= m_currentNs &&
        ev.GetUid () < m_currentUid) 
      {
        return false;
      }
    return true;
}


}; // namespace ns3


#include "scheduler-list.h"
#include "scheduler-heap.h"
#include "scheduler-map.h"
#include "scheduler-factory.h"


namespace ns3 {

SimulatorPrivate *Simulator::m_priv = 0;
Simulator::ListType Simulator::m_listType = LINKED_LIST;
SchedulerFactory const*Simulator::m_schedFactory = 0;

void Simulator::SetLinkedList (void)
{
    m_listType = LINKED_LIST;
}
void Simulator::SetBinaryHeap (void)
{
    m_listType = BINARY_HEAP;
}
void Simulator::SetStdMap (void)
{
    m_listType = STD_MAP;
}
void 
Simulator::SetExternal (SchedulerFactory const*factory)
{
    assert (factory != 0);
    m_schedFactory = factory;
    m_listType = EXTERNAL;
}
void Simulator::EnableLogTo (char const *filename)
{
    GetPriv ()->EnableLogTo (filename);
}


SimulatorPrivate *
Simulator::GetPriv (void)
{
    if (m_priv == 0) 
      {
        Scheduler *events;
        switch (m_listType) {
        case LINKED_LIST:
            events = new SchedulerList ();
            break;
        case BINARY_HEAP:
            events = new SchedulerHeap ();
            break;
        case STD_MAP:
            events = new SchedulerMap ();
            break;
        case EXTERNAL:
            events = m_schedFactory->Create ();
        default: // not reached
            events = 0;
            assert (false); 
            break;
        }
        m_priv = new SimulatorPrivate (events);
      }
    TRACE_S ("priv " << m_priv);
    return m_priv;
}

void
Simulator::Destroy (void)
{
    delete m_priv;
    m_priv = 0;
}


bool 
Simulator::IsFinished (void)
{
    return GetPriv ()->IsFinished ();
}
Time
Simulator::Next (void)
{
    return GetPriv ()->Next ();
}


void 
Simulator::Run (void)
{
    GetPriv ()->Run ();
}
void 
Simulator::Stop (void)
{
    TRACE ("stop");
    GetPriv ()->Stop ();
}
void 
Simulator::StopAt (Time const &at)
{
    GetPriv ()->StopAt (at);
}
Time
Simulator::Now (void)
{
    return GetPriv ()->Now ();
}

EventId
Simulator::Schedule (Time const &time, EventImpl *ev)
{
    return GetPriv ()->Schedule (time, ev);
}
EventId
Simulator::Schedule (Time const &time, void (*f) (void))
{
    // zero arg version
    class EventFunctionImpl0 : public EventImpl {
    public:
    	typedef void (*F)(void);
        
    	EventFunctionImpl0 (F function) 
    		: m_function (function)
    	{}
    	virtual ~EventFunctionImpl0 () {}
    protected:
    	virtual void Notify (void) { 
    		(*m_function) (); 
        }
    private:
    	F m_function;
    } *ev = new EventFunctionImpl0 (f);
    return Schedule (time, ev);
}


void
Simulator::Remove (EventId ev)
{
    return GetPriv ()->Remove (ev);
}

void
Simulator::Cancel (EventId ev)
{
    return GetPriv ()->Cancel (ev);
}
bool 
Simulator::IsExpired (EventId id)
{
    return GetPriv ()->IsExpired (id);
}

}; // namespace ns3


#ifdef RUN_SELF_TESTS

#include "ns3/test.h"

namespace ns3 {

class SimulatorTests : public Test {
public:
    SimulatorTests ();
    virtual ~SimulatorTests ();
    virtual bool RunTests (void);
private:
    uint64_t NowUs ();
    bool RunOneTest (void);
    void A (int a);
    void B (int b);
    void C (int c);
    void D (int d);
    bool m_b;
    bool m_a;
    bool m_c;
    bool m_d;
    EventId m_idC;
};

SimulatorTests::SimulatorTests ()
    : Test ("Simulator")
{}
SimulatorTests::~SimulatorTests ()
{}
uint64_t
SimulatorTests::NowUs (void)
{
    uint64_t ns = Now ().ApproximateToNanoSeconds ();
    return ns / 1000;
}  
void
SimulatorTests::A (int a)
{
    m_a = false;
}
void
SimulatorTests::B (int b)
{
    if (b != 2 || NowUs () != 11) 
      {
        m_b = false;
      } 
    else 
      {
        m_b = true;
      }
    Simulator::Remove (m_idC);
    Simulator::Schedule (Now () + MicroSeconds (10), &SimulatorTests::D, this, 4);
}
void
SimulatorTests::C (int c)
{
    m_c = false;
}
void
SimulatorTests::D (int d)
{
    if (d != 4 || NowUs () != (11+10)) 
      {
        m_d = false;
      } 
    else 
      {
        m_d = true;
      }
}
bool
SimulatorTests::RunOneTest (void)
{
    bool ok = true;
    m_a = true;
    m_b = false;
    m_c = true;
    m_d = false;

    EventId a = Simulator::Schedule (Now () + MicroSeconds (10), &SimulatorTests::A, this, 1);
    Simulator::Schedule (Now () + MicroSeconds (11), &SimulatorTests::B, this, 2);
    m_idC = Simulator::Schedule (Now () + MicroSeconds (12), &SimulatorTests::C, this, 3);

    Simulator::Cancel (a);
    Simulator::Run ();

    if (!m_a || !m_b || !m_c || !m_d) 
      {
        ok = false;
      }
    return ok;
}
bool 
SimulatorTests::RunTests (void)
{
    bool ok = true;

    Simulator::SetLinkedList ();
    if (!RunOneTest ()) 
      {
        ok = false;
      }
    Simulator::Destroy ();
    Simulator::SetBinaryHeap ();
    if (!RunOneTest ()) 
      {
        ok = false;
      }
    Simulator::Destroy ();
    Simulator::SetStdMap ();
    if (!RunOneTest ()) 
      {
        ok = false;
      }
    Simulator::Destroy ();

    return ok;
}

SimulatorTests gSimulatorTests;

}; // namespace ns3

#endif /* RUN_SELF_TESTS */