src/simulator/realtime-simulator-impl.cc
author Guillaume Seguin <guillaume@segu.in>
Thu Nov 12 13:19:35 2009 +0100 (2009-11-12)
changeset 5507 915abd2b907b
parent 5302 5ed72d440db4
child 5521 37c6c83d4252
permissions -rw-r--r--
Simulator::SetScheduler now takes an ObjectFactory
craigdo@3560
     1
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
craigdo@3560
     2
/*
craigdo@3560
     3
 * Copyright (c) 2008 University of Washington
craigdo@3560
     4
 *
craigdo@3560
     5
 * This program is free software; you can redistribute it and/or modify
craigdo@3560
     6
 * it under the terms of the GNU General Public License version 2 as
craigdo@3560
     7
 * published by the Free Software Foundation;
craigdo@3560
     8
 *
craigdo@3560
     9
 * This program is distributed in the hope that it will be useful,
craigdo@3560
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
craigdo@3560
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
craigdo@3560
    12
 * GNU General Public License for more details.
craigdo@3560
    13
 *
craigdo@3560
    14
 * You should have received a copy of the GNU General Public License
craigdo@3560
    15
 * along with this program; if not, write to the Free Software
craigdo@3560
    16
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
craigdo@3560
    17
 */
craigdo@3560
    18
craigdo@3560
    19
#include "simulator.h"
craigdo@3560
    20
#include "realtime-simulator-impl.h"
craigdo@3560
    21
#include "wall-clock-synchronizer.h"
craigdo@3560
    22
#include "scheduler.h"
craigdo@3560
    23
#include "event-impl.h"
craigdo@3560
    24
#include "synchronizer.h"
craigdo@3560
    25
craigdo@3560
    26
#include "ns3/ptr.h"
craigdo@3560
    27
#include "ns3/pointer.h"
craigdo@3560
    28
#include "ns3/assert.h"
craigdo@3560
    29
#include "ns3/fatal-error.h"
craigdo@3560
    30
#include "ns3/log.h"
craigdo@3560
    31
#include "ns3/system-mutex.h"
craigdo@3560
    32
#include "ns3/boolean.h"
craigdo@3560
    33
#include "ns3/enum.h"
craigdo@3560
    34
craigdo@3560
    35
craigdo@3560
    36
#include <math.h>
craigdo@3560
    37
craigdo@3560
    38
NS_LOG_COMPONENT_DEFINE ("RealtimeSimulatorImpl");
craigdo@3560
    39
craigdo@3560
    40
namespace ns3 {
craigdo@3560
    41
craigdo@3560
    42
NS_OBJECT_ENSURE_REGISTERED (RealtimeSimulatorImpl);
craigdo@3560
    43
craigdo@3560
    44
TypeId
craigdo@3560
    45
RealtimeSimulatorImpl::GetTypeId (void)
craigdo@3560
    46
{
craigdo@3560
    47
  static TypeId tid = TypeId ("ns3::RealtimeSimulatorImpl")
craigdo@3560
    48
    .SetParent<Object> ()
craigdo@3560
    49
    .AddConstructor<RealtimeSimulatorImpl> ()
craigdo@3560
    50
    .AddAttribute ("SynchronizationMode", 
craigdo@3560
    51
                   "What to do if the simulation cannot keep up with real time.",
craigdo@3560
    52
                   EnumValue (SYNC_BEST_EFFORT),
craigdo@3560
    53
                   MakeEnumAccessor (&RealtimeSimulatorImpl::SetSynchronizationMode),
craigdo@3560
    54
                   MakeEnumChecker (SYNC_BEST_EFFORT, "BestEffort",
craigdo@3560
    55
                                    SYNC_HARD_LIMIT, "HardLimit"))
craigdo@3560
    56
    .AddAttribute ("HardLimit", 
craigdo@3560
    57
                   "Maximum acceptable real-time jitter (used in conjunction with SynchronizationMode=HardLimit)",
craigdo@3560
    58
                   TimeValue (Seconds (0.1)),
craigdo@3560
    59
                   MakeTimeAccessor (&RealtimeSimulatorImpl::m_hardLimit),
craigdo@3560
    60
                   MakeTimeChecker ())
craigdo@3560
    61
    ;
craigdo@3560
    62
  return tid;
craigdo@3560
    63
}
craigdo@3560
    64
craigdo@3560
    65
craigdo@3560
    66
RealtimeSimulatorImpl::RealtimeSimulatorImpl ()
craigdo@3560
    67
{
craigdo@3560
    68
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
    69
craigdo@3560
    70
  m_stop = false;
craigdo@3560
    71
  m_running = false;
craigdo@3560
    72
  // uids are allocated from 4.
craigdo@3560
    73
  // uid 0 is "invalid" events
craigdo@3560
    74
  // uid 1 is "now" events
craigdo@3560
    75
  // uid 2 is "destroy" events
craigdo@3560
    76
  m_uid = 4; 
craigdo@3560
    77
  // before ::Run is entered, the m_currentUid will be zero
craigdo@3560
    78
  m_currentUid = 0;
craigdo@3560
    79
  m_currentTs = 0;
craigdo@3560
    80
  m_unscheduledEvents = 0;
craigdo@3560
    81
craigdo@3560
    82
  // Be very careful not to do anything that would cause a change or assignment
craigdo@3560
    83
  // of the underlying reference counts of m_synchronizer or you will be sorry.
craigdo@3560
    84
  m_synchronizer = CreateObject<WallClockSynchronizer> ();
craigdo@3560
    85
}
craigdo@3560
    86
craigdo@3560
    87
RealtimeSimulatorImpl::~RealtimeSimulatorImpl ()
craigdo@3560
    88
{
craigdo@3560
    89
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
    90
  while (m_events->IsEmpty () == false)
craigdo@3560
    91
    {
mathieu@3807
    92
      Scheduler::Event next = m_events->RemoveNext ();
mathieu@3807
    93
      next.impl->Unref ();
craigdo@3560
    94
    }
craigdo@3560
    95
  m_events = 0;
craigdo@3560
    96
  m_synchronizer = 0;
craigdo@3560
    97
}
craigdo@3560
    98
craigdo@3560
    99
void
craigdo@3560
   100
RealtimeSimulatorImpl::Destroy ()
craigdo@3560
   101
{
craigdo@3560
   102
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   103
craigdo@3560
   104
  //
craigdo@3560
   105
  // This function is only called with the private version "disconnected" from
craigdo@3560
   106
  // the main simulator functions.  We rely on the user not calling 
craigdo@3560
   107
  // Simulator::Destroy while there is a chance that a worker thread could be
craigdo@3560
   108
  // accessing the current instance of the private object.  In practice this
craigdo@3560
   109
  // means shutting down the workers and doing a Join() before calling the
craigdo@3560
   110
  // Simulator::Destroy().
craigdo@3560
   111
  //
craigdo@3560
   112
  while (m_destroyEvents.empty () == false) 
craigdo@3560
   113
    {
craigdo@3560
   114
      Ptr<EventImpl> ev = m_destroyEvents.front ().PeekEventImpl ();
craigdo@3560
   115
      m_destroyEvents.pop_front ();
craigdo@3560
   116
      NS_LOG_LOGIC ("handle destroy " << ev);
craigdo@3560
   117
      if (ev->IsCancelled () == false)
craigdo@3560
   118
        {
craigdo@3560
   119
          ev->Invoke ();
craigdo@3560
   120
        }
craigdo@3560
   121
    }
craigdo@3560
   122
}
craigdo@3560
   123
craigdo@3560
   124
void
guillaume@5507
   125
RealtimeSimulatorImpl::SetScheduler (ObjectFactory schedulerFactory)
craigdo@3560
   126
{
craigdo@3560
   127
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   128
guillaume@5507
   129
  Ptr<Scheduler> scheduler = schedulerFactory.Create<Scheduler> ();
guillaume@5507
   130
craigdo@3560
   131
  { 
craigdo@3560
   132
    CriticalSection cs (m_mutex);
craigdo@3560
   133
craigdo@3560
   134
    if (m_events != 0)
craigdo@3560
   135
      {
craigdo@3560
   136
        while (m_events->IsEmpty () == false)
craigdo@3560
   137
          {
mathieu@3807
   138
            Scheduler::Event next = m_events->RemoveNext ();
craigdo@3560
   139
            scheduler->Insert (next);
craigdo@3560
   140
          }
craigdo@3560
   141
      }
craigdo@3560
   142
    m_events = scheduler;
craigdo@3560
   143
  }
craigdo@3560
   144
}
craigdo@3560
   145
craigdo@3560
   146
void
craigdo@3560
   147
RealtimeSimulatorImpl::ProcessOneEvent (void)
craigdo@3560
   148
{
craigdo@3560
   149
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   150
  //
craigdo@3560
   151
  // The idea here is to wait until the next event comes due.  In the case of
craigdo@3796
   152
  // a realtime simulation, we want real time to be consumed between events.  
craigdo@3796
   153
  // It is the realtime synchronizer that causes real time to be consumed by
craigdo@3796
   154
  // doing some kind of a wait.
craigdo@3560
   155
  //
craigdo@3796
   156
  // We need to be able to have external events (such as a packet reception event)
craigdo@3796
   157
  // cause us to re-evaluate our state.  The way this works is that the synchronizer
craigdo@3796
   158
  // gets interrupted and returs.  So, there is a possibility that things may change
craigdo@3796
   159
  // out from under us dynamically.  In this case, we need to re-evaluate how long to 
craigdo@3796
   160
  // wait in a for-loop until we have waited sucessfully (until a timeout) for the 
craigdo@3796
   161
  // event at the head of the event list.
craigdo@3560
   162
  //
craigdo@3796
   163
  // m_synchronizer->Synchronize will return true if the wait was completed without 
craigdo@3796
   164
  // interruption, otherwise it will return false indicating that something has changed
craigdo@3796
   165
  // out from under us.  If we sit in the for-loop trying to synchronize until 
craigdo@3796
   166
  // Synchronize() returns true, we will have successfully synchronized the execution 
craigdo@3796
   167
  // time of the next event with the wall clock time of the synchronizer.
craigdo@3560
   168
  //
craigdo@3560
   169
craigdo@3560
   170
  for (;;) 
craigdo@3560
   171
    {
craigdo@3560
   172
      uint64_t tsDelay = 0;
craigdo@3560
   173
      uint64_t tsNext = 0;
craigdo@3796
   174
craigdo@3560
   175
      //
craigdo@3796
   176
      // It is important to understand that m_currentTs is interpreted only as the 
craigdo@3796
   177
      // timestamp  of the last event we executed.  Current time can a bit of a 
craigdo@3796
   178
      // slippery concept in realtime mode.  What we have here is a discrete event 
craigdo@3796
   179
      // simulator, so the last event is, by defintion, executed entirely at a single
craigdo@3796
   180
      //  discrete time.  This is the definition of m_currentTs.  It really has 
craigdo@3796
   181
      // nothing to do with the current real time, except that we are trying to arrange
craigdo@3796
   182
      // that at the instant of the beginning of event execution, the current real time
craigdo@3796
   183
      // and m_currentTs coincide.
craigdo@3796
   184
      //
craigdo@3796
   185
      // We use tsNow as the indication of the current real time.
craigdo@3796
   186
      //
craigdo@3796
   187
      uint64_t tsNow;
craigdo@3796
   188
craigdo@3560
   189
      { 
craigdo@3560
   190
        CriticalSection cs (m_mutex);
craigdo@3560
   191
        //
craigdo@3560
   192
        // Since we are in realtime mode, the time to delay has got to be the 
craigdo@3560
   193
        // difference between the current realtime and the timestamp of the next 
craigdo@3560
   194
        // event.  Since m_currentTs is actually the timestamp of the last event we 
craigdo@3560
   195
        // executed, it's not particularly meaningful for us here since real time has
craigdo@3796
   196
        // certainly elapsed since it was last updated.
craigdo@3560
   197
        //
craigdo@3560
   198
        // It is possible that the current realtime has drifted past the next event
craigdo@3560
   199
        // time so we need to be careful about that and not delay in that case.
craigdo@3560
   200
        //
craigdo@3560
   201
        NS_ASSERT_MSG (m_synchronizer->Realtime (), 
craigdo@3560
   202
          "RealtimeSimulatorImpl::ProcessOneEvent (): Synchronizer reports not Realtime ()");
craigdo@3560
   203
craigdo@3796
   204
        //
craigdo@3796
   205
        // tsNow is set to the normalized current real time.  When the simulation was
craigdo@3796
   206
        // started, the current real time was effectively set to zero; so tsNow is
craigdo@3796
   207
        // the current "real" simulation time.
craigdo@3796
   208
        //
craigdo@3796
   209
        // tsNext is the simulation time of the next event we want to execute.
craigdo@3796
   210
        //
craigdo@3560
   211
        tsNow = m_synchronizer->GetCurrentRealtime ();
craigdo@3560
   212
        tsNext = NextTs ();
craigdo@3560
   213
craigdo@3796
   214
        //
craigdo@3796
   215
        // tsDelay is therefore the real time we need to delay in order to bring the
craigdo@3796
   216
        // real time in sync with the simulation time.  If we wait for this amount of
craigdo@3796
   217
        // real time, we will accomplish moving the simulation time at the same rate
craigdo@3796
   218
        // as the real time.  This is typically called "pacing" the simulation time.
craigdo@3796
   219
        //
craigdo@3796
   220
        // We do have to be careful if we are falling behind.  If so, tsDelay must be
craigdo@3796
   221
        // zero.  If we're late, don't dawdle.
craigdo@3796
   222
        //
craigdo@3560
   223
        if (tsNext <= tsNow)
craigdo@3560
   224
          {
craigdo@3560
   225
            tsDelay = 0;
craigdo@3560
   226
          }
craigdo@3560
   227
        else
craigdo@3560
   228
          {
craigdo@3560
   229
            tsDelay = tsNext - tsNow;
craigdo@3560
   230
          }
craigdo@3560
   231
      
craigdo@3796
   232
        //
craigdo@3796
   233
        // We've figured out how long we need to delay in order to pace the 
craigdo@3796
   234
        // simulation time with the real time.  We're going to sleep, but need
craigdo@3796
   235
        // to work with the synchronizer to make sure we're awakened if something 
craigdo@3796
   236
        // external happens (like a packet is received).  This next line resets
craigdo@3796
   237
        // the synchronizer so that any future event will cause it to interrupt.
craigdo@3796
   238
        //
craigdo@3560
   239
        m_synchronizer->SetCondition (false);
craigdo@3560
   240
      }
craigdo@3560
   241
craigdo@3560
   242
      //
craigdo@3796
   243
      // We have a time to delay.  This time may actually not be valid anymore
craigdo@3796
   244
      // since we released the critical section immediately above, and a real-time
craigdo@3796
   245
      // ScheduleReal or ScheduleRealNow may have snuck in, well, between the 
craigdo@3796
   246
      // closing brace above and this comment so to speak.  If this is the case, 
craigdo@3796
   247
      // that schedule operation will have done a synchronizer Signal() that 
craigdo@3796
   248
      // will set the condition variable to true and cause the Synchronize call 
craigdo@3796
   249
      // below to return immediately.
craigdo@3560
   250
      //
craigdo@3560
   251
      // It's easiest to understand if you just consider a short tsDelay that only
craigdo@3560
   252
      // requires a SpinWait down in the synchronizer.  What will happen is that 
craigdo@3560
   253
      // whan Synchronize calls SpinWait, SpinWait will look directly at its 
craigdo@3560
   254
      // condition variable.  Note that we set this condition variable to false 
craigdo@3560
   255
      // inside the critical section above. 
craigdo@3560
   256
      //
craigdo@3560
   257
      // SpinWait will go into a forever loop until either the time has expired or
craigdo@3560
   258
      // until the condition variable becomes true.  A true condition indicates that
craigdo@3560
   259
      // the wait should stop.  The condition is set to true by one of the Schedule
craigdo@3560
   260
      // methods of the simulator; so if we are in a wait down in Synchronize, and
craigdo@3796
   261
      // a Simulator::ScheduleReal is done, the wait down in Synchronize will exit and
craigdo@3796
   262
      // Synchronize will return false.  This means we have not actually synchronized
craigdo@3796
   263
      // to the event expiration time.  If no real-time schedule operation is done
craigdo@3796
   264
      // while down in Synchronize, the wait will time out and Synchronize will return 
craigdo@3796
   265
      // true.  This indicates that we have synchronized to the event time.
craigdo@3560
   266
      //
craigdo@3796
   267
      // So we need to stay in this for loop, looking for the next event timestamp and 
craigdo@3796
   268
      // attempting to sleep until its due.  If we've slept until the timestamp is due, 
craigdo@3796
   269
      // Synchronize returns true and we break out of the sync loop.  If an external
craigdo@3796
   270
      // event happens that requires a re-schedule, Synchronize returns false and
craigdo@3796
   271
      // we re-evaluate our timing by continuing in the loop.  
craigdo@3560
   272
      //
craigdo@3796
   273
      // It is expected that tsDelay become shorter as external events interrupt our
craigdo@3796
   274
      // waits.
craigdo@3560
   275
      //
craigdo@3560
   276
      if (m_synchronizer->Synchronize (tsNow, tsDelay))
craigdo@3560
   277
        {
craigdo@3560
   278
          NS_LOG_LOGIC ("Interrupted ...");
craigdo@3560
   279
          break;
craigdo@3560
   280
        }
craigdo@3796
   281
 
craigdo@3796
   282
      //
craigdo@3796
   283
      // If we get to this point, we have been interrupted during a wait by a real-time
craigdo@3796
   284
      // schedule operation.  This means all bets are off regarding tsDelay and we need
craigdo@3796
   285
      // to re-evaluate what it is we want to do.  We'll loop back around in the 
craigdo@3796
   286
      // for-loop and start again from scratch.
craigdo@3796
   287
      //
craigdo@3560
   288
    }
craigdo@3560
   289
craigdo@3560
   290
  //
craigdo@3796
   291
  // If we break out of the for-loop above, we have waited until the time specified
craigdo@3796
   292
  // by the event that was at the head of the event list when we started the process.
craigdo@3796
   293
  // Since there is a bunch of code that was executed outside a critical section (the
craigdo@3796
   294
  // Synchronize call) we cannot be sure that the event at the head of the event list
craigdo@3796
   295
  // is the one we think it is.  What we can be sure of is that it is time to execute
craigdo@3796
   296
  // whatever event is at the head of this list if the list is in time order.
craigdo@3560
   297
  //
mathieu@3807
   298
  Scheduler::Event next;
craigdo@3560
   299
craigdo@3560
   300
  { 
craigdo@3560
   301
    CriticalSection cs (m_mutex);
craigdo@3560
   302
craigdo@3796
   303
    // 
craigdo@3796
   304
    // We do know we're waiting for an event, so there had better be an event on the 
craigdo@3796
   305
    // event queue.  Let's pull it off.  When we release the critical section, the
craigdo@3796
   306
    // event we're working on won't be on the list and so subsequent operations won't
craigdo@3796
   307
    // mess with us.
craigdo@3796
   308
    //
craigdo@3560
   309
    NS_ASSERT_MSG (m_events->IsEmpty () == false, 
craigdo@3560
   310
      "RealtimeSimulatorImpl::ProcessOneEvent(): event queue is empty");
craigdo@3560
   311
    next = m_events->RemoveNext ();
craigdo@3560
   312
    --m_unscheduledEvents;
craigdo@3796
   313
craigdo@3796
   314
    //
craigdo@3796
   315
    // We cannot make any assumption that "next" is the same event we originally waited 
craigdo@3796
   316
    // for.  We can only assume that only that it must be due and cannot cause time 
craigdo@3796
   317
    // to move backward.
craigdo@3796
   318
    //
mathieu@3807
   319
    NS_ASSERT_MSG (next.key.m_ts >= m_currentTs,
craigdo@3796
   320
                   "RealtimeSimulatorImpl::ProcessOneEvent(): "
craigdo@3796
   321
                   "next.GetTs() earlier than m_currentTs (list order error)");
mathieu@3807
   322
    NS_LOG_LOGIC ("handle " << next.key.m_ts);
craigdo@3796
   323
craigdo@3796
   324
    // 
craigdo@3796
   325
    // Update the current simulation time to be the timestamp of the event we're 
craigdo@3796
   326
    // executing.  From the rest of the simulation's point of view, simulation time
craigdo@3796
   327
    // is frozen until the next event is executed.
craigdo@3796
   328
    //
mathieu@3807
   329
    m_currentTs = next.key.m_ts;
mathieu@3807
   330
    m_currentUid = next.key.m_uid;
craigdo@3796
   331
craigdo@3796
   332
    // 
craigdo@3796
   333
    // We're about to run the event and we've done our best to synchronize this
craigdo@3796
   334
    // event execution time to real time.  Now, if we're in SYNC_HARD_LIMIT mode
craigdo@3796
   335
    // we have to decide if we've done a good enough job and if we haven't, we've
craigdo@3796
   336
    // been asked to commit ritual suicide.
craigdo@3796
   337
    //
craigdo@3796
   338
    // We check the simulation time against the current real time to make this
craigdo@3796
   339
    // judgement.
craigdo@3796
   340
    //
craigdo@3796
   341
    if (m_synchronizationMode == SYNC_HARD_LIMIT)
craigdo@3796
   342
      {
craigdo@3796
   343
        uint64_t tsFinal = m_synchronizer->GetCurrentRealtime ();
craigdo@3796
   344
        uint64_t tsJitter;
craigdo@3796
   345
craigdo@3796
   346
        if (tsFinal >= m_currentTs)
craigdo@3796
   347
          {
craigdo@3796
   348
            tsJitter = tsFinal - m_currentTs;
craigdo@3796
   349
          }
craigdo@3796
   350
        else
craigdo@3796
   351
          {
craigdo@3796
   352
            tsJitter = m_currentTs - tsFinal;
craigdo@3796
   353
          }
craigdo@3796
   354
craigdo@3796
   355
        if (tsJitter > static_cast<uint64_t>(m_hardLimit.GetTimeStep ()))
craigdo@3796
   356
          {
craigdo@3796
   357
            NS_FATAL_ERROR ("RealtimeSimulatorImpl::ProcessOneEvent (): "
craigdo@3796
   358
                            "Hard real-time limit exceeded (jitter = " << tsJitter << ")");
craigdo@3796
   359
          }
craigdo@3796
   360
      }
craigdo@3560
   361
  }
craigdo@3560
   362
craigdo@3560
   363
  //
craigdo@3796
   364
  // We have got the event we're about to execute completely disentangled from the 
craigdo@3796
   365
  // event list so we can execute it outside a critical section without fear of someone
craigdo@3796
   366
  // changing things out from under us.
craigdo@3560
   367
mathieu@3807
   368
  EventImpl *event = next.impl;
craigdo@3560
   369
  m_synchronizer->EventStart ();
craigdo@3560
   370
  event->Invoke ();
craigdo@3560
   371
  m_synchronizer->EventEnd ();
mathieu@3807
   372
  event->Unref ();
craigdo@3560
   373
}
craigdo@3560
   374
craigdo@3560
   375
bool 
craigdo@3560
   376
RealtimeSimulatorImpl::IsFinished (void) const
craigdo@3560
   377
{
craigdo@3560
   378
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   379
  bool rc;
craigdo@3560
   380
  {
craigdo@3560
   381
    CriticalSection cs (m_mutex);
gjc@4085
   382
    rc = m_events->IsEmpty () || m_stop;
craigdo@3560
   383
  }
craigdo@3560
   384
craigdo@3560
   385
  return rc;
craigdo@3560
   386
}
craigdo@3560
   387
craigdo@3560
   388
//
craigdo@3560
   389
// Peeks into event list.  Should be called with critical section locked.
craigdo@3560
   390
//
craigdo@3560
   391
uint64_t
craigdo@3560
   392
RealtimeSimulatorImpl::NextTs (void) const
craigdo@3560
   393
{
craigdo@3560
   394
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   395
  NS_ASSERT_MSG (m_events->IsEmpty () == false, 
craigdo@3560
   396
    "RealtimeSimulatorImpl::NextTs(): event queue is empty");
mathieu@3807
   397
  Scheduler::Event ev = m_events->PeekNext ();
mathieu@3807
   398
  return ev.key.m_ts;
craigdo@3560
   399
}
craigdo@3560
   400
craigdo@3560
   401
//
craigdo@3560
   402
// Calls NextTs().  Should be called with critical section locked.
craigdo@3560
   403
//
craigdo@3560
   404
Time
craigdo@3560
   405
RealtimeSimulatorImpl::Next (void) const
craigdo@3560
   406
{
craigdo@3560
   407
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   408
  return TimeStep (NextTs ());
craigdo@3560
   409
}
craigdo@3560
   410
craigdo@3560
   411
void
craigdo@3560
   412
RealtimeSimulatorImpl::Run (void)
craigdo@3560
   413
{
craigdo@3560
   414
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3802
   415
craigdo@3802
   416
  NS_ASSERT_MSG (m_running == false, 
craigdo@3802
   417
                 "RealtimeSimulatorImpl::Run(): Simulator already running");
craigdo@3802
   418
mathieu@4057
   419
  m_stop = false;
craigdo@3560
   420
  m_running = true;
craigdo@3560
   421
  m_synchronizer->SetOrigin (m_currentTs);
craigdo@3560
   422
craigdo@3560
   423
  for (;;) 
craigdo@3560
   424
    {
craigdo@3560
   425
      bool done = false;
craigdo@3560
   426
craigdo@3560
   427
      {
craigdo@3560
   428
        CriticalSection cs (m_mutex);
craigdo@3560
   429
        //
craigdo@3560
   430
        // In all cases we stop when the event list is empty.  If you are doing a 
craigdo@3560
   431
        // realtime simulation and you want it to extend out for some time, you must
craigdo@3560
   432
        // call StopAt.  In the realtime case, this will stick a placeholder event out
craigdo@3560
   433
        // at the end of time.
craigdo@3560
   434
        //
craigdo@3560
   435
        if (m_stop || m_events->IsEmpty ())
craigdo@3560
   436
          {
craigdo@3560
   437
            done = true;
craigdo@3560
   438
          }
craigdo@3560
   439
      }
craigdo@3560
   440
craigdo@3560
   441
      if (done)
craigdo@3560
   442
        {
craigdo@3560
   443
          break;
craigdo@3560
   444
        }
craigdo@3560
   445
craigdo@3560
   446
      ProcessOneEvent ();
craigdo@3560
   447
    }
craigdo@3560
   448
craigdo@3560
   449
  //
craigdo@3560
   450
  // If the simulator stopped naturally by lack of events, make a
craigdo@3560
   451
  // consistency test to check that we didn't lose any events along the way.
craigdo@3560
   452
  //
craigdo@3560
   453
  {
craigdo@3560
   454
    CriticalSection cs (m_mutex);
craigdo@3560
   455
craigdo@3560
   456
    NS_ASSERT_MSG (m_events->IsEmpty () == false || m_unscheduledEvents == 0,
craigdo@3560
   457
      "RealtimeSimulatorImpl::Run(): Empty queue and unprocessed events");
craigdo@3560
   458
  }
craigdo@3560
   459
craigdo@3560
   460
  m_running = false;
craigdo@3560
   461
}
craigdo@3560
   462
craigdo@3560
   463
bool
craigdo@3560
   464
RealtimeSimulatorImpl::Running (void) const
craigdo@3560
   465
{
craigdo@3560
   466
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   467
  return m_running;
craigdo@3560
   468
}
craigdo@3560
   469
craigdo@3560
   470
bool
craigdo@3560
   471
RealtimeSimulatorImpl::Realtime (void) const
craigdo@3560
   472
{
craigdo@3560
   473
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   474
  return m_synchronizer->Realtime ();
craigdo@3560
   475
}
craigdo@3560
   476
craigdo@3802
   477
//
craigdo@3802
   478
// This will run the first event on the queue without considering any realtime
craigdo@3802
   479
// synchronization.  It's mainly implemented to allow simulations requiring
craigdo@3802
   480
// the multithreaded ScheduleRealtimeNow() functions the possibility of driving
craigdo@3802
   481
// the simulation from their own event loop.
craigdo@3802
   482
//
craigdo@3802
   483
// It is expected that if there are any realtime requirements, the responsibility
craigdo@3802
   484
// for synchronizing with real time in an external event loop will be picked up
craigdo@3802
   485
// by that loop.  For example, they may call Simulator::Next() to find the 
craigdo@3802
   486
// execution time of the next event and wait for that time somehow -- then call
craigdo@3802
   487
// RunOneEvent to fire the event.
craigdo@3802
   488
// 
craigdo@3560
   489
void
craigdo@3560
   490
RealtimeSimulatorImpl::RunOneEvent (void)
craigdo@3560
   491
{
craigdo@3560
   492
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3802
   493
craigdo@3802
   494
  NS_ASSERT_MSG (m_running == false, 
craigdo@3802
   495
                 "RealtimeSimulatorImpl::RunOneEvent(): An internal simulator event loop is running");
craigdo@3802
   496
craigdo@3802
   497
  EventImpl *event = 0;
craigdo@3802
   498
craigdo@3802
   499
  //
craigdo@3802
   500
  // Run this in a critical section in case there's another thread around that
craigdo@3802
   501
  // may be inserting things onto the event list.
craigdo@3802
   502
  //
craigdo@3802
   503
  {
craigdo@3802
   504
    CriticalSection cs (m_mutex);
craigdo@3802
   505
mathieu@3807
   506
    Scheduler::Event next = m_events->RemoveNext ();
craigdo@3802
   507
mathieu@3807
   508
    NS_ASSERT (next.key.m_ts >= m_currentTs);
craigdo@3802
   509
    --m_unscheduledEvents;
craigdo@3802
   510
mathieu@3807
   511
    NS_LOG_LOGIC ("handle " << next.key.m_ts);
mathieu@3807
   512
    m_currentTs = next.key.m_ts;
mathieu@3807
   513
    m_currentUid = next.key.m_ts;
mathieu@3807
   514
    event = next.impl;
craigdo@3802
   515
  }
craigdo@3802
   516
  event->Invoke ();
mathieu@3807
   517
  event->Unref ();
craigdo@3560
   518
}
craigdo@3560
   519
craigdo@3560
   520
void 
craigdo@3560
   521
RealtimeSimulatorImpl::Stop (void)
craigdo@3560
   522
{
craigdo@3560
   523
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   524
  m_stop = true;
craigdo@3560
   525
}
craigdo@3560
   526
craigdo@3796
   527
//
craigdo@3796
   528
// Schedule an event for a _relative_ time in the future.
craigdo@3796
   529
//
craigdo@3560
   530
EventId
mathieu@3808
   531
RealtimeSimulatorImpl::Schedule (Time const &time, EventImpl *impl)
craigdo@3560
   532
{
mathieu@3807
   533
  NS_LOG_FUNCTION (time << impl);
craigdo@3560
   534
mathieu@3807
   535
  Scheduler::Event ev;
craigdo@3560
   536
  {
craigdo@3560
   537
    CriticalSection cs (m_mutex);
craigdo@3796
   538
    //
craigdo@3796
   539
    // This is the reason we had to bring the absolute time calcualtion in from the
craigdo@3796
   540
    // simulator.h into the implementation.  Since the implementations may be 
craigdo@3796
   541
    // multi-threaded, we need this calculation to be atomic.  You can see it is
craigdo@3796
   542
    // here since we are running in a CriticalSection.
craigdo@3796
   543
    //
craigdo@3796
   544
    Time tAbsolute = Simulator::Now () + time;
craigdo@3796
   545
    NS_ASSERT_MSG (tAbsolute.IsPositive (), "RealtimeSimulatorImpl::Schedule(): Negative time");
craigdo@3796
   546
    NS_ASSERT_MSG (tAbsolute >= TimeStep (m_currentTs), "RealtimeSimulatorImpl::Schedule(): time < m_currentTs");
mathieu@3808
   547
    ev.impl = impl;
mathieu@3807
   548
    ev.key.m_ts = (uint64_t) tAbsolute.GetTimeStep ();
mathieu@3807
   549
    ev.key.m_uid = m_uid;
craigdo@3560
   550
    m_uid++;
craigdo@3560
   551
    ++m_unscheduledEvents;
mathieu@3807
   552
    m_events->Insert (ev);
craigdo@3560
   553
    m_synchronizer->Signal ();
craigdo@3560
   554
  }
craigdo@3560
   555
mathieu@3807
   556
  return EventId (impl, ev.key.m_ts, ev.key.m_uid);
craigdo@3560
   557
}
craigdo@3560
   558
craigdo@3560
   559
EventId
mathieu@3808
   560
RealtimeSimulatorImpl::ScheduleNow (EventImpl *impl)
craigdo@3560
   561
{
craigdo@3560
   562
  NS_LOG_FUNCTION_NOARGS ();
mathieu@3807
   563
  Scheduler::Event ev;
craigdo@3560
   564
  {
craigdo@3560
   565
    CriticalSection cs (m_mutex);
craigdo@3560
   566
mathieu@3808
   567
    ev.impl = impl;
mathieu@3807
   568
    ev.key.m_ts = m_currentTs;
mathieu@3807
   569
    ev.key.m_uid = m_uid;
craigdo@3560
   570
    m_uid++;
craigdo@3560
   571
    ++m_unscheduledEvents;
mathieu@3807
   572
    m_events->Insert (ev);
craigdo@3560
   573
    m_synchronizer->Signal ();
craigdo@3560
   574
  }
craigdo@3560
   575
mathieu@3807
   576
  return EventId (impl, ev.key.m_ts, ev.key.m_uid);
craigdo@3560
   577
}
craigdo@3560
   578
craigdo@3798
   579
Time
craigdo@3798
   580
RealtimeSimulatorImpl::Now (void) const
craigdo@3798
   581
{
craigdo@3798
   582
  return TimeStep (m_currentTs);
craigdo@3798
   583
}
craigdo@3798
   584
craigdo@3798
   585
//
craigdo@3798
   586
// Schedule an event for a _relative_ time in the future.
craigdo@3798
   587
//
mathieu@3809
   588
void
mathieu@3816
   589
RealtimeSimulatorImpl::ScheduleRealtime (Time const &time, EventImpl *impl)
craigdo@3798
   590
{
mathieu@3807
   591
  NS_LOG_FUNCTION (time << impl);
craigdo@3799
   592
mathieu@3809
   593
  
craigdo@3799
   594
  {
craigdo@3799
   595
    CriticalSection cs (m_mutex);
craigdo@3799
   596
craigdo@3799
   597
    uint64_t ts = m_synchronizer->GetCurrentRealtime () + time.GetTimeStep ();
mathieu@3816
   598
    NS_ASSERT_MSG (ts >= m_currentTs, "RealtimeSimulatorImpl::ScheduleRealtime(): schedule for time < m_currentTs");
mathieu@3809
   599
    Scheduler::Event ev;
mathieu@3809
   600
    ev.impl = impl;
mathieu@3807
   601
    ev.key.m_ts = ts;
mathieu@3807
   602
    ev.key.m_uid = m_uid;
craigdo@3799
   603
    m_uid++;
craigdo@3799
   604
    ++m_unscheduledEvents;
mathieu@3807
   605
    m_events->Insert (ev);
craigdo@3799
   606
    m_synchronizer->Signal ();
craigdo@3799
   607
  }
craigdo@3799
   608
craigdo@3798
   609
}
craigdo@3798
   610
mathieu@3809
   611
void
mathieu@3816
   612
RealtimeSimulatorImpl::ScheduleRealtimeNow (EventImpl *impl)
craigdo@3798
   613
{
craigdo@3799
   614
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3799
   615
  {
craigdo@3799
   616
    CriticalSection cs (m_mutex);
craigdo@3799
   617
craigdo@3800
   618
    //
craigdo@3800
   619
    // If the simulator is running, we're pacing and have a meaningful 
craigdo@3800
   620
    // realtime clock.  If we're not, then m_currentTs is were we stopped.
craigdo@3800
   621
    // 
craigdo@3800
   622
    uint64_t ts = m_running ? m_synchronizer->GetCurrentRealtime () : m_currentTs;
mathieu@3816
   623
    NS_ASSERT_MSG (ts >= m_currentTs, "RealtimeSimulatorImpl::ScheduleRealtimeNow(): schedule for time < m_currentTs");
mathieu@3809
   624
    Scheduler::Event ev;
mathieu@3809
   625
    ev.impl = impl;
mathieu@3807
   626
    ev.key.m_ts = ts;
mathieu@3807
   627
    ev.key.m_uid = m_uid;
craigdo@3799
   628
    m_uid++;
craigdo@3799
   629
    ++m_unscheduledEvents;
mathieu@3807
   630
    m_events->Insert (ev);
craigdo@3799
   631
    m_synchronizer->Signal ();
craigdo@3799
   632
  }
craigdo@3798
   633
}
craigdo@3798
   634
craigdo@3798
   635
Time
craigdo@3801
   636
RealtimeSimulatorImpl::RealtimeNow (void) const
craigdo@3798
   637
{
craigdo@3799
   638
  return TimeStep (m_synchronizer->GetCurrentRealtime ());
craigdo@3798
   639
}
craigdo@3798
   640
craigdo@3560
   641
EventId
mathieu@3808
   642
RealtimeSimulatorImpl::ScheduleDestroy (EventImpl *impl)
craigdo@3560
   643
{
craigdo@3560
   644
  NS_LOG_FUNCTION_NOARGS ();
mathieu@3807
   645
craigdo@3560
   646
  EventId id;
craigdo@3560
   647
  {
craigdo@3560
   648
    CriticalSection cs (m_mutex);
craigdo@3560
   649
craigdo@3560
   650
    //
craigdo@3560
   651
    // Time doesn't really matter here (especially in realtime mode).  It is 
craigdo@3560
   652
    // overridden by the uid of 2 which identifies this as an event to be 
craigdo@3560
   653
    // executed at Simulator::Destroy time.
craigdo@3560
   654
    //
mathieu@3808
   655
    id = EventId (Ptr<EventImpl> (impl, false), m_currentTs, 2);
craigdo@3560
   656
    m_destroyEvents.push_back (id);
craigdo@3560
   657
    m_uid++;
craigdo@3560
   658
  }
craigdo@3560
   659
craigdo@3560
   660
  return id;
craigdo@3560
   661
}
craigdo@3560
   662
craigdo@3560
   663
Time 
craigdo@3560
   664
RealtimeSimulatorImpl::GetDelayLeft (const EventId &id) const
craigdo@3560
   665
{
craigdo@3561
   666
  //
craigdo@3561
   667
  // If the event has expired, there is no delay until it runs.  It is not the
craigdo@3561
   668
  // case that there is a negative time until it runs.
craigdo@3561
   669
  //
craigdo@3560
   670
  if (IsExpired (id))
craigdo@3560
   671
    {
craigdo@3560
   672
      return TimeStep (0);
craigdo@3560
   673
    }
craigdo@3561
   674
craigdo@3561
   675
  return TimeStep (id.GetTs () - m_currentTs);
craigdo@3560
   676
}
craigdo@3560
   677
craigdo@3560
   678
void
mathieu@3807
   679
RealtimeSimulatorImpl::Remove (const EventId &id)
craigdo@3560
   680
{
mathieu@3807
   681
  if (id.GetUid () == 2)
craigdo@3560
   682
    {
craigdo@3560
   683
      // destroy events.
craigdo@3560
   684
      for (DestroyEvents::iterator i = m_destroyEvents.begin (); 
craigdo@3560
   685
           i != m_destroyEvents.end (); 
craigdo@3560
   686
           i++)
craigdo@3560
   687
        {
mathieu@3807
   688
          if (*i == id)
craigdo@3560
   689
            {
craigdo@3560
   690
              m_destroyEvents.erase (i);
craigdo@3560
   691
              break;
craigdo@3560
   692
            }
craigdo@3560
   693
         }
craigdo@3560
   694
      return;
craigdo@3560
   695
    }
mathieu@3807
   696
  if (IsExpired (id))
craigdo@3560
   697
    {
craigdo@3560
   698
      return;
craigdo@3560
   699
    }
craigdo@3560
   700
craigdo@3560
   701
  {
craigdo@3560
   702
    CriticalSection cs (m_mutex);
craigdo@3560
   703
mathieu@3807
   704
    Scheduler::Event event;
mathieu@3807
   705
    event.impl = id.PeekEventImpl ();
mathieu@3807
   706
    event.key.m_ts = id.GetTs ();
mathieu@3807
   707
    event.key.m_uid = id.GetUid ();
mathieu@3807
   708
    
mathieu@3807
   709
    m_events->Remove (event);
craigdo@3560
   710
    --m_unscheduledEvents;
mathieu@3807
   711
    event.impl->Cancel ();
mathieu@3807
   712
    event.impl->Unref ();
craigdo@3560
   713
  }
craigdo@3560
   714
}
craigdo@3560
   715
craigdo@3560
   716
void
craigdo@3560
   717
RealtimeSimulatorImpl::Cancel (const EventId &id)
craigdo@3560
   718
{
craigdo@3560
   719
  if (IsExpired (id) == false)
craigdo@3560
   720
    {
craigdo@3560
   721
      id.PeekEventImpl ()->Cancel ();
craigdo@3560
   722
    }
craigdo@3560
   723
}
craigdo@3560
   724
craigdo@3560
   725
bool
craigdo@3560
   726
RealtimeSimulatorImpl::IsExpired (const EventId &ev) const
craigdo@3560
   727
{
craigdo@3560
   728
  if (ev.GetUid () == 2)
craigdo@3560
   729
    {
mathieu@5302
   730
      if (ev.PeekEventImpl () == 0 ||
mathieu@5302
   731
          ev.PeekEventImpl ()->IsCancelled ())
mathieu@5302
   732
        {
mathieu@5302
   733
          return true;
mathieu@5302
   734
        }
craigdo@3560
   735
      // destroy events.
craigdo@3560
   736
      for (DestroyEvents::const_iterator i = m_destroyEvents.begin (); 
craigdo@3560
   737
           i != m_destroyEvents.end (); i++)
craigdo@3560
   738
        {
craigdo@3560
   739
          if (*i == ev)
craigdo@3560
   740
            {
craigdo@3560
   741
              return false;
craigdo@3560
   742
            }
craigdo@3560
   743
         }
craigdo@3560
   744
      return true;
craigdo@3560
   745
    }
craigdo@3561
   746
craigdo@3561
   747
  //
craigdo@3561
   748
  // If the time of the event is less than the current timestamp of the 
craigdo@3561
   749
  // simulator, the simulator has gone past the invocation time of the 
craigdo@3561
   750
  // event, so the statement ev.GetTs () < m_currentTs does mean that 
craigdo@3561
   751
  // the event has been fired even in realtime mode.
craigdo@3561
   752
  //
craigdo@3561
   753
  // The same is true for the next line involving the m_currentUid.
craigdo@3561
   754
  //
craigdo@3560
   755
  if (ev.PeekEventImpl () == 0 ||
craigdo@3560
   756
      ev.GetTs () < m_currentTs ||
craigdo@3561
   757
      (ev.GetTs () == m_currentTs && ev.GetUid () <= m_currentUid) ||
craigdo@3560
   758
      ev.PeekEventImpl ()->IsCancelled ()) 
craigdo@3560
   759
    {
craigdo@3560
   760
      return true;
craigdo@3560
   761
    }
craigdo@3560
   762
  else
craigdo@3560
   763
    {
craigdo@3560
   764
      return false;
craigdo@3560
   765
    }
craigdo@3560
   766
}
craigdo@3560
   767
craigdo@3560
   768
Time 
craigdo@3560
   769
RealtimeSimulatorImpl::GetMaximumSimulationTime (void) const
craigdo@3560
   770
{
craigdo@3560
   771
  // XXX: I am fairly certain other compilers use other non-standard
craigdo@3560
   772
  // post-fixes to indicate 64 bit constants.
craigdo@3560
   773
  return TimeStep (0x7fffffffffffffffLL);
craigdo@3560
   774
}
craigdo@3560
   775
craigdo@3560
   776
void 
craigdo@3560
   777
RealtimeSimulatorImpl::SetSynchronizationMode (enum SynchronizationMode mode)
craigdo@3560
   778
{
craigdo@3560
   779
  NS_LOG_FUNCTION (mode);
craigdo@3560
   780
  m_synchronizationMode = mode;
craigdo@3560
   781
}
craigdo@3560
   782
craigdo@3560
   783
RealtimeSimulatorImpl::SynchronizationMode
craigdo@3560
   784
RealtimeSimulatorImpl::GetSynchronizationMode (void) const
craigdo@3560
   785
{
craigdo@3560
   786
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   787
  return m_synchronizationMode;
craigdo@3560
   788
}
craigdo@3560
   789
  
craigdo@3560
   790
void 
craigdo@3560
   791
RealtimeSimulatorImpl::SetHardLimit (Time limit)
craigdo@3560
   792
{
craigdo@3560
   793
  NS_LOG_FUNCTION (limit);
craigdo@3560
   794
  m_hardLimit = limit;
craigdo@3560
   795
}
craigdo@3560
   796
craigdo@3560
   797
Time
craigdo@3560
   798
RealtimeSimulatorImpl::GetHardLimit (void) const
craigdo@3560
   799
{
craigdo@3560
   800
  NS_LOG_FUNCTION_NOARGS ();
craigdo@3560
   801
  return m_hardLimit;
craigdo@3560
   802
}
craigdo@3560
   803
  
craigdo@3560
   804
}; // namespace ns3