Statistics
| Branch: | Revision:

root / src / sim / csimulation.cc @ aeae20a1

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

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

    
98
    networktype = NULL;
99
    hasherp = NULL;
100

    
101
    //sim_time = SIMTIME_ZERO;
102
    cThreadPool::setSimTime(SIMTIME_ZERO);
103
    barrierMin = 0;
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

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

    
118
    deleteNetwork();
119

    
120
    delete hasherp;
121
    delete schedulerp;
122
    delete ownEvPtr;
123
    drop(&msgQueue);
124
}
125

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

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

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

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

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

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

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

    
191
        if (os.fail()) throw EndTraversalException();
192
    }
193
};
194

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

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

    
204
    ostream& os = *osptr;
205

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

    
214
    cSnapshotWriterVisitor v(os);
215
    v.process(object);
216

    
217
    os << "</snapshot>\n";
218

    
219
    bool success = !os.fail();
220
    ev.releaseStreamForSnapshot(&os);
221

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

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

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

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

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

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

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

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

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

    
298
    last_id++;
299

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

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

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

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

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

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

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

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

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

    
368
    return modp;  // NULL if not found
369
}
370

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

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

    
384
    // set cNetworkType pointer
385
    networktype = network;
386

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

    
391
    simulationstage = CTX_BUILD;
392

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

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

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

    
424
    // reset counters. Note msgQueue.clear() was already called from setupNetwork()
425
    cThreadPool::setSimTime(0.0);
426
    //sim_time = 0;
427
    event_num = 0; // initialize() has event number 0
428
    cMessage::resetMessageCounters();
429

    
430
    simulationstage = CTX_INITIALIZE;
431

    
432
    // init the scheduler. Note this may insert events into the FES (see e.g. cNullMessageProtocol)
433
    getScheduler()->startRun();
434

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

    
448
    event_num = 1; // events are numbered from 1
449

    
450
    simulationstage = CTX_EVENT;
451
    isrunning = true;
452
}
453

    
454
void cSimulation::callFinish()
455
{
456
    checkActive();
457

    
458
    simulationstage = CTX_FINISH;
459

    
460
    if (threaded)
461
        threadPool->shutdown();
462

    
463
    // call user-defined finish() functions for all modules recursively
464
    if (systemmodp)
465
    {
466
        systemmodp->callFinish();
467
    }
468
    isrunning = false;
469
}
470

    
471
void cSimulation::endRun()
472
{
473
    checkActive();
474
    getScheduler()->endRun();
475

    
476
    // move to deleteNetwork later?
477
    if (threaded) {
478
        threadPool->shutdown();
479
        delete threadPool;
480
    }
481
}
482

    
483
void cSimulation::deleteNetwork()
484
{
485
    if (!systemmodp)
486
        return;  // network already deleted
487

    
488
    if (getContextModule()!=NULL)
489
        throw cRuntimeError("Attempt to delete network during simulation");
490

    
491
    simulationstage = CTX_CLEANUP;
492

    
493
    // delete all modules recursively
494
    systemmodp->deleteModule();
495

    
496
    // make sure it was successful
497
    for (int i=1; i<size; i++)
498
        ASSERT(vect[i]==NULL);
499

    
500
    // and clean up
501
    delete [] vect;
502
    vect = NULL;
503
    size = 0;
504
    last_id = 0;
505

    
506
    networktype = NULL;
507

    
508
    //FIXME todo delete cParImpl caches too (cParImplCache, cParImplCache2)
509
    cModule::clearNamePools();
510

    
511
    // clear remaining messages (module dtors may have cancelled & deleted some of them)
512
    msgQueue.clear();
513

    
514
    simulationstage = CTX_NONE;
515

    
516
#ifdef DEVELOPER_DEBUG
517
    printf("DEBUG: after deleteNetwork: %d objects\n", cOwnedObject::getLiveObjectCount());
518
    printAllObjects();
519
#endif
520

    
521
}
522

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

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

    
548
    // advance simulation time
549
    //sim_time = msg->getArrivalTime();
550
    cThreadPool::setSimTime(msg->getArrivalTime());
551
    return modp;
552
}
553

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

    
562
simtime_t cSimulation::guessNextSimtime()
563
{
564
    cMessage *msg = guessNextEvent();
565
    return msg==NULL ? -1 : msg->getArrivalTime();
566
}
567

    
568
cSimpleModule *cSimulation::guessNextModule()
569
{
570
    cMessage *msg = guessNextEvent();
571
    if (!msg)
572
        return NULL;
573

    
574
    // check if dest module exists and still running
575
    if (msg->getArrivalModuleId()==-1)
576
        return NULL;
577
    cSimpleModule *modp = (cSimpleModule *)vect[msg->getArrivalModuleId()];
578
    if (!modp || modp->isTerminated())
579
        return NULL;
580
    return modp;
581
}
582

    
583
void cSimulation::transferTo(cSimpleModule *modp)
584
{
585
    if (modp==NULL)
586
        throw cRuntimeError("transferTo(): attempt to transfer to NULL");
587

    
588
    // switch to activity() of the simple module
589
    exception = NULL;
590
    activitymodp = modp;
591
    cCoroutine::switchTo(modp->coroutine);
592

    
593
    if (modp->hasStackOverflow())
594
        throw cRuntimeError("Stack violation in module (%s)%s: module stack too small? "
595
                            "Try increasing it in the class' Module_Class_Members() or constructor",
596
                            modp->getClassName(), modp->getFullPath().c_str());
597

    
598
    // if exception occurred in activity(), re-throw it. This allows us to handle
599
    // handleMessage() and activity() in an uniform way in the upper layer.
600
    if (exception)
601
    {
602
        cException *e = exception;
603
        exception = NULL;
604

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

    
634
void cSimulation::doOneEvent()
635
{
636
    cSimpleModule* mod = NULL; //TODO FIXME?
637
#ifndef NDEBUG
638
    checkActive();
639
#endif
640
    try
641
    {
642
        cMessage * msg;
643

    
644
#ifdef NOBARRIER
645
        // Do we have to wait for a barrier?
646
        if(threaded) {
647
            barrierMin = threadPool->waitAtBarrier(barrierMin, &msgQueue);
648
        }
649
#endif
650
        msg = schedulerp->getNextEvent();
651
        //Advance Simulation Time
652
        cThreadPool::setSimTime(msg->getArrivalTime());
653

    
654
#ifndef NOBARRIER
655
        // check if this is an barrier message indicating the end of an event
656
        cBarrierMessage* barrier = dynamic_cast<cBarrierMessage*> (msg);
657
        if (barrier != NULL) {
658
            // wait for the task to complete
659
            barrier->wait();
660
            delete barrier;
661

    
662
            //It was a Barrier Message, after waiting, we are done and can return
663
            setGlobalContext();
664
            event_num++;
665
            return;
666
        }
667
#endif
668
        // Update mod pointer:
669
        mod = (cSimpleModule*) simulation.vect[msg->getArrivalModuleId()];
670
        // notify the environment about the event (writes eventlog, etc.)
671
        EVCB.simulationEvent(msg);
672
        // store arrival event number of this message; it is useful input for the
673
        // sequence chart tool if the message doesn't get immediately deleted or
674
        // sent out again
675
        msg->setPreviousEventNumber(event_num);
676

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

    
683
            simtime_t duration = aMod->getProcessingDelay(msg);
684
            bool mayPar = aMod->mayParallelize(msg, duration);
685

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

    
690
#ifndef NOBARRIER
691
            simtime_t now = cThreadPool::getSimTime();
692
#endif
693
            msg->setEventDuration(duration);
694
            // execute this event in parallel
695
            if (mayPar && threaded) {
696
#ifndef NOBARRIER
697
                /*
698
                 * MOVED TO cSCHEDULER.cc
699
                // create a new barrier and schedule it
700
                cBarrierMessage* barrier = new cBarrierMessage();
701
                barrier->setArrival(mod, -1, now + duration);
702
                simulation.msgQueue.insert(barrier);
703
                // insert user supplied message in task queue.
704
                msg->setBarrier(barrier);*/
705
#endif
706
                // block if another thread is busy inside this module
707
                // then set the module to busy
708
                aMod->waitIfBusy();
709
                aMod->setBusy();
710
                //Debugoutput
711
                //printf("Offloading: %s\n",mod->getName());
712
                barrierMin = threadPool->insertTask(msg, duration, barrierMin);
713
            } else {
714
                // set the context for sequential execution
715
                setContext(mod);
716
                setContextType(CTX_EVENT);
717
                // take ownership in callHandleMessage after concurrency check
718

    
719
                aMod->callHandleMessage(msg);
720
            }
721

    
722
            //We have handled the Message / offloaded it. We can return
723
            setGlobalContext();
724
            event_num++;
725
            return;
726
        }
727

    
728
        //The module is no cAsyncModule
729
        // set the context for sequential execution
730
        setContext(mod);
731
        setContextType(CTX_EVENT);
732
        // take owner here: no danger of a race condition since
733
        // simple modules are only called from here
734
        mod->take(msg);
735
        // if there was an error during simulation, handleMessage()
736
        // will come back with an exception
737
        mod->handleMessage(msg);
738

    
739

    
740
        //We have handled the Message, return
741
        setGlobalContext();
742
        event_num++;
743
        return;
744

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

    
767
    // Note: simulation time (as read via simTime() from modules) is updated
768
    // in selectNextModule()) called right before the next doOneEvent().
769
    // It must not be updated here, because it will interfere with parallel
770
    // simulation (cIdealSimulationProtocol, etc) that relies on simTime()
771
    // returning the time of the last executed event. If Tkenv wants to display
772
    // the time of the next event, it should call guessNextSimtime().
773
}
774

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

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

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

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

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

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

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

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

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

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

    
865
//----
866

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

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

    
898
  public:
899
    // constructor, destructor
900
    StaticEnv() {}
901
    virtual ~StaticEnv() {}
902

    
903
    // eventlog callback interface
904
    virtual void objectDeleted(cObject *object) {}
905
    virtual void simulationEvent(cMessage *msg)  {}
906
    virtual void messageSent_OBSOLETE(cMessage *msg, cGate *directToGate=NULL)  {}
907
    virtual void messageScheduled(cMessage *msg)  {}
908
    virtual void messageCancelled(cMessage *msg)  {}
909
    virtual void beginSend(cMessage *msg)  {}
910
    virtual void messageSendDirect(cMessage *msg, cGate *toGate, simtime_t propagationDelay, simtime_t transmissionDelay)  {}
911
    virtual void messageSendHop(cMessage *msg, cGate *srcGate)  {}
912
    virtual void messageSendHop(cMessage *msg, cGate *srcGate, simtime_t propagationDelay, simtime_t transmissionDelay)  {}
913
    virtual void endSend(cMessage *msg)  {}
914
    virtual void messageDeleted(cMessage *msg)  {}
915
    virtual void moduleReparented(cModule *module, cModule *oldparent)  {}
916
    virtual void componentMethodBegin(cComponent *from, cComponent *to, const char *methodFmt, va_list va, bool silent)  {}
917
    virtual void componentMethodEnd()  {}
918
    virtual void moduleCreated(cModule *newmodule)  {}
919
    virtual void moduleDeleted(cModule *module)  {}
920
    virtual void gateCreated(cGate *newgate)  {}
921
    virtual void gateDeleted(cGate *gate)  {}
922
    virtual void connectionCreated(cGate *srcgate)  {}
923
    virtual void connectionDeleted(cGate *srcgate)  {}
924
    virtual void displayStringChanged(cComponent *component)  {}
925
    virtual void undisposedObject(cObject *obj);
926

    
927
     // configuration, model parameters
928
    virtual void configure(cComponent *component) {}
929
    virtual void readParameter(cPar *parameter)  {unsupported();}
930
    virtual bool isModuleLocal(cModule *parentmod, const char *modname, int index)  {return true;}
931
    virtual cXMLElement *getXMLDocument(const char *filename, const char *path=NULL)  {unsupported(); return NULL;}
932
    virtual void forgetXMLDocument(const char *filename) {}
933
    virtual void flushXMLDocumentCache() {}
934
    virtual unsigned getExtraStackForEnvir() const  {return 0;}
935
    virtual cConfiguration *getConfig()  {unsupported(); return NULL;}
936
    virtual bool isGUI() const  {return false;}
937

    
938
    // UI functions (see also protected ones)
939
    virtual void bubble(cComponent *component, const char *text)  {}
940
    virtual std::string gets(const char *prompt, const char *defaultreply=NULL)  {unsupported(); return "";}
941
    virtual cEnvir& flush()  {::fflush(stdout); return *this;}
942

    
943
    // RNGs
944
    virtual int getNumRNGs() const {return 0;}
945
    virtual cRNG *getRNG(int k)  {unsupported(); return NULL;}
946
    virtual void getRNGMappingFor(cComponent *component)  {component->setRNGMap(0,NULL);}
947

    
948
    // output vectors
949
    virtual void *registerOutputVector(const char *modulename, const char *vectorname)  {return NULL;}
950
    virtual void deregisterOutputVector(void *vechandle)  {}
951
    virtual void setVectorAttribute(void *vechandle, const char *name, const char *value)  {}
952
    virtual bool recordInOutputVector(void *vechandle, simtime_t t, double value)  {return false;}
953

    
954
    // output scalars
955
    virtual void recordScalar(cComponent *component, const char *name, double value, opp_string_map *attributes=NULL)  {}
956
    virtual void recordStatistic(cComponent *component, const char *name, cStatistic *statistic, opp_string_map *attributes=NULL)  {}
957

    
958
    // snapshot file
959
    virtual std::ostream *getStreamForSnapshot()  {unsupported(); return NULL;}
960
    virtual void releaseStreamForSnapshot(std::ostream *os)  {unsupported();}
961

    
962
    // misc
963
    virtual int getArgCount() const  {unsupported(); return 0;}
964
    virtual char **getArgVector() const  {unsupported(); return NULL;}
965
    virtual int getParsimProcId() const {return 0;}
966
    virtual int getParsimNumPartitions() const {return 1;}
967
    virtual unsigned long getUniqueNumber()  {unsupported(); return 0;}
968
    virtual bool idle()  {return false;}
969
};
970

    
971
void StaticEnv::undisposedObject(cObject *obj)
972
{
973
    if (!cStaticFlag::isSet())
974
    {
975
        ::printf("<!> WARNING: global object variable (DISCOURAGED) detected: "
976
                 "(%s)`%s' at %p\n", obj->getClassName(), obj->getFullPath().c_str(), obj);
977
    }
978
}
979

    
980
static StaticEnv staticEnv;
981

    
982
// cSimulation's global variables
983
cEnvir *cSimulation::evPtr = &staticEnv;
984
cEnvir *cSimulation::staticEvPtr = &staticEnv;
985

    
986
cSimulation *cSimulation::simPtr = NULL;
987

    
988

    
989
NAMESPACE_END
990