Statistics
| Branch: | Revision:

root / src / sim / csimulation.cc @ 68da4f12

History | View | Annotate | Download (31.4 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
                endSimulation(false)
80
{
81
    ASSERT(cStaticFlag::isSet()); // cannot be instantiated as global variable
82

    
83
    ownEvPtr = env;
84

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

    
88
    simulationstage = CTX_NONE;
89
    contexttype = CTX_NONE;
90

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

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

    
100
    networktype = NULL;
101
    hasherp = NULL;
102

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

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

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

    
113
    eventsPerSimTimeInstance = 0;
114

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

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

    
124
    deleteNetwork();
125

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

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

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

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

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

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

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

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

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

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

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

    
210
    ostream& os = *osptr;
211

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

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

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

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

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

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

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

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

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

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

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

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

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

    
304
    last_id++;
305

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
397
    simulationstage = CTX_BUILD;
398

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

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

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

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

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

    
439
    simulationstage = CTX_INITIALIZE;
440

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

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

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

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

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

    
467
    simulationstage = CTX_FINISH;
468

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

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

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

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

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

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

    
500
    simulationstage = CTX_CLEANUP;
501

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

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

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

    
515
    networktype = NULL;
516

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

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

    
523
    simulationstage = CTX_NONE;
524

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

    
530
}
531

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
651
    try
652
    {
653
        // a worker signaled to shutdown the simulation
654
        if(endSimulation)
655
            throw cTerminationException(eENDSIM);
656

    
657
        // update the number of events that occur at the same simtime
658
        simtime_t currentTime = cThreadPool::getSimTime();
659
        if (currentTime < msg->getArrivalTime())
660
            eventsPerSimTimeInstance = 0;
661
        else
662
            eventsPerSimTimeInstance++;
663

    
664
        // remember how many events at this simtime instance have already been executed
665
        msg->setExecutionOrderId(eventsPerSimTimeInstance);
666

    
667
        // finally, advance Simulation Time
668
        cThreadPool::setSimTime(msg->getArrivalTime());
669

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

    
680
        // check if this module supports parallel execution
681
        if (mod->isAsyncModule())
682
        {
683
            // yes, it does. Next, check if this event should be executed
684
            // in parallel
685
            cAsyncModule* aMod = (cAsyncModule*) mod;
686

    
687
            simtime_t duration = msg->getEventDuration();
688
            bool sequentialExecution = (duration == SimTime::simTimeSequentialExecution);
689

    
690
            if (sequentialExecution)
691
                duration = SimTime::simTimeZero;
692

    
693
            if (duration < SimTime::simTimeZero)
694
            {
695
                throw cRuntimeError("negative event duration not allowed.");
696
            }
697

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

    
712
                EVCB.simulationEvent(msg);
713

    
714
                msg->setPreviousEventNumber(event_num);
715

    
716
                sequentialWatch.reset();
717
                sequentialWatch.startTicking();
718
                // take ownership in callHandleMessage after concurrency check
719
                aMod->callHandleMessage(msg);
720
                sequentialWatch.stopTicking();
721

    
722
                //Store measured complexity and write to file
723
                EVCB.simulationEventEnd(sequentialWatch.getTicksTime());
724

    
725
                // take ownership in callHandleMessage after concurrency check
726
                // unset busy to release module for further events
727
                aMod->unsetBusy();
728
            }
729
        }
730
        else
731
        { //The module is not a cAsyncModule
732

    
733
            // set the context for sequential execution
734
            setContext(mod);
735
            setContextType(CTX_EVENT);
736

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

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

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

    
779
void cSimulation::transferToMain()
780
{
781
    if (activitymodp!=NULL)
782
    {
783
        activitymodp = NULL;
784
        cCoroutine::switchToMain();     // stack switch
785
    }
786
}
787

    
788
void cSimulation::setContext(cComponent *p)
789
{
790
    cThreadPool::setContext(p);
791
    cThreadPool::setDefaultOwner(p);
792
}
793

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

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

    
812
unsigned long cSimulation::getUniqueNumber()
813
{
814
    return ev.getUniqueNumber();
815
}
816

    
817
void cSimulation::setHasher(cHasher *hasher)
818
{
819
    if (hasherp)
820
        delete hasherp;
821
    hasherp = hasher;
822
}
823

    
824
void cSimulation::insertMsg(cMessage *msg)
825
{
826
    msg->setPreviousEventNumber(event_num);
827
    simulation.msgQueue.insert(msg);
828
}
829

    
830
void cSimulation::setupThreadPool() {
831
    threaded = ev.getConfig()->getAsBool(CFGID_USE_THREADPOOL);
832

    
833
#ifdef ENABLE_OWNERSHIP
834
    if (threaded) {
835
        throw cRuntimeError(this, "Ownership Model is incompatible with threaded "
836
                "architecture of Horizon. Please disable threading or ownership");
837
    }
838
#endif
839

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

    
869
unsigned int cSimulation::getNextExecutionOrderId(cMessage* msg) {
870
    simtime_t currentTime = cThreadPool::getSimTime();
871
    if (currentTime < msg->getArrivalTime())
872
        return 0;
873
    else
874
        return eventsPerSimTimeInstance + 1;
875
}
876

    
877
void cSimulation::setupLocalRNGs() {
878

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

    
894
//----
895

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

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

    
927
  public:
928
    // constructor, destructor
929
    StaticEnv() {}
930
    virtual ~StaticEnv() {}
931

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

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

    
968
    // UI functions (see also protected ones)
969
    virtual void bubble(cComponent *component, const char *text)  {}
970
    virtual std::string gets(const char *prompt, const char *defaultreply=NULL)  {unsupported(); return "";}
971
    virtual cEnvir& flush()  {::fflush(stdout); return *this;}
972

    
973
    // RNGs
974
    virtual int getNumRNGs() const {return 0;}
975
    virtual cRNG *getRNG(int k)  {unsupported(); return NULL;}
976
    virtual void getRNGMappingFor(cComponent *component)  {component->setRNGMap(0,NULL);}
977

    
978
    // output vectors
979
    virtual void *registerOutputVector(const char *modulename, const char *vectorname)  {return NULL;}
980
    virtual void deregisterOutputVector(void *vechandle)  {}
981
    virtual void setVectorAttribute(void *vechandle, const char *name, const char *value)  {}
982
    virtual bool recordInOutputVector(void *vechandle, simtime_t t, double value)  {return false;}
983

    
984
    // output scalars
985
    virtual void recordScalar(cComponent *component, const char *name, double value, opp_string_map *attributes=NULL)  {}
986
    virtual void recordStatistic(cComponent *component, const char *name, cStatistic *statistic, opp_string_map *attributes=NULL)  {}
987

    
988
    // snapshot file
989
    virtual std::ostream *getStreamForSnapshot()  {unsupported(); return NULL;}
990
    virtual void releaseStreamForSnapshot(std::ostream *os)  {unsupported();}
991

    
992
    // misc
993
    virtual int getArgCount() const  {unsupported(); return 0;}
994
    virtual char **getArgVector() const  {unsupported(); return NULL;}
995
    virtual int getParsimProcId() const {return 0;}
996
    virtual int getParsimNumPartitions() const {return 1;}
997
    virtual unsigned long getUniqueNumber()  {unsupported(); return 0;}
998
    virtual bool idle()  {return false;}
999
};
1000

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

    
1010
static StaticEnv staticEnv;
1011

    
1012
// cSimulation's global variables
1013
cEnvir *cSimulation::evPtr = &staticEnv;
1014
cEnvir *cSimulation::staticEvPtr = &staticEnv;
1015

    
1016
cSimulation *cSimulation::simPtr = NULL;
1017

    
1018

    
1019
NAMESPACE_END
1020