Statistics
| Branch: | Revision:

root / src / sim / csimulation.cc @ 6b81f4fa

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

    
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
    event_num = 0;
104

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

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

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

    
117
    deleteNetwork();
118

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

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

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

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

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

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

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

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

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

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

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

    
203
    ostream& os = *osptr;
204

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

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

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

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

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

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

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

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

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

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

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

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

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

    
297
    last_id++;
298

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
390
    simulationstage = CTX_BUILD;
391

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

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

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

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

    
429
    simulationstage = CTX_INITIALIZE;
430

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

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

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

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

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

    
457
    simulationstage = CTX_FINISH;
458

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

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

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

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

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

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

    
490
    simulationstage = CTX_CLEANUP;
491

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

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

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

    
505
    networktype = NULL;
506

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

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

    
513
    simulationstage = CTX_NONE;
514

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

    
520
}
521

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

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

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

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

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

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

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

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

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

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

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

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

    
633
void cSimulation::doOneEvent()
634
{
635
    cMessage * msg = NULL;
636
    cSimpleModule* mod = NULL;
637

    
638
#ifndef NDEBUG
639
    checkActive();
640
#endif
641

    
642
    try
643
    {
644
        msg = schedulerp->getNextEvent();
645

    
646
        //Advance Simulation Time
647
        cThreadPool::setSimTime(msg->getArrivalTime());
648

    
649
        // Update mod pointer:
650
        mod = (cSimpleModule*) simulation.vect[msg->getArrivalModuleId()];
651
        // notify the environment about the event (writes eventlog, etc.)
652
        EVCB.simulationEvent(msg);
653
        // store arrival event number of this message; it is useful input for the
654
        // sequence chart tool if the message doesn't get immediately deleted or
655
        // sent out again
656
        msg->setPreviousEventNumber(event_num);
657
        // check if this module supports parallel execution
658
        if (mod->isAsyncModule())
659
        {
660
            // yes, it does. Next, check if this event should be executed
661
            // in parallel
662
            cAsyncModule* aMod = (cAsyncModule*) mod;
663

    
664
            simtime_t duration = msg->getEventDuration();
665
            if (duration < SimTime::simTimeZero)
666
            {
667
                throw cRuntimeError("negative event duration not allowed.");
668
            }
669

    
670
            bool mayPar = aMod->mayParallelize(msg, duration);
671
            // execute this event in parallel
672
            if (mayPar && threaded)
673
            {
674
                // block if another thread is busy inside this module
675
                // then set the module to busy
676
                aMod->waitIfBusy();
677
                aMod->setBusy();
678
                //printf("Offloading: %s\n",mod->getName());
679
                threadPool->insertTask(msg, duration);
680
            }
681
            else
682
            {
683
                // set the context for sequential execution
684
                setContext(mod);
685
                setContextType(CTX_EVENT);
686
                // take ownership in callHandleMessage after concurrency check
687
                aMod->callHandleMessage(msg);
688
            }
689
        }
690
        else
691
        { //The module is not a cAsyncModule
692

    
693
            // set the context for sequential execution
694
            setContext(mod);
695
            setContextType(CTX_EVENT);
696
            // take owner here: no danger of a race condition since
697
            // simple modules are only called from here
698
            mod->take(msg);
699
            // if there was an error during simulation, handleMessage()
700
            // will come back with an exception
701
            mod->handleMessage(msg);
702
        }
703

    
704
        //We have handled the Message, return
705
        setGlobalContext();
706
        event_num++;
707
        return;
708
    }
709
    catch (cDeleteModuleException& e)
710
    {
711
        assert(false); //not implemented
712
        setGlobalContext();
713
        mod->deleteModule();
714
    }
715
    catch (cException&)
716
    {
717
        // restore global context before throwing the exception further
718
        setGlobalContext();
719
        throw;
720
    }
721
    catch (std::exception& e)
722
    {
723
        // restore global context before throwing the exception further
724
        // but wrap into a cRuntimeError which captures the module before that
725
        cRuntimeError e2("%s: %s", opp_typename(typeid(e)), e.what());
726
        setGlobalContext();
727
        throw e2;
728
    }
729
}
730

    
731
void cSimulation::transferToMain()
732
{
733
    if (activitymodp!=NULL)
734
    {
735
        activitymodp = NULL;
736
        cCoroutine::switchToMain();     // stack switch
737
    }
738
}
739

    
740
void cSimulation::setContext(cComponent *p)
741
{
742
    cThreadPool::setContext(p);
743
    cThreadPool::setDefaultOwner(p);
744
}
745

    
746
cModule *cSimulation::getContextModule() const
747
{
748
    cComponent* ctxMod = cThreadPool::getContext();
749
    // cannot go inline (upward cast would require including cmodule.h in csimulation.h)
750
    if (!ctxMod || !ctxMod->isModule())
751
        return NULL;
752
    return (cModule *) ctxMod;
753
}
754

    
755
cSimpleModule *cSimulation::getContextSimpleModule() const
756
{
757
    cComponent* ctxMod = cThreadPool::getContext();
758
    // cannot go inline (upward cast would require including cmodule.h in csimulation.h)
759
    if (!ctxMod || !ctxMod->isModule() || !((cModule *) ctxMod)->isSimple())
760
        return NULL;
761
    return (cSimpleModule *) ctxMod;
762
}
763

    
764
unsigned long cSimulation::getUniqueNumber()
765
{
766
    return ev.getUniqueNumber();
767
}
768

    
769
void cSimulation::setHasher(cHasher *hasher)
770
{
771
    if (hasherp)
772
        delete hasherp;
773
    hasherp = hasher;
774
}
775

    
776
void cSimulation::insertMsg(cMessage *msg)
777
{
778
    msg->setPreviousEventNumber(event_num);
779
    simulation.msgQueue.insert(msg);
780
}
781

    
782
void cSimulation::setupThreadPool() {
783
    threaded = ev.getConfig()->getAsBool(CFGID_USE_THREADPOOL);
784

    
785
#ifdef ENABLE_OWNERSHIP
786
    if (threaded) {
787
        throw cRuntimeError(this, "Ownership Model is incompatible with threaded "
788
                "architecture of Horizon. Please disable threading or ownership");
789
    }
790
#endif
791

    
792
    if (threaded) {
793
        std::string threadPoolClass = ev.getConfig()->getAsString(
794
                CFGID_THREADPOOL_CLASS);
795
        if (threadPoolClass.compare("cLockedThreadPool") == 0) {
796
            threadPool = new cLockedThreadPool();
797
        } else if (threadPoolClass.compare("cSpinningThreadPool") == 0) {
798
            threadPool = new cSpinningThreadPool();
799
        } else {
800
            printf("WARNING: Threadpool class selected does not exist!\n");
801
            printf("Falling back to default (cSpinningThreadPool)\n");
802
            sleep(3);
803
            threadPool = new cSpinningThreadPool();
804
        }
805
        threadPool->activate();
806
    } else {
807
        //
808
        // set the cpu-id offset (used for demo reasons only)
809
        //
810
        unsigned cpuIdOffset = ev.getConfig()->getAsInt(
811
                CFGID_THREADPOOL_CPU_ID_OFFSET);
812
        unsigned cpuCount = (unsigned) sysconf(_SC_NPROCESSORS_ONLN);
813
        if (cpuIdOffset >= cpuCount) {
814
            throw cRuntimeError("Mis-configured CPU-id offset: exceeding number of"
815
                                " physical CPUs");
816
        }
817
        thread_set_affinity(pthread_self(), 0 + cpuIdOffset);
818
    }
819
}
820

    
821
//----
822

    
823
/**
824
 * A dummy implementation of cEnvir, only provided so that one
825
 * can use simulation library classes outside simulations, that is,
826
 * in programs that only link with the simulation library (<i>sim_std</i>)
827
 * and not with the <i>envir</i>, <i>cmdenv</i>, <i>tkenv</i>,
828
 * etc. libraries.
829
 *
830
 * Many simulation library classes make calls to <i>ev</i> methods,
831
 * which would crash if <tt>evPtr</tt> was NULL; one example is
832
 * cObject's destructor which contains an <tt>ev.objectDeleted()</tt>.
833
 * The solution provided here is that <tt>evPtr</tt> is initialized
834
 * to point to a StaticEnv instance, thus enabling library classes to work.
835
 *
836
 * StaticEnv methods either do nothing, or throw an "unsupported method"
837
 * exception, so StaticEnv is only useful for the most basic usage scenarios.
838
 * For anything more complicated, <tt>evPtr</tt> must be set in <tt>main()</tt>
839
 * to point to a proper cEnvir implementation, like the Cmdenv or
840
 * Tkenv classes. (The <i>envir</i> library provides a <tt>main()</tt>
841
 * which does exactly that.)
842
 *
843
 * @ingroup Envir
844
 */
845
class StaticEnv : public cEnvir
846
{
847
  protected:
848
    void unsupported() const {throw opp_runtime_error("StaticEnv: unsupported method called");}
849

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

    
854
  public:
855
    // constructor, destructor
856
    StaticEnv() {}
857
    virtual ~StaticEnv() {}
858

    
859
    // eventlog callback interface
860
    virtual void objectDeleted(cObject *object) {}
861
    virtual void simulationEvent(cMessage *msg)  {}
862
    virtual void messageSent_OBSOLETE(cMessage *msg, cGate *directToGate=NULL)  {}
863
    virtual void messageScheduled(cMessage *msg)  {}
864
    virtual void messageCancelled(cMessage *msg)  {}
865
    virtual void beginSend(cMessage *msg)  {}
866
    virtual void messageSendDirect(cMessage *msg, cGate *toGate, simtime_t propagationDelay, simtime_t transmissionDelay)  {}
867
    virtual void messageSendHop(cMessage *msg, cGate *srcGate)  {}
868
    virtual void messageSendHop(cMessage *msg, cGate *srcGate, simtime_t propagationDelay, simtime_t transmissionDelay)  {}
869
    virtual void endSend(cMessage *msg)  {}
870
    virtual void messageDeleted(cMessage *msg)  {}
871
    virtual void moduleReparented(cModule *module, cModule *oldparent)  {}
872
    virtual void componentMethodBegin(cComponent *from, cComponent *to, const char *methodFmt, va_list va, bool silent)  {}
873
    virtual void componentMethodEnd()  {}
874
    virtual void moduleCreated(cModule *newmodule)  {}
875
    virtual void moduleDeleted(cModule *module)  {}
876
    virtual void gateCreated(cGate *newgate)  {}
877
    virtual void gateDeleted(cGate *gate)  {}
878
    virtual void connectionCreated(cGate *srcgate)  {}
879
    virtual void connectionDeleted(cGate *srcgate)  {}
880
    virtual void displayStringChanged(cComponent *component)  {}
881
    virtual void undisposedObject(cObject *obj);
882

    
883
     // configuration, model parameters
884
    virtual void configure(cComponent *component) {}
885
    virtual void readParameter(cPar *parameter)  {unsupported();}
886
    virtual bool isModuleLocal(cModule *parentmod, const char *modname, int index)  {return true;}
887
    virtual cXMLElement *getXMLDocument(const char *filename, const char *path=NULL)  {unsupported(); return NULL;}
888
    virtual void forgetXMLDocument(const char *filename) {}
889
    virtual void flushXMLDocumentCache() {}
890
    virtual unsigned getExtraStackForEnvir() const  {return 0;}
891
    virtual cConfiguration *getConfig()  {unsupported(); return NULL;}
892
    virtual bool isGUI() const  {return false;}
893

    
894
    // UI functions (see also protected ones)
895
    virtual void bubble(cComponent *component, const char *text)  {}
896
    virtual std::string gets(const char *prompt, const char *defaultreply=NULL)  {unsupported(); return "";}
897
    virtual cEnvir& flush()  {::fflush(stdout); return *this;}
898

    
899
    // RNGs
900
    virtual int getNumRNGs() const {return 0;}
901
    virtual cRNG *getRNG(int k)  {unsupported(); return NULL;}
902
    virtual void getRNGMappingFor(cComponent *component)  {component->setRNGMap(0,NULL);}
903

    
904
    // output vectors
905
    virtual void *registerOutputVector(const char *modulename, const char *vectorname)  {return NULL;}
906
    virtual void deregisterOutputVector(void *vechandle)  {}
907
    virtual void setVectorAttribute(void *vechandle, const char *name, const char *value)  {}
908
    virtual bool recordInOutputVector(void *vechandle, simtime_t t, double value)  {return false;}
909

    
910
    // output scalars
911
    virtual void recordScalar(cComponent *component, const char *name, double value, opp_string_map *attributes=NULL)  {}
912
    virtual void recordStatistic(cComponent *component, const char *name, cStatistic *statistic, opp_string_map *attributes=NULL)  {}
913

    
914
    // snapshot file
915
    virtual std::ostream *getStreamForSnapshot()  {unsupported(); return NULL;}
916
    virtual void releaseStreamForSnapshot(std::ostream *os)  {unsupported();}
917

    
918
    // misc
919
    virtual int getArgCount() const  {unsupported(); return 0;}
920
    virtual char **getArgVector() const  {unsupported(); return NULL;}
921
    virtual int getParsimProcId() const {return 0;}
922
    virtual int getParsimNumPartitions() const {return 1;}
923
    virtual unsigned long getUniqueNumber()  {unsupported(); return 0;}
924
    virtual bool idle()  {return false;}
925
};
926

    
927
void StaticEnv::undisposedObject(cObject *obj)
928
{
929
    if (!cStaticFlag::isSet())
930
    {
931
        ::printf("<!> WARNING: global object variable (DISCOURAGED) detected: "
932
                 "(%s)`%s' at %p\n", obj->getClassName(), obj->getFullPath().c_str(), obj);
933
    }
934
}
935

    
936
static StaticEnv staticEnv;
937

    
938
// cSimulation's global variables
939
cEnvir *cSimulation::evPtr = &staticEnv;
940
cEnvir *cSimulation::staticEvPtr = &staticEnv;
941

    
942
cSimulation *cSimulation::simPtr = NULL;
943

    
944

    
945
NAMESPACE_END
946