Project

General

Profile

Statistics
| Branch: | Revision:

root / src / sim / csimulation.cc @ 4f536d0b

History | View | Annotate | Download (30.9 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

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

    
120
    deleteNetwork();
121

    
122
    delete hasherp;
123
    delete schedulerp;
124
    delete ownEvPtr;
125
    drop(&msgQueue);
126
}
127

    
128
void cSimulation::setActiveSimulation(cSimulation *sim)
129
{
130
    simPtr = sim;
131
    evPtr = sim==NULL ? staticEvPtr : sim->ownEvPtr;
132
}
133

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

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

    
148
std::string cSimulation::getFullPath() const
149
{
150
    return getFullName();
151
}
152

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

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

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

    
193
        if (os.fail()) throw EndTraversalException();
194
    }
195
};
196

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

    
202
    ostream *osptr = ev.getStreamForSnapshot();
203
    if (!osptr)
204
        throw cRuntimeError("Could not create stream for snapshot");
205

    
206
    ostream& os = *osptr;
207

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

    
216
    cSnapshotWriterVisitor v(os);
217
    v.process(object);
218

    
219
    os << "</snapshot>\n";
220

    
221
    bool success = !os.fail();
222
    ev.releaseStreamForSnapshot(&os);
223

    
224
    if (!success)
225
        throw cRuntimeError("Could not write snapshot");
226
    return success;
227
}
228

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

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

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

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

    
270
void cSimulation::doneLoadingNedFiles()
271
{
272
#ifdef WITH_NETBUILDER
273
    cNEDLoader::getInstance()->doneLoadingNedFiles();
274
#endif
275
}
276

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

    
286
void cSimulation::clearLoadedNedFiles()
287
{
288
#ifdef WITH_NETBUILDER
289
    cNEDLoader::clear();
290
#endif
291
}
292

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

    
300
    last_id++;
301

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

    
316
void cSimulation::deregisterModule(cModule *mod)
317
{
318
    int id = mod->getId();
319
    vect[id] = NULL;
320

    
321
    if (mod==systemmodp)
322
    {
323
        drop(systemmodp);
324
        systemmodp = NULL;
325
    }
326
}
327

    
328
void cSimulation::setSystemModule(cModule *p)
329
{
330
    systemmodp = p;
331
    take(p);
332
}
333

    
334
cModule *cSimulation::getModuleByPath(const char *path) const
335
{
336
    if (opp_isempty(path))
337
        return NULL;
338

    
339
    // start tokenizing the path
340
    opp_string pathbuf(path);
341
    char *s = strtok(pathbuf.buffer(),".");
342

    
343
    // search starts from system module
344
    cModule *modp = getSystemModule();
345
    if (!modp)
346
        return NULL;
347

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

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

    
370
    return modp;  // NULL if not found
371
}
372

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

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

    
386
    // set cNetworkType pointer
387
    networktype = network;
388

    
389
    // just to be sure
390
    msgQueue.clear();
391
    cComponent::clearSignalState();
392

    
393
    simulationstage = CTX_BUILD;
394

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

    
417
    //printf("setupNetwork finished, cParImpl objects in use: %ld\n", cParImpl::getLiveParImplObjectCount());
418
}
419

    
420
void cSimulation::startRun()
421
{
422
    checkActive();
423
    // read config and setup thread pool
424
    setupThreadPool();
425

    
426
    // setup local RNGs for each module according to config
427
    setupLocalRNGs();
428

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

    
435
    simulationstage = CTX_INITIALIZE;
436

    
437
    // init the scheduler. Note this may insert events into the FES (see e.g. cNullMessageProtocol)
438
    getScheduler()->startRun();
439

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

    
453
    event_num = 1; // events are numbered from 1
454

    
455
    simulationstage = CTX_EVENT;
456
    isrunning = true;
457
}
458

    
459
void cSimulation::callFinish()
460
{
461
    checkActive();
462

    
463
    simulationstage = CTX_FINISH;
464

    
465
    if (threaded)
466
        threadPool->shutdown();
467

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

    
476
void cSimulation::endRun()
477
{
478
    checkActive();
479
    getScheduler()->endRun();
480

    
481
    // move to deleteNetwork later?
482
    if (threaded) {
483
        threadPool->shutdown();
484
        delete threadPool;
485
    }
486
}
487

    
488
void cSimulation::deleteNetwork()
489
{
490
    if (!systemmodp)
491
        return;  // network already deleted
492

    
493
    if (getContextModule()!=NULL)
494
        throw cRuntimeError("Attempt to delete network during simulation");
495

    
496
    simulationstage = CTX_CLEANUP;
497

    
498
    // delete all modules recursively
499
    systemmodp->deleteModule();
500

    
501
    // make sure it was successful
502
    for (int i=1; i<size; i++)
503
        ASSERT(vect[i]==NULL);
504

    
505
    // and clean up
506
    delete [] vect;
507
    vect = NULL;
508
    size = 0;
509
    last_id = 0;
510

    
511
    networktype = NULL;
512

    
513
    //FIXME todo delete cParImpl caches too (cParImplCache, cParImplCache2)
514
    cModule::clearNamePools();
515

    
516
    // clear remaining messages (module dtors may have cancelled & deleted some of them)
517
    msgQueue.clear();
518

    
519
    simulationstage = CTX_NONE;
520

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

    
526
}
527

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

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

    
553
    // advance simulation time
554
    //sim_time = msg->getArrivalTime();
555
    cThreadPool::setSimTime(msg->getArrivalTime());
556
    return modp;
557
}
558

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

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

    
573
cSimpleModule *cSimulation::guessNextModule()
574
{
575
    cMessage *msg = guessNextEvent();
576
    if (!msg)
577
        return NULL;
578

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

    
588
void cSimulation::transferTo(cSimpleModule *modp)
589
{
590
    if (modp==NULL)
591
        throw cRuntimeError("transferTo(): attempt to transfer to NULL");
592

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

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

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

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

    
639
void cSimulation::doOneEvent(cMessage* msg)
640
{
641
    cSimpleModule* mod = NULL;
642

    
643
#ifndef NDEBUG
644
    checkActive();
645
#endif
646

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

    
656
        // remember how many events at this simtime instance have already been executed
657
        msg->setExecutionOrderId(eventsPerSimTimeInstance);
658

    
659
        // finally, advance Simulation Time
660
        cThreadPool::setSimTime(msg->getArrivalTime());
661

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

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

    
679
            simtime_t duration = msg->getEventDuration();
680
            bool sequentialExecution = (duration == SimTime::simTimeSequentialExecution);
681

    
682
            if (sequentialExecution)
683
                duration = SimTime::simTimeZero;
684

    
685
            if (duration < SimTime::simTimeZero)
686
            {
687
                throw cRuntimeError("negative event duration not allowed.");
688
            }
689

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

    
704
                EVCB.simulationEvent(msg);
705
                // store arrival event number of this message; it is useful input for the
706
                // sequence chart tool if the message doesn't get immediately deleted or
707
                // sent out again
708
                msg->setPreviousEventNumber(event_num);
709

    
710
                // take ownership in callHandleMessage after concurrency check
711
                aMod->callHandleMessage(msg);
712
                // unset busy to release module for further events
713
                aMod->unsetBusy();
714
            }
715
        }
716
        else
717
        { //The module is not a cAsyncModule
718

    
719
            // set the context for sequential execution
720
            setContext(mod);
721
            setContextType(CTX_EVENT);
722

    
723
            EVCB.simulationEvent(msg);
724
            // store arrival event number of this message; it is useful input for the
725
            // sequence chart tool if the message doesn't get immediately deleted or
726
            // sent out again
727
            msg->setPreviousEventNumber(event_num);
728

    
729
            // take owner here: no danger of a race condition since
730
            // simple modules are only called from here
731
            mod->take(msg);
732
            // if there was an error during simulation, handleMessage()
733
            // will come back with an exception
734
            mod->handleMessage(msg);
735
        }
736

    
737
        //We have handled the Message, return
738
        setGlobalContext();
739
        event_num++;
740
        return;
741
    }
742
    catch (cDeleteModuleException& e)
743
    {
744
        assert(false); //not implemented
745
        setGlobalContext();
746
        mod->deleteModule();
747
    }
748
    catch (cException&)
749
    {
750
        // restore global context before throwing the exception further
751
        setGlobalContext();
752
        throw;
753
    }
754
    catch (std::exception& e)
755
    {
756
        // restore global context before throwing the exception further
757
        // but wrap into a cRuntimeError which captures the module before that
758
        cRuntimeError e2("%s: %s", opp_typename(typeid(e)), e.what());
759
        setGlobalContext();
760
        throw e2;
761
    }
762
}
763

    
764
void cSimulation::transferToMain()
765
{
766
    if (activitymodp!=NULL)
767
    {
768
        activitymodp = NULL;
769
        cCoroutine::switchToMain();     // stack switch
770
    }
771
}
772

    
773
void cSimulation::setContext(cComponent *p)
774
{
775
    cThreadPool::setContext(p);
776
    cThreadPool::setDefaultOwner(p);
777
}
778

    
779
cModule *cSimulation::getContextModule() const
780
{
781
    cComponent* ctxMod = cThreadPool::getContext();
782
    // cannot go inline (upward cast would require including cmodule.h in csimulation.h)
783
    if (!ctxMod || !ctxMod->isModule())
784
        return NULL;
785
    return (cModule *) ctxMod;
786
}
787

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

    
797
unsigned long cSimulation::getUniqueNumber()
798
{
799
    return ev.getUniqueNumber();
800
}
801

    
802
void cSimulation::setHasher(cHasher *hasher)
803
{
804
    if (hasherp)
805
        delete hasherp;
806
    hasherp = hasher;
807
}
808

    
809
void cSimulation::insertMsg(cMessage *msg)
810
{
811
    msg->setPreviousEventNumber(event_num);
812
    simulation.msgQueue.insert(msg);
813
}
814

    
815
void cSimulation::setupThreadPool() {
816
    threaded = ev.getConfig()->getAsBool(CFGID_USE_THREADPOOL);
817

    
818
#ifdef ENABLE_OWNERSHIP
819
    if (threaded) {
820
        throw cRuntimeError(this, "Ownership Model is incompatible with threaded "
821
                "architecture of Horizon. Please disable threading or ownership");
822
    }
823
#endif
824

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

    
854
unsigned int cSimulation::getNextExecutionOrderId(cMessage* msg) {
855
    simtime_t currentTime = cThreadPool::getSimTime();
856
    if (currentTime < msg->getArrivalTime())
857
        return 0;
858
    else
859
        return eventsPerSimTimeInstance + 1;
860
}
861

    
862
void cSimulation::setupLocalRNGs() {
863

    
864
    //
865
    // Walk through vect[] and setup RNGs for each cAsyncModule
866
    //
867
    // TODO: Better way to do this? What about dynamically created Modules?
868
    cAsyncModule* mod = NULL;
869
    for (int i = 1; i <= last_id; i++) {
870
        if (vect[i]) {
871
            mod = dynamic_cast<cAsyncModule*>(vect[i]);
872
            if (mod) {
873
                mod->initLocalRNGs();
874
            }
875
        }
876
    }
877
}
878

    
879
//----
880

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

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

    
912
  public:
913
    // constructor, destructor
914
    StaticEnv() {}
915
    virtual ~StaticEnv() {}
916

    
917
    // eventlog callback interface
918
    virtual void objectDeleted(cObject *object) {}
919
    virtual void simulationEvent(cMessage *msg)  {}
920
    virtual void messageSent_OBSOLETE(cMessage *msg, cGate *directToGate=NULL)  {}
921
    virtual void messageScheduled(cMessage *msg)  {}
922
    virtual void messageCancelled(cMessage *msg)  {}
923
    virtual void beginSend(cMessage *msg)  {}
924
    virtual void messageSendDirect(cMessage *msg, cGate *toGate, simtime_t propagationDelay, simtime_t transmissionDelay)  {}
925
    virtual void messageSendHop(cMessage *msg, cGate *srcGate)  {}
926
    virtual void messageSendHop(cMessage *msg, cGate *srcGate, simtime_t propagationDelay, simtime_t transmissionDelay)  {}
927
    virtual void endSend(cMessage *msg)  {}
928
    virtual void messageDeleted(cMessage *msg)  {}
929
    virtual void moduleReparented(cModule *module, cModule *oldparent)  {}
930
    virtual void componentMethodBegin(cComponent *from, cComponent *to, const char *methodFmt, va_list va, bool silent)  {}
931
    virtual void componentMethodEnd()  {}
932
    virtual void moduleCreated(cModule *newmodule)  {}
933
    virtual void moduleDeleted(cModule *module)  {}
934
    virtual void gateCreated(cGate *newgate)  {}
935
    virtual void gateDeleted(cGate *gate)  {}
936
    virtual void connectionCreated(cGate *srcgate)  {}
937
    virtual void connectionDeleted(cGate *srcgate)  {}
938
    virtual void displayStringChanged(cComponent *component)  {}
939
    virtual void undisposedObject(cObject *obj);
940

    
941
     // configuration, model parameters
942
    virtual void configure(cComponent *component) {}
943
    virtual void readParameter(cPar *parameter)  {unsupported();}
944
    virtual bool isModuleLocal(cModule *parentmod, const char *modname, int index)  {return true;}
945
    virtual cXMLElement *getXMLDocument(const char *filename, const char *path=NULL)  {unsupported(); return NULL;}
946
    virtual void forgetXMLDocument(const char *filename) {}
947
    virtual void flushXMLDocumentCache() {}
948
    virtual unsigned getExtraStackForEnvir() const  {return 0;}
949
    virtual cConfiguration *getConfig()  {unsupported(); return NULL;}
950
    virtual bool isGUI() const  {return false;}
951

    
952
    // UI functions (see also protected ones)
953
    virtual void bubble(cComponent *component, const char *text)  {}
954
    virtual std::string gets(const char *prompt, const char *defaultreply=NULL)  {unsupported(); return "";}
955
    virtual cEnvir& flush()  {::fflush(stdout); return *this;}
956

    
957
    // RNGs
958
    virtual int getNumRNGs() const {return 0;}
959
    virtual cRNG *getRNG(int k)  {unsupported(); return NULL;}
960
    virtual void getRNGMappingFor(cComponent *component)  {component->setRNGMap(0,NULL);}
961

    
962
    // output vectors
963
    virtual void *registerOutputVector(const char *modulename, const char *vectorname)  {return NULL;}
964
    virtual void deregisterOutputVector(void *vechandle)  {}
965
    virtual void setVectorAttribute(void *vechandle, const char *name, const char *value)  {}
966
    virtual bool recordInOutputVector(void *vechandle, simtime_t t, double value)  {return false;}
967

    
968
    // output scalars
969
    virtual void recordScalar(cComponent *component, const char *name, double value, opp_string_map *attributes=NULL)  {}
970
    virtual void recordStatistic(cComponent *component, const char *name, cStatistic *statistic, opp_string_map *attributes=NULL)  {}
971

    
972
    // snapshot file
973
    virtual std::ostream *getStreamForSnapshot()  {unsupported(); return NULL;}
974
    virtual void releaseStreamForSnapshot(std::ostream *os)  {unsupported();}
975

    
976
    // misc
977
    virtual int getArgCount() const  {unsupported(); return 0;}
978
    virtual char **getArgVector() const  {unsupported(); return NULL;}
979
    virtual int getParsimProcId() const {return 0;}
980
    virtual int getParsimNumPartitions() const {return 1;}
981
    virtual unsigned long getUniqueNumber()  {unsupported(); return 0;}
982
    virtual bool idle()  {return false;}
983
};
984

    
985
void StaticEnv::undisposedObject(cObject *obj)
986
{
987
    if (!cStaticFlag::isSet())
988
    {
989
        ::printf("<!> WARNING: global object variable (DISCOURAGED) detected: "
990
                 "(%s)`%s' at %p\n", obj->getClassName(), obj->getFullPath().c_str(), obj);
991
    }
992
}
993

    
994
static StaticEnv staticEnv;
995

    
996
// cSimulation's global variables
997
cEnvir *cSimulation::evPtr = &staticEnv;
998
cEnvir *cSimulation::staticEvPtr = &staticEnv;
999

    
1000
cSimulation *cSimulation::simPtr = NULL;
1001

    
1002

    
1003
NAMESPACE_END
1004