Statistics
| Branch: | Revision:

root / src / sim / csimulation.cc @ 2f5cc443

History | View | Annotate | Download (31.2 KB)

1
//=========================================================================
2
//  CSIMULATION.CC - part of
3
//
4
//                  OMNeT++/OMNEST
5
//           Discrete System Simulation in C++
6
//
7
//   Definition of global object:
8
//    simulation
9
//
10
//   Member functions of
11
//    cSimulation  : holds modules and manages the simulation
12
//
13
//  Author: Andras Varga
14
//
15
//=========================================================================
16

    
17
/*--------------------------------------------------------------*
18
  Copyright (C) 1992-2008 Andras Varga
19
  Copyright (C) 2006-2008 OpenSim Ltd.
20

21
  This file is distributed WITHOUT ANY WARRANTY. See the file
22
  `license' for details on this and other legal matters.
23
*--------------------------------------------------------------*/
24

    
25
#include <string.h>
26
#include <stdio.h>
27
#include <algorithm>
28
#include "stringutil.h"
29
#include "cmodule.h"
30
#include "csimplemodule.h"
31
#include "cmessage.h"
32
#include "csimulation.h"
33
#include "cscheduler.h"
34
#include "cenvir.h"
35
#include "ccomponenttype.h"
36
#include "cstatistic.h"
37
#include "cexception.h"
38
#include "cparimpl.h"
39
#include "chasher.h"
40
#include "cconfiguration.h"
41
#include "ccoroutine.h"
42
#include "cthreadpool.h"
43
#include "casyncmodule.h"
44
#include "cbarriermessage.h"
45
#include "cconfiguration.h"
46
#include "cconfigoption.h"
47
#include "clockedthreadpool.h"
48
#include "cspinningthreadpool.h"
49
#include "sysdep.h"
50

    
51

    
52

    
53
#ifdef WITH_PARSIM
54
#include "ccommbuffer.h"
55
#endif
56

    
57
#ifdef WITH_NETBUILDER
58
#include "netbuilder/cnedloader.h"
59
#endif
60

    
61
NAMESPACE_BEGIN
62

    
63
using std::ostream;
64

    
65
#ifdef DEVELOPER_DEBUG
66
#include <set>
67
extern std::set<cOwnedObject*> objectlist;
68
void printAllObjects();
69
#endif
70

    
71
Register_PerRunConfigOption(CFGID_USE_THREADPOOL, "use-thread-pool", CFG_BOOL, "false", "Use the threadpool");
72
Register_PerRunConfigOption(CFGID_THREADPOOL_CLASS, "thread-pool-class", CFG_STRING, "cSpinningThreadPool", "What class of threadpool to use");
73
Register_GlobalConfigOption(CFGID_THREADPOOL_CPU_ID_OFFSET, "cpu-id-offset", CFG_INT, "0", "Offset for Thread Affinity. (When running multiple instances of Horizon at once)");
74
Register_PerRunConfigOption(CFGID_SIMUL_THREADPOOL_SIZE, "thread-pool-size", CFG_STRING, "5", "Number of Threads in Threadpool");
75

    
76

    
77
cSimulation::cSimulation(const char *name, cEnvir *env) :
78
                cNamedObject(name, false), isrunning(false)
79
{
80
    ASSERT(cStaticFlag::isSet()); // cannot be instantiated as global variable
81

    
82
    ownEvPtr = env;
83

    
84
    activitymodp = NULL;
85
    cThreadPool::setContext(NULL);
86

    
87
    simulationstage = CTX_NONE;
88
    contexttype = CTX_NONE;
89

    
90
    systemmodp = NULL;
91
    schedulerp = NULL;
92
    threadPool = NULL;
93

    
94
    delta = 32;
95
    size = 0;
96
    last_id = 0; // vect[0] is not used for historical reasons
97
    vect = NULL;
98

    
99
    networktype = NULL;
100
    hasherp = NULL;
101

    
102
    //sim_time = SIMTIME_ZERO;
103
    cThreadPool::setSimTime(SIMTIME_ZERO);
104
    event_num = 0;
105

    
106
    msgQueue.setName("scheduled-events");
107
    take(&msgQueue);
108

    
109
    // install a default scheduler
110
    setScheduler(new cSequentialScheduler());
111

    
112
    eventsPerSimTimeInstance = 0;
113

    
114
    //calibrate stopwatch
115
    sequentialWatch.calibrateTSC();
116
}
117

    
118
cSimulation::~cSimulation()
119
{
120
    if (this==simPtr)
121
        throw cRuntimeError(this, "cannot delete the active simulation manager object");
122

    
123
    deleteNetwork();
124

    
125
    delete hasherp;
126
    delete schedulerp;
127
    delete ownEvPtr;
128
    drop(&msgQueue);
129
}
130

    
131
void cSimulation::setActiveSimulation(cSimulation *sim)
132
{
133
    simPtr = sim;
134
    evPtr = sim==NULL ? staticEvPtr : sim->ownEvPtr;
135
}
136

    
137
void cSimulation::setStaticEnvir(cEnvir *env)
138
{
139
    if (!env)
140
         throw cRuntimeError("cSimulation::setStaticEnvir(): argument cannot be NULL");
141
    staticEvPtr = env;
142
}
143

    
144
void cSimulation::forEachChild(cVisitor *v)
145
{
146
    if (systemmodp!=NULL)
147
        v->visit(systemmodp);
148
    v->visit(&msgQueue);
149
}
150

    
151
std::string cSimulation::getFullPath() const
152
{
153
    return getFullName();
154
}
155

    
156
static std::string xmlquote(const std::string& str)
157
{
158
    if (!strchr(str.c_str(), '<') && !strchr(str.c_str(), '>'))
159
        return str;
160

    
161
    std::stringstream out;
162
    for (const char *s=str.c_str(); *s; s++)
163
    {
164
        char c = *s;
165
        if (c=='<')
166
           out << "&lt;";
167
        else if (c=='>')
168
           out << "&gt;";
169
        else
170
           out << c;
171
    }
172
    return out.str();
173
}
174

    
175
class cSnapshotWriterVisitor : public cVisitor
176
{
177
  protected:
178
    ostream& os;
179
    int indentlevel;
180
  public:
181
    cSnapshotWriterVisitor(ostream& ostr) : os(ostr) {
182
        indentlevel = 0;
183
    }
184
    virtual void visit(cObject *obj) {
185
        std::string indent(2*indentlevel, ' ');
186
        os << indent << "<object class=\"" << obj->getClassName() << "\" fullpath=\"" << xmlquote(obj->getFullPath()) << "\">\n";
187
        os << indent << "  <info>" << xmlquote(obj->info()) << "</info>\n";
188
        std::string details = obj->detailedInfo();
189
        if (!details.empty())
190
            os << indent << "  <detailedinfo>" << xmlquote(details) << "</detailedinfo>\n";
191
        indentlevel++;
192
        obj->forEachChild(this);
193
        indentlevel--;
194
        os << indent << "</object>\n\n";
195

    
196
        if (os.fail()) throw EndTraversalException();
197
    }
198
};
199

    
200
bool cSimulation::snapshot(cObject *object, const char *label)
201
{
202
    if (!object)
203
        throw cRuntimeError("snapshot(): object pointer is NULL");
204

    
205
    ostream *osptr = ev.getStreamForSnapshot();
206
    if (!osptr)
207
        throw cRuntimeError("Could not create stream for snapshot");
208

    
209
    ostream& os = *osptr;
210

    
211
    os << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n";
212
    os << "<snapshot\n";
213
    os << "    object=\"" << xmlquote(object->getFullPath()) << "\"\n";
214
    os << "    label=\"" << xmlquote(label?label:"") << "\"\n";
215
    os << "    simtime=\"" << xmlquote(SIMTIME_STR(simTime())) << "\"\n";
216
    os << "    network=\"" << xmlquote(networktype?networktype->getName():"") << "\"\n";
217
    os << "    >\n";
218

    
219
    cSnapshotWriterVisitor v(os);
220
    v.process(object);
221

    
222
    os << "</snapshot>\n";
223

    
224
    bool success = !os.fail();
225
    ev.releaseStreamForSnapshot(&os);
226

    
227
    if (!success)
228
        throw cRuntimeError("Could not write snapshot");
229
    return success;
230
}
231

    
232
void cSimulation::setScheduler(cScheduler *sch)
233
{
234
    if (systemmodp)
235
        throw cRuntimeError(this, "setScheduler(): cannot switch schedulers when a network is already set up");
236
    if (!sch)
237
        throw cRuntimeError(this, "setScheduler(): scheduler pointer is NULL");
238
    delete schedulerp;
239
    schedulerp = sch;
240
    schedulerp->setSimulation(this);
241
}
242

    
243
int cSimulation::loadNedSourceFolder(const char *folder)
244
{
245
#ifdef WITH_NETBUILDER
246
    return cNEDLoader::getInstance()->loadNedSourceFolder(folder);
247
#else
248
    throw cRuntimeError("cannot load NED files from `%s': simulation kernel was compiled without "
249
                        "support for dynamic loading of NED files (WITH_NETBUILDER=no)", folder);
250
#endif
251
}
252

    
253
void cSimulation::loadNedFile(const char *nedfile, const char *expectedPackage, bool isXML)
254
{
255
#ifdef WITH_NETBUILDER
256
    cNEDLoader::getInstance()->loadNedFile(nedfile, expectedPackage, isXML);
257
#else
258
    throw cRuntimeError("cannot load `%s': simulation kernel was compiled without "
259
                        "support for dynamic loading of NED files (WITH_NETBUILDER=no)", nedfile);
260
#endif
261
}
262

    
263
void cSimulation::loadNedText(const char *name, const char *nedtext, const char *expectedPackage, bool isXML)
264
{
265
#ifdef WITH_NETBUILDER
266
    cNEDLoader::getInstance()->loadNedText(name, nedtext, expectedPackage, isXML);
267
#else
268
    throw cRuntimeError("cannot source NED text: simulation kernel was compiled without "
269
                        "support for dynamic loading of NED files (WITH_NETBUILDER=no)");
270
#endif
271
}
272

    
273
void cSimulation::doneLoadingNedFiles()
274
{
275
#ifdef WITH_NETBUILDER
276
    cNEDLoader::getInstance()->doneLoadingNedFiles();
277
#endif
278
}
279

    
280
std::string cSimulation::getNedPackageForFolder(const char *folder)
281
{
282
#ifdef WITH_NETBUILDER
283
    return cNEDLoader::getInstance()->getNedPackageForFolder(folder);
284
#else
285
    return "-";
286
#endif
287
}
288

    
289
void cSimulation::clearLoadedNedFiles()
290
{
291
#ifdef WITH_NETBUILDER
292
    cNEDLoader::clear();
293
#endif
294
}
295

    
296
int cSimulation::registerModule(cModule *mod)
297
{
298
    // Insert module into the vector.
299
    // The module will get (last_id+1) as ID. We do not reuse "holes"
300
    // (empty slots) in the vector because we want the module ids to be
301
    // unique during the whole simulation.
302

    
303
    last_id++;
304

    
305
    if (last_id>=size)
306
    {
307
        // vector full, grow by delta
308
        cModule **v = new cModule *[size+delta];
309
        memcpy(v, vect, sizeof(cModule*)*size );
310
        for (int i=size; i<size+delta; i++) v[i]=NULL;
311
        delete [] vect;
312
        vect = v;
313
        size += delta;
314
    }
315
    vect[last_id] = mod;
316
    return last_id;
317
}
318

    
319
void cSimulation::deregisterModule(cModule *mod)
320
{
321
    int id = mod->getId();
322
    vect[id] = NULL;
323

    
324
    if (mod==systemmodp)
325
    {
326
        drop(systemmodp);
327
        systemmodp = NULL;
328
    }
329
}
330

    
331
void cSimulation::setSystemModule(cModule *p)
332
{
333
    systemmodp = p;
334
    take(p);
335
}
336

    
337
cModule *cSimulation::getModuleByPath(const char *path) const
338
{
339
    if (opp_isempty(path))
340
        return NULL;
341

    
342
    // start tokenizing the path
343
    opp_string pathbuf(path);
344
    char *s = strtok(pathbuf.buffer(),".");
345

    
346
    // search starts from system module
347
    cModule *modp = getSystemModule();
348
    if (!modp)
349
        return NULL;
350

    
351
    // 1st component in the path may be the system module, skip it then
352
    if (modp->isName(s))
353
        s = strtok(NULL,".");
354

    
355
    // match components of the path
356
    while (s && modp)
357
    {
358
        char *b;
359
        if ((b=strchr(s,'['))==NULL)
360
        {
361
            modp = modp->getSubmodule(s);  // no index given
362
        }
363
        else
364
        {
365
            if (s[strlen(s)-1]!=']')
366
                throw cRuntimeError("getModuleByPath(): syntax error in path `%s'", path);
367
            *b='\0';
368
            modp = modp->getSubmodule(s,atoi(b+1));
369
        }
370
        s = strtok(NULL,".");
371
    }
372

    
373
    return modp;  // NULL if not found
374
}
375

    
376
void cSimulation::setupNetwork(cModuleType *network)
377
{
378
#ifdef DEVELOPER_DEBUG
379
    printf("DEBUG: before setupNetwork: %d objects\n", cOwnedObject::getLiveObjectCount());
380
    objectlist.clear();
381
#endif
382

    
383
    checkActive();
384
    if (!network)
385
        throw cRuntimeError(eNONET);
386
    if (!network->isNetwork())
387
        throw cRuntimeError("setupNetwork: `%s' is not a network", network->getFullName());
388

    
389
    // set cNetworkType pointer
390
    networktype = network;
391

    
392
    // just to be sure
393
    msgQueue.clear();
394
    cComponent::clearSignalState();
395

    
396
    simulationstage = CTX_BUILD;
397

    
398
    try
399
    {
400
        // set up the network by instantiating the toplevel module
401
        cContextTypeSwitcher tmp(CTX_BUILD);
402
        cModule *mod = networktype->create(networktype->getName(), NULL);
403
        mod->finalizeParameters();
404
        mod->buildInside();
405
    }
406
    catch (std::exception& e)
407
    {
408
        // Note: no deleteNetwork() call here. We could call it here, but it's
409
        // dangerous: module destructors may try to delete uninitialized pointers
410
        // and crash. (Often pointers incorrectly get initialized in initialize()
411
        // and not in the constructor.)
412
        throw;
413
    }
414
    //catch (...) -- this is not a good idea because it would make debugging more difficult
415
    //{
416
    //    deleteNetwork();
417
    //    throw cRuntimeError("unknown exception occurred");
418
    //}
419

    
420
    //printf("setupNetwork finished, cParImpl objects in use: %ld\n", cParImpl::getLiveParImplObjectCount());
421
}
422

    
423
void cSimulation::startRun()
424
{
425
    checkActive();
426
    // read config and setup thread pool
427
    setupThreadPool();
428

    
429
    // setup local RNGs for each module according to config
430
    setupLocalRNGs();
431

    
432
    // reset counters. Note msgQueue.clear() was already called from setupNetwork()
433
    cThreadPool::setSimTime(0.0);
434
    //sim_time = 0;
435
    event_num = 0; // initialize() has event number 0
436
    cMessage::resetMessageCounters();
437

    
438
    simulationstage = CTX_INITIALIZE;
439

    
440
    // init the scheduler. Note this may insert events into the FES (see e.g. cNullMessageProtocol)
441
    getScheduler()->startRun();
442

    
443
    // prepare simple modules for simulation run:
444
    //    1. create starter message for all modules,
445
    //    2. then call initialize() for them (recursively)
446
    //  This order is important because initialize() functions might contain
447
    //  send() calls which could otherwise insert msgs BEFORE starter messages
448
    //  for the destination module and cause trouble in cSimpleMod's activate().
449
    if (systemmodp)
450
    {
451
        cContextSwitcher tmp(systemmodp);
452
        systemmodp->scheduleStart(0);
453
        systemmodp->callInitialize();
454
    }
455

    
456
    event_num = 1; // events are numbered from 1
457

    
458
    simulationstage = CTX_EVENT;
459
    isrunning = true;
460
}
461

    
462
void cSimulation::callFinish()
463
{
464
    checkActive();
465

    
466
    simulationstage = CTX_FINISH;
467

    
468
    if (threaded)
469
        threadPool->shutdown();
470

    
471
    // call user-defined finish() functions for all modules recursively
472
    if (systemmodp)
473
    {
474
        systemmodp->callFinish();
475
    }
476
    isrunning = false;
477
}
478

    
479
void cSimulation::endRun()
480
{
481
    checkActive();
482
    getScheduler()->endRun();
483

    
484
    // move to deleteNetwork later?
485
    if (threaded) {
486
        threadPool->shutdown();
487
        delete threadPool;
488
    }
489
}
490

    
491
void cSimulation::deleteNetwork()
492
{
493
    if (!systemmodp)
494
        return;  // network already deleted
495

    
496
    if (getContextModule()!=NULL)
497
        throw cRuntimeError("Attempt to delete network during simulation");
498

    
499
    simulationstage = CTX_CLEANUP;
500

    
501
    // delete all modules recursively
502
    systemmodp->deleteModule();
503

    
504
    // make sure it was successful
505
    for (int i=1; i<size; i++)
506
        ASSERT(vect[i]==NULL);
507

    
508
    // and clean up
509
    delete [] vect;
510
    vect = NULL;
511
    size = 0;
512
    last_id = 0;
513

    
514
    networktype = NULL;
515

    
516
    //FIXME todo delete cParImpl caches too (cParImplCache, cParImplCache2)
517
    cModule::clearNamePools();
518

    
519
    // clear remaining messages (module dtors may have cancelled & deleted some of them)
520
    msgQueue.clear();
521

    
522
    simulationstage = CTX_NONE;
523

    
524
#ifdef DEVELOPER_DEBUG
525
    printf("DEBUG: after deleteNetwork: %d objects\n", cOwnedObject::getLiveObjectCount());
526
    printAllObjects();
527
#endif
528

    
529
}
530

    
531
cSimpleModule *cSimulation::selectNextModule()
532
{
533
    assert(false); //Do not call the scheduler from here.
534
    // determine next event. Normally (with sequential simulation),
535
    // the scheduler just returns msgQueue->peekFirst().
536
    cMessage *msg = schedulerp->getNextEvent();
537
    if (!msg)
538
        return NULL; // scheduler got interrupted while waiting
539

    
540
    // check if destination module exists and is still running
541
    cSimpleModule *modp = (cSimpleModule *)vect[msg->getArrivalModuleId()];
542
    if (!modp || modp->isTerminated())
543
    {
544
        // Deleted/ended modules may have self-messages and sent messages
545
        // pending for them. Here we choose just to ignore them (delete them without
546
        // any error or warning). Rationale: if we thew an error here, it would
547
        // be very difficult to delete compound modules in which simple modules
548
        // send messages to one another -- each and every simple module
549
        // would have to be notified in advance that they're "shutting down",
550
        // and even then, deletion could progress only after waiting the maximum
551
        // propagation delay to elapse.
552
        delete msgQueue.removeFirst();
553
        return selectNextModule();
554
    }
555

    
556
    // advance simulation time
557
    //sim_time = msg->getArrivalTime();
558
    cThreadPool::setSimTime(msg->getArrivalTime());
559
    return modp;
560
}
561

    
562
cMessage *cSimulation::guessNextEvent()
563
{
564
    // determine the probable next event. No call to cSheduler!
565
    // TBD if this event is "not good" (no module or module ended),
566
    // we might look for another event, but this is not done right now.
567
    return msgQueue.peekFirst();
568
}
569

    
570
simtime_t cSimulation::guessNextSimtime()
571
{
572
    cMessage *msg = guessNextEvent();
573
    return msg==NULL ? -1 : msg->getArrivalTime();
574
}
575

    
576
cSimpleModule *cSimulation::guessNextModule()
577
{
578
    cMessage *msg = guessNextEvent();
579
    if (!msg)
580
        return NULL;
581

    
582
    // check if dest module exists and still running
583
    if (msg->getArrivalModuleId()==-1)
584
        return NULL;
585
    cSimpleModule *modp = (cSimpleModule *)vect[msg->getArrivalModuleId()];
586
    if (!modp || modp->isTerminated())
587
        return NULL;
588
    return modp;
589
}
590

    
591
void cSimulation::transferTo(cSimpleModule *modp)
592
{
593
    if (modp==NULL)
594
        throw cRuntimeError("transferTo(): attempt to transfer to NULL");
595

    
596
    // switch to activity() of the simple module
597
    exception = NULL;
598
    activitymodp = modp;
599
    cCoroutine::switchTo(modp->coroutine);
600

    
601
    if (modp->hasStackOverflow())
602
        throw cRuntimeError("Stack violation in module (%s)%s: module stack too small? "
603
                            "Try increasing it in the class' Module_Class_Members() or constructor",
604
                            modp->getClassName(), modp->getFullPath().c_str());
605

    
606
    // if exception occurred in activity(), re-throw it. This allows us to handle
607
    // handleMessage() and activity() in an uniform way in the upper layer.
608
    if (exception)
609
    {
610
        cException *e = exception;
611
        exception = NULL;
612

    
613
        // ok, so we have an exception *pointer*, but we have to throw further
614
        // by *value*, and possibly without leaking it. Hence the following magic...
615
        if (dynamic_cast<cDeleteModuleException *>(e))
616
        {
617
            cDeleteModuleException e2(*(cDeleteModuleException *)e);
618
            delete e;
619
            throw e2;
620
        }
621
        else if (dynamic_cast<cTerminationException *>(e))
622
        {
623
            cTerminationException e2(*(cTerminationException *)e);
624
            delete e;
625
            throw e2;
626
        }
627
        else if (dynamic_cast<cRuntimeError *>(e))
628
        {
629
            cRuntimeError e2(*(cRuntimeError *)e);
630
            delete e;
631
            throw e2;
632
        }
633
        else
634
        {
635
            cException e2(*(cException *)e);
636
            delete e;
637
            throw e2;
638
        }
639
    }
640
}
641

    
642
void cSimulation::doOneEvent(cMessage* msg)
643
{
644
    cSimpleModule* mod = NULL;
645

    
646
#ifndef NDEBUG
647
    checkActive();
648
#endif
649

    
650
    try
651
    {
652
        // update the number of events that occur at the same simtime
653
        simtime_t currentTime = cThreadPool::getSimTime();
654
        if (currentTime < msg->getArrivalTime())
655
            eventsPerSimTimeInstance = 0;
656
        else
657
            eventsPerSimTimeInstance++;
658

    
659
        // remember how many events at this simtime instance have already been executed
660
        msg->setExecutionOrderId(eventsPerSimTimeInstance);
661

    
662
        // finally, advance Simulation Time
663
        cThreadPool::setSimTime(msg->getArrivalTime());
664

    
665
        // Update mod pointer:
666
        mod = (cSimpleModule*) simulation.vect[msg->getArrivalModuleId()];
667
        // notify the environment about the event (writes eventlog, etc.)
668
        if (!mod) {
669
            throw cRuntimeError("Module was deleted during Simulation. This is not supported by Horizon.");
670
        }
671
        if (mod->usesActivity()) {
672
            throw cRuntimeError("Activity-Model not supported with Horizon.");
673
        }
674

    
675
        // check if this module supports parallel execution
676
        if (mod->isAsyncModule())
677
        {
678
            // yes, it does. Next, check if this event should be executed
679
            // in parallel
680
            cAsyncModule* aMod = (cAsyncModule*) mod;
681

    
682
            simtime_t duration = msg->getEventDuration();
683
            bool sequentialExecution = (duration == SimTime::simTimeSequentialExecution);
684

    
685
            if (sequentialExecution)
686
                duration = SimTime::simTimeZero;
687

    
688
            if (duration < SimTime::simTimeZero)
689
            {
690
                throw cRuntimeError("negative event duration not allowed.");
691
            }
692

    
693
            bool mayPar = aMod->mayParallelize(msg, duration);
694
            // execute this event in parallel
695
            if (mayPar && threaded && !sequentialExecution)
696
            {
697
                //printf("Offloading: %s\n",mod->getName());
698
                threadPool->insertTask(msg, duration);
699
                // the threadpool unsets the module busy after it finishes the event
700
            }
701
            else
702
            {
703
                // set the context for sequential execution
704
                setContext(mod);
705
                setContextType(CTX_EVENT);
706

    
707
                EVCB.simulationEvent(msg);
708

    
709
                msg->setPreviousEventNumber(event_num);
710

    
711
                sequentialWatch.reset();
712
                sequentialWatch.startTicking();
713
                // take ownership in callHandleMessage after concurrency check
714
                aMod->callHandleMessage(msg);
715
                sequentialWatch.stopTicking();
716

    
717
                //Store measured complexity and write to file
718
                EVCB.simulationEventEnd(sequentialWatch.getTicksTime());
719

    
720
                // take ownership in callHandleMessage after concurrency check
721
                // unset busy to release module for further events
722
                aMod->unsetBusy();
723
            }
724
        }
725
        else
726
        { //The module is not a cAsyncModule
727

    
728
            // set the context for sequential execution
729
            setContext(mod);
730
            setContextType(CTX_EVENT);
731

    
732
            //Disabled logging for simpleModules as we assume to have a duration.
733
            //EVCB.simulationEvent(msg);
734
            // store arrival event number of this message; it is useful input for the
735
            // sequence chart tool if the message doesn't get immediately deleted or
736
            // sent out again
737
            msg->setPreviousEventNumber(event_num);
738

    
739
            // take owner here: no danger of a race condition since
740
            // simple modules are only called from here
741
            mod->take(msg);
742
            // if there was an error during simulation, handleMessage()
743
            // will come back with an exception
744
            mod->handleMessage(msg);
745
        }
746

    
747
        //We have handled the Message, return
748
        setGlobalContext();
749
        event_num++;
750
        return;
751
    }
752
    catch (cDeleteModuleException& e)
753
    {
754
        assert(false); //not implemented
755
        setGlobalContext();
756
        mod->deleteModule();
757
    }
758
    catch (cException&)
759
    {
760
        // restore global context before throwing the exception further
761
        setGlobalContext();
762
        throw;
763
    }
764
    catch (std::exception& e)
765
    {
766
        // restore global context before throwing the exception further
767
        // but wrap into a cRuntimeError which captures the module before that
768
        cRuntimeError e2("%s: %s", opp_typename(typeid(e)), e.what());
769
        setGlobalContext();
770
        throw e2;
771
    }
772
}
773

    
774
void cSimulation::transferToMain()
775
{
776
    if (activitymodp!=NULL)
777
    {
778
        activitymodp = NULL;
779
        cCoroutine::switchToMain();     // stack switch
780
    }
781
}
782

    
783
void cSimulation::setContext(cComponent *p)
784
{
785
    cThreadPool::setContext(p);
786
    cThreadPool::setDefaultOwner(p);
787
}
788

    
789
cModule *cSimulation::getContextModule() const
790
{
791
    cComponent* ctxMod = cThreadPool::getContext();
792
    // cannot go inline (upward cast would require including cmodule.h in csimulation.h)
793
    if (!ctxMod || !ctxMod->isModule())
794
        return NULL;
795
    return (cModule *) ctxMod;
796
}
797

    
798
cSimpleModule *cSimulation::getContextSimpleModule() const
799
{
800
    cComponent* ctxMod = cThreadPool::getContext();
801
    // cannot go inline (upward cast would require including cmodule.h in csimulation.h)
802
    if (!ctxMod || !ctxMod->isModule() || !((cModule *) ctxMod)->isSimple())
803
        return NULL;
804
    return (cSimpleModule *) ctxMod;
805
}
806

    
807
unsigned long cSimulation::getUniqueNumber()
808
{
809
    return ev.getUniqueNumber();
810
}
811

    
812
void cSimulation::setHasher(cHasher *hasher)
813
{
814
    if (hasherp)
815
        delete hasherp;
816
    hasherp = hasher;
817
}
818

    
819
void cSimulation::insertMsg(cMessage *msg)
820
{
821
    msg->setPreviousEventNumber(event_num);
822
    simulation.msgQueue.insert(msg);
823
}
824

    
825
void cSimulation::setupThreadPool() {
826
    threaded = ev.getConfig()->getAsBool(CFGID_USE_THREADPOOL);
827

    
828
#ifdef ENABLE_OWNERSHIP
829
    if (threaded) {
830
        throw cRuntimeError(this, "Ownership Model is incompatible with threaded "
831
                "architecture of Horizon. Please disable threading or ownership");
832
    }
833
#endif
834

    
835
    if (threaded) {
836
        std::string threadPoolClass = ev.getConfig()->getAsString(
837
                CFGID_THREADPOOL_CLASS);
838
        if (threadPoolClass.compare("cLockedThreadPool") == 0) {
839
            threadPool = new cLockedThreadPool();
840
        } else if (threadPoolClass.compare("cSpinningThreadPool") == 0) {
841
            threadPool = new cSpinningThreadPool();
842
        } else {
843
            printf("WARNING: Threadpool class selected does not exist!\n");
844
            printf("Falling back to default (cSpinningThreadPool)\n");
845
            sleep(3);
846
            threadPool = new cSpinningThreadPool();
847
        }
848
        threadPool->activate();
849
    } else {
850
        //
851
        // set the cpu-id offset (used for demo reasons only)
852
        //
853
        unsigned cpuIdOffset = ev.getConfig()->getAsInt(
854
                CFGID_THREADPOOL_CPU_ID_OFFSET);
855
        unsigned cpuCount = (unsigned) sysconf(_SC_NPROCESSORS_ONLN);
856
        if (cpuIdOffset >= cpuCount) {
857
            throw cRuntimeError("Mis-configured CPU-id offset: exceeding number of"
858
                                " physical CPUs");
859
        }
860
        thread_set_affinity(pthread_self(), 0 + cpuIdOffset);
861
    }
862
}
863

    
864
unsigned int cSimulation::getNextExecutionOrderId(cMessage* msg) {
865
    simtime_t currentTime = cThreadPool::getSimTime();
866
    if (currentTime < msg->getArrivalTime())
867
        return 0;
868
    else
869
        return eventsPerSimTimeInstance + 1;
870
}
871

    
872
void cSimulation::setupLocalRNGs() {
873

    
874
    //
875
    // Walk through vect[] and setup RNGs for each cAsyncModule
876
    //
877
    // TODO: Better way to do this? What about dynamically created Modules?
878
    cAsyncModule* mod = NULL;
879
    for (int i = 1; i <= last_id; i++) {
880
        if (vect[i]) {
881
            mod = dynamic_cast<cAsyncModule*>(vect[i]);
882
            if (mod) {
883
                mod->initLocalRNGs();
884
            }
885
        }
886
    }
887
}
888

    
889
//----
890

    
891
/**
892
 * A dummy implementation of cEnvir, only provided so that one
893
 * can use simulation library classes outside simulations, that is,
894
 * in programs that only link with the simulation library (<i>sim_std</i>)
895
 * and not with the <i>envir</i>, <i>cmdenv</i>, <i>tkenv</i>,
896
 * etc. libraries.
897
 *
898
 * Many simulation library classes make calls to <i>ev</i> methods,
899
 * which would crash if <tt>evPtr</tt> was NULL; one example is
900
 * cObject's destructor which contains an <tt>ev.objectDeleted()</tt>.
901
 * The solution provided here is that <tt>evPtr</tt> is initialized
902
 * to point to a StaticEnv instance, thus enabling library classes to work.
903
 *
904
 * StaticEnv methods either do nothing, or throw an "unsupported method"
905
 * exception, so StaticEnv is only useful for the most basic usage scenarios.
906
 * For anything more complicated, <tt>evPtr</tt> must be set in <tt>main()</tt>
907
 * to point to a proper cEnvir implementation, like the Cmdenv or
908
 * Tkenv classes. (The <i>envir</i> library provides a <tt>main()</tt>
909
 * which does exactly that.)
910
 *
911
 * @ingroup Envir
912
 */
913
class StaticEnv : public cEnvir
914
{
915
  protected:
916
    void unsupported() const {throw opp_runtime_error("StaticEnv: unsupported method called");}
917

    
918
    virtual void sputn(const char *s, int n) {(void) ::fwrite(s,1,n,stdout);}
919
    virtual void putsmsg(const char *msg) {::printf("\n<!> %s\n\n", msg);}
920
    virtual bool askyesno(const char *msg)  {unsupported(); return false;}
921

    
922
  public:
923
    // constructor, destructor
924
    StaticEnv() {}
925
    virtual ~StaticEnv() {}
926

    
927
    // eventlog callback interface
928
    virtual void objectDeleted(cObject *object) {}
929
    virtual void simulationEvent(cMessage *msg)  {}
930
    virtual void simulationEventEnd(double complexity)  {}
931
    virtual void messageSent_OBSOLETE(cMessage *msg, cGate *directToGate=NULL)  {}
932
    virtual void messageScheduled(cMessage *msg)  {}
933
    virtual void messageCancelled(cMessage *msg)  {}
934
    virtual void beginSend(cMessage *msg)  {}
935
    virtual void messageSendDirect(cMessage *msg, cGate *toGate, simtime_t propagationDelay, simtime_t transmissionDelay)  {}
936
    virtual void messageSendHop(cMessage *msg, cGate *srcGate)  {}
937
    virtual void messageSendHop(cMessage *msg, cGate *srcGate, simtime_t propagationDelay, simtime_t transmissionDelay)  {}
938
    virtual void endSend(cMessage *msg)  {}
939
    virtual void messageDeleted(cMessage *msg)  {}
940
    virtual void moduleReparented(cModule *module, cModule *oldparent)  {}
941
    virtual void componentMethodBegin(cComponent *from, cComponent *to, const char *methodFmt, va_list va, bool silent)  {}
942
    virtual void componentMethodEnd()  {}
943
    virtual void moduleCreated(cModule *newmodule)  {}
944
    virtual void moduleDeleted(cModule *module)  {}
945
    virtual void gateCreated(cGate *newgate)  {}
946
    virtual void gateDeleted(cGate *gate)  {}
947
    virtual void connectionCreated(cGate *srcgate)  {}
948
    virtual void connectionDeleted(cGate *srcgate)  {}
949
    virtual void displayStringChanged(cComponent *component)  {}
950
    virtual void undisposedObject(cObject *obj);
951

    
952
     // configuration, model parameters
953
    virtual void configure(cComponent *component) {}
954
    virtual void readParameter(cPar *parameter)  {unsupported();}
955
    virtual bool isModuleLocal(cModule *parentmod, const char *modname, int index)  {return true;}
956
    virtual cXMLElement *getXMLDocument(const char *filename, const char *path=NULL)  {unsupported(); return NULL;}
957
    virtual void forgetXMLDocument(const char *filename) {}
958
    virtual void flushXMLDocumentCache() {}
959
    virtual unsigned getExtraStackForEnvir() const  {return 0;}
960
    virtual cConfiguration *getConfig()  {unsupported(); return NULL;}
961
    virtual bool isGUI() const  {return false;}
962

    
963
    // UI functions (see also protected ones)
964
    virtual void bubble(cComponent *component, const char *text)  {}
965
    virtual std::string gets(const char *prompt, const char *defaultreply=NULL)  {unsupported(); return "";}
966
    virtual cEnvir& flush()  {::fflush(stdout); return *this;}
967

    
968
    // RNGs
969
    virtual int getNumRNGs() const {return 0;}
970
    virtual cRNG *getRNG(int k)  {unsupported(); return NULL;}
971
    virtual void getRNGMappingFor(cComponent *component)  {component->setRNGMap(0,NULL);}
972

    
973
    // output vectors
974
    virtual void *registerOutputVector(const char *modulename, const char *vectorname)  {return NULL;}
975
    virtual void deregisterOutputVector(void *vechandle)  {}
976
    virtual void setVectorAttribute(void *vechandle, const char *name, const char *value)  {}
977
    virtual bool recordInOutputVector(void *vechandle, simtime_t t, double value)  {return false;}
978

    
979
    // output scalars
980
    virtual void recordScalar(cComponent *component, const char *name, double value, opp_string_map *attributes=NULL)  {}
981
    virtual void recordStatistic(cComponent *component, const char *name, cStatistic *statistic, opp_string_map *attributes=NULL)  {}
982

    
983
    // snapshot file
984
    virtual std::ostream *getStreamForSnapshot()  {unsupported(); return NULL;}
985
    virtual void releaseStreamForSnapshot(std::ostream *os)  {unsupported();}
986

    
987
    // misc
988
    virtual int getArgCount() const  {unsupported(); return 0;}
989
    virtual char **getArgVector() const  {unsupported(); return NULL;}
990
    virtual int getParsimProcId() const {return 0;}
991
    virtual int getParsimNumPartitions() const {return 1;}
992
    virtual unsigned long getUniqueNumber()  {unsupported(); return 0;}
993
    virtual bool idle()  {return false;}
994
};
995

    
996
void StaticEnv::undisposedObject(cObject *obj)
997
{
998
    if (!cStaticFlag::isSet())
999
    {
1000
        ::printf("<!> WARNING: global object variable (DISCOURAGED) detected: "
1001
                 "(%s)`%s' at %p\n", obj->getClassName(), obj->getFullPath().c_str(), obj);
1002
    }
1003
}
1004

    
1005
static StaticEnv staticEnv;
1006

    
1007
// cSimulation's global variables
1008
cEnvir *cSimulation::evPtr = &staticEnv;
1009
cEnvir *cSimulation::staticEvPtr = &staticEnv;
1010

    
1011
cSimulation *cSimulation::simPtr = NULL;
1012

    
1013

    
1014
NAMESPACE_END
1015