Statistics
| Branch: | Revision:

root / src / sim / csimplemodule.cc @ fbe00e73

History | View | Annotate | Download (39.3 KB)

1
//======================================================================
2
//  CSIMPLEMODULE.CC - part of
3
//
4
//                 OMNeT++/OMNEST
5
//              Discrete System Simulation in C++
6
//
7
//   Member functions of
8
//    cSimpleModule   : base for simple module objects
9
//
10
//  Author: Andras Varga
11
//
12
//======================================================================
13

    
14
/*--------------------------------------------------------------*
15
  Copyright (C) 1992-2008 Andras Varga
16
  Copyright (C) 2006-2008 OpenSim Ltd.
17
  Copyright (C) 2009 Georg Kunz
18

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

    
23
#include <assert.h>
24
#include <stdio.h>           // sprintf
25
#include <string.h>          // strcpy
26
#include <exception>
27
#include <cstring>
28
#include <sstream>
29
#include <stdlib.h>
30
#include "csimplemodule.h"
31

    
32
#include "cgate.h"
33
#include "cmessage.h"
34
#include "ccoroutine.h"
35
#include "csimulation.h"
36
#include "carray.h"
37
#include "cmsgpar.h"
38
#include "cqueue.h"
39
#include "cenvir.h"
40
#include "cexception.h"
41
#include "commonutil.h"
42
#include "cthreadpool.h"
43
#include "cconfiguration.h"
44
#include "cconfigoption.h"
45
#include "../envir/envirbase.h"
46
#include "cstringtokenizer.h"
47

    
48

    
49
USING_NAMESPACE
50

    
51

    
52
bool cSimpleModule::stack_cleanup_requested;
53
cSimpleModule *cSimpleModule::after_cleanup_transfer_to;
54

    
55
Register_PerRunConfigOption(CFGID_ASYNCMODULE_PARZERODUR, "parallelize-zero-duration-events", CFG_BOOL, "false", "Parallelize Events with zero duration");
56
Register_PerObjectConfigOption(CFGID_NUM_LOCAL_RNGS, "number-local-rngs", CFG_INT, "1", "Number of RNGS per module");
57
Register_PerObjectConfigOption(CFGID_LOCAL_RNG_SEED, "local-rng-seed", CFG_STRING, "", "A whitespace seperated list of the seeds for each RNG of the corresponding module, eg '24 232 342 auto 342'");
58

    
59

    
60
void cSimpleModule::activate(void *p)
61
{
62
    cSimpleModule *mod = (cSimpleModule *)p;
63

    
64
    if (stack_cleanup_requested)
65
    {
66
        // module has just been created, but already deleted
67
        mod->setFlag(FL_ISTERMINATED, true);
68
        mod->setFlag(FL_STACKALREADYUNWOUND, true);
69
        if (after_cleanup_transfer_to)
70
            simulation.transferTo(after_cleanup_transfer_to);
71
        else
72
            simulation.transferToMain();
73
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
74
        abort();
75
    }
76

    
77
    // The starter message should be the same as the timeoutmsg member of
78
    // cSimpleModule. If not, then something is wrong...
79
    cMessage *starter = simulation.msg_for_activity;
80
    if (starter!=mod->timeoutmsg)
81
    {
82
        // hand exception to cSimulation::transferTo() and switch back
83
        mod->setFlag(FL_ISTERMINATED, true);
84
        mod->setFlag(FL_STACKALREADYUNWOUND, true);
85
        simulation.exception = new cRuntimeError("scheduleStart() should have been called for dynamically created module `%s'", mod->getFullPath().c_str());
86
        simulation.transferToMain();
87
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
88
        abort();
89
    }
90

    
91
    // rename message
92
    starter->setKind(MK_TIMEOUT);
93
    char buf[24];
94
    sprintf(buf,"timeout-%d", mod->getId());
95
    starter->setName(buf);
96

    
97
    cException *exception = NULL;
98
    try
99
    {
100
        //
101
        // call activity(). At this point, initialize() has already been called
102
        // from cSimulation::startRun(), or manually in the case of dynamically
103
        // created modules.
104
        //
105
        mod->activity();
106
    }
107
    catch (cRuntimeError *e) // compat
108
    {
109
        // IMPORTANT: No transferTo() in catch blocks! See Note 2 below.
110
        exception = new cRuntimeError("%s [NOTE: exception was thrown by pointer. "
111
                                      "In OMNeT++ 4.0+, exceptions have to be thrown by value. "
112
                                      "Please delete `new' from `throw new ...' in the code]",
113
                                      e->what());
114
        delete e;
115
    }
116
    catch (cException *e) // compat
117
    {
118
        // IMPORTANT: No transferTo() in catch blocks! See Note 2 below.
119
        exception = new cRuntimeError("%s [NOTE: exception was thrown with pointer. "
120
                                      "In OMNeT++ 4.0+, exceptions have to be thrown by value. "
121
                                      "Please delete `new' from `throw new ...' in the code]",
122
                                      e->what());
123
        delete e;
124
    }
125
    catch (cException& e)
126
    {
127
        // IMPORTANT: No transferTo() in catch blocks! See Note 2 below.
128
        exception = e.dup();
129
    }
130
    catch (std::exception& e)
131
    {
132
        // IMPORTANT: No transferTo() in catch blocks! See Note 2 below.
133
        // We need to wrap std::exception into cRuntimeError.
134
        exception = new cRuntimeError("%s: %s", opp_typename(typeid(e)), e.what());
135
    }
136

    
137
    //
138
    // Note 1: catch(...) is probably not a good idea because makes just-in-time debugging impossible on Windows
139
    // Note 2: with Visual C++, SwitchToFiber() calls in catch blocks mess up exception handling;
140
    // see http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=835791&SiteID=1&mode=1
141
    //
142

    
143
    // When we get here, the module is already terminated. No further cStackCleanupException
144
    // will need to be thrown, as the stack has has already been unwound by an exception
145
    // or by having returned from activity() normally.
146
    mod->setFlag(FL_ISTERMINATED, true);
147
    mod->setFlag(FL_STACKALREADYUNWOUND, true);
148

    
149
    if (!exception)
150
    {
151
        // Module function terminated normally, without exception. Just mark
152
        // the module as finished, and transfer to the main coroutine (fiber).
153
        simulation.transferToMain();
154
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
155
        abort();
156
    }
157
    else if (dynamic_cast<cStackCleanupException *>(exception))
158
    {
159
        // A cStackCleanupException exception has been thrown on purpose,
160
        // to force stack unwinding in the coroutine (fiber) function, activity().
161
        // Just transfer back to whoever forced the stack cleanup (the main coroutine
162
        // or some other simple module) and nothing else to do.
163
        delete exception;
164
        if (after_cleanup_transfer_to)
165
            simulation.transferTo(after_cleanup_transfer_to);
166
        else
167
            simulation.transferToMain();
168
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
169
        abort();
170
    }
171
    else
172
    {
173
        // Some exception (likely cRuntimeError, cTerminationException, or
174
        // cDeleteModuleException) occurred within the activity() function.
175
        // Pass this exception to the main coroutine so that it can be displayed as
176
        // an error dialog or the like.
177
        simulation.exception = exception;
178
        simulation.transferToMain();
179
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
180
        abort();
181
    }
182
}
183

    
184
// legacy constructor, only for backwards compatiblity; first two args are unused
185
cSimpleModule::cSimpleModule(const char *, cModule *, unsigned stksize) :
186
                t_end(0.0)
187
{
188
    coroutine = NULL;
189
    setFlag(FL_USESACTIVITY, stksize!=0);
190
    setFlag(FL_ISTERMINATED, false);
191
    setFlag(FL_STACKALREADYUNWOUND, false);
192

    
193
    // for an activity() module, timeoutmsg will be created in scheduleStart()
194
    // which must always be called
195
    timeoutmsg = NULL;
196

    
197
    if (usesActivity())
198
    {
199
       // setup coroutine, allocate stack for it
200
       coroutine = new cCoroutine;
201
       if (!coroutine->setup(cSimpleModule::activate, this, stksize+ev.getExtraStackForEnvir()))
202
           throw cRuntimeError("Cannot create coroutine with %d+%d bytes of stack space for module `%s' -- "
203
                               "see Manual for hints on how to increase the number of coroutines that can be created, "
204
                               "or rewrite modules to use handleMessage() instead of activity()",
205
                               stksize,ev.getExtraStackForEnvir(),getFullPath().c_str());
206
    }
207

    
208
        //
209
        // find out if events with 0 durations should also be parallelized?
210
        //
211
        parZeroDur = ev.getConfig()->getAsBool(CFGID_ASYNCMODULE_PARZERODUR);
212

    
213
        //
214
        // init state flag to non-busy since no thread is active yet
215
        //
216
        AO_store(&busy, 0);
217

    
218
        //
219
        // needs to be initialized here for use during model initialization
220
        //
221
        scheduledMessageCount = 0;
222
    currentEventPriority = 0;
223

    
224
        //
225
        // initialize with the current time. This variable is already used when
226
        // scheduling events during the initialization phase of the simulation.
227
        // Hence, it needs to be initialized. However, we cannot assume 0 here since
228
        // the constructor is also called during dynamic module creation at any
229
        // point in time during the simulation.
230
        //
231
        now = simTime();
232

    
233
}
234

    
235
cSimpleModule::cSimpleModule(unsigned stksize) :
236
                t_end(0.0)
237
{
238
    coroutine = NULL;
239
    setFlag(FL_USESACTIVITY, stksize!=0);
240
    setFlag(FL_ISTERMINATED, false);
241
    setFlag(FL_STACKALREADYUNWOUND, false);
242

    
243
    // for an activity() module, timeoutmsg will be created in scheduleStart()
244
    // which must always be called
245
    timeoutmsg = NULL;
246

    
247
    if (usesActivity())
248
    {
249
       // setup coroutine, allocate stack for it
250
       coroutine = new cCoroutine;
251
       if (!coroutine->setup(cSimpleModule::activate, this, stksize+ev.getExtraStackForEnvir()))
252
           throw cRuntimeError("Cannot create coroutine with %d+%d bytes of stack space for module `%s' -- "
253
                               "see Manual for hints on how to increase the number of coroutines that can be created, "
254
                               "or rewrite modules to use handleMessage() instead of activity()",
255
                               stksize,ev.getExtraStackForEnvir(),getFullPath().c_str());
256
    }
257

    
258
        //
259
        // find out if events with 0 durations should also be parallelized?
260
        //
261
        parZeroDur = ev.getConfig()->getAsBool(CFGID_ASYNCMODULE_PARZERODUR);
262

    
263
        //
264
        // init state flag to non-busy since no thread is active yet
265
        //
266
        AO_store(&busy, 0);
267

    
268
        //
269
        // needs to be initialized here for use during model initialization
270
        //
271
        scheduledMessageCount = 0;
272
    currentEventPriority = 0;
273

    
274
        //
275
        // initialize with the current time. This variable is already used when
276
        // scheduling events during the initialization phase of the simulation.
277
        // Hence, it needs to be initialized. However, we cannot assume 0 here since
278
        // the constructor is also called during dynamic module creation at any
279
        // point in time during the simulation.
280
        //
281
        now = simTime();
282

    
283
}
284

    
285
cSimpleModule::~cSimpleModule()
286
{
287
    if (simulation.getContext()==this)
288
        throw cRuntimeError(this, "cannot delete itself, only via deleteModule()");
289

    
290
    if (usesActivity())
291
    {
292
        // clean up user's objects on coroutine stack by forcing an exception inside the coroutine
293
        if ((flags&FL_STACKALREADYUNWOUND)==0)
294
        {
295
            //FIXME: check this is OK for brand new modules too (no transferTo() yet)
296
            stack_cleanup_requested = true;
297
            after_cleanup_transfer_to = simulation.getActivityModule();
298
            ASSERT(!after_cleanup_transfer_to || after_cleanup_transfer_to->usesActivity());
299
            simulation.transferTo(this);
300
            stack_cleanup_requested = false;
301
        }
302

    
303
        // delete timeoutmsg if not currently scheduled (then it'll be deleted by message queue)
304
        if (timeoutmsg && !timeoutmsg->isScheduled())
305
            delete timeoutmsg;
306

    
307
        // deallocate coroutine
308
        delete coroutine;
309
    }
310

    
311
    // deletion of pending messages for this module: not done since 3.1 because it
312
    // made it very long to clean up large models. Instead, such messages are
313
    // discarded in cSimulation::selectNextModule() when they're met.
314

    
315
    //for (cMessageHeap::Iterator iter(simulation.msgQueue); !iter.end(); iter++)
316
    //{
317
    //    cMessage *msg = iter();
318
    //    if (msg->getArrivalModuleId() == getId())
319
    //        delete simulation.msgQueue.get( msg );
320
    //}
321
}
322

    
323
std::string cSimpleModule::info() const
324
{
325
    std::stringstream out;
326
    out << "id=" << getId();
327
    return out.str();
328
}
329

    
330
void cSimpleModule::forEachChild(cVisitor *v)
331
{
332
    cModule::forEachChild(v);
333
}
334

    
335
void cSimpleModule::setId(int n)
336
{
337
    cModule::setId(n);
338

    
339
    if (timeoutmsg)
340
        timeoutmsg->setArrival(this, n);
341
}
342

    
343
void cSimpleModule::halt()
344
{
345
    if (!usesActivity())
346
        throw cRuntimeError("halt() can only be invoked from activity()-based simple modules");
347

    
348
    setFlag(FL_ISTERMINATED, true);
349
    simulation.transferToMain();
350
    assert(stack_cleanup_requested);
351
    throw cStackCleanupException();
352
}
353

    
354
#define BUFLEN 512
355

    
356
void cSimpleModule::error(const char *fmt...) const
357
{
358
    char buf[BUFLEN];
359
    VSNPRINTF(buf, BUFLEN, fmt);
360
    throw cRuntimeError(eUSER, buf);
361
}
362

    
363
#undef BUFLEN
364

    
365
//---------
366

    
367
void cSimpleModule::scheduleStart(simtime_t t)
368
{
369
    // Note: Simple modules using handleMessage() don't need starter
370
    // messages, but nevertheless, we still define scheduleStart()
371
    // for them. The benefit is that this way code that creates simple
372
    // modules dynamically doesn't have to know whether the module is
373
    // using activity() or handleMessage(); the same code (which
374
    // contains a call to scheduleStart()) can be used for both.
375
    if (usesActivity())
376
    {
377
        if (timeoutmsg!=NULL)
378
            throw cRuntimeError("scheduleStart(): module `%s' already started",getFullPath().c_str());
379

    
380
        Enter_Method_Silent("scheduleStart()");
381

    
382
        // create timeoutmsg, used as internal timeout message
383
        char buf[24];
384
        sprintf(buf,"starter-%d", getId());
385
        timeoutmsg = new cMessage(buf,MK_STARTER);
386

    
387
        // initialize message fields
388
        timeoutmsg->setSentFrom(NULL, -1, 0);
389
        timeoutmsg->setArrival(this, -1, t);
390

    
391
        // use timeoutmsg as the activation message; insert it into the FES
392
        EVCB.messageScheduled(timeoutmsg);
393
        simulation.insertMsg(timeoutmsg);
394
    }
395

    
396
    for (SubmoduleIterator submod(this); !submod.end(); submod++)
397
        submod()->scheduleStart(t);
398
}
399

    
400
void cSimpleModule::deleteModule()
401
{
402
    // if a coroutine wants to delete itself, that has to be handled from
403
    // another coroutine (i.e. from main). Control is passed there by
404
    // throwing an exception that gets transferred to the main coroutine
405
    // by activate(), and handled in cSimulation::transferTo().
406
    // execution must be immediately suspended in activity() and handleMessage()
407
    // if deleteModule() is called (i.e. we are deleting ourselves)
408
    // the exception will be handled in the doOneEvent loop and the module
409
    // will be deleted from the globalContext
410
    if (simulation.getContextModule()==this)
411
        throw cDeleteModuleException();
412

    
413
    // else fall back to the base class implementation
414
    cModule::deleteModule();
415
}
416

    
417
#define TRY(code, msgprefix) try {code;} catch(cRuntimeError& e) {e.prependMessage(msgprefix);throw;}
418

    
419
cMessage *cSimpleModule::cancelEvent(cMessage *msg)
420
{
421
    // make sure we really have the message and it is scheduled
422
    if (msg==NULL)
423
        throw cRuntimeError("cancelEvent(): message pointer is NULL");
424

    
425
    // now remove it from future events and return pointer
426
    if (msg->isScheduled())
427
    {
428
        if (!msg->isSelfMessage())
429
            throw cRuntimeError("cancelEvent(): message (%s)%s is not a self-message", msg->getClassName(), msg->getFullName());
430
        simulation.msgQueue.remove(msg);
431
        EVCB.messageCancelled(msg);
432
        msg->setPreviousEventNumber(simulation.getEventNumber());
433
    }
434

    
435
    return msg;
436
}
437

    
438
void cSimpleModule::cancelAndDelete(cMessage *msg)
439
{
440
    if (msg)
441
        delete cancelEvent(msg);
442
}
443

    
444
void cSimpleModule::arrived(cMessage *msg, cGate *ongate, simtime_t t)
445
{
446
    if (isTerminated())
447
        throw cRuntimeError(eMODFIN, getFullPath().c_str());
448
    if (t < simTime())
449
        throw cRuntimeError("Causality violation: message `%s' arrival time %s at module `%s' "
450
                            "is earlier than current simulation time",
451
                            msg->getName(), SIMTIME_STR(t), getFullPath().c_str());
452
    msg->setArrival(this, ongate->getId());
453
    bool isStart = ongate->getDeliverOnReceptionStart();
454
    if (msg->isPacket()) {
455
        cPacket *pkt = (cPacket *)msg;
456
        pkt->setReceptionStart(isStart);
457
        pkt->setArrivalTime(isStart ? t : t + pkt->getDuration());
458
    }
459
    else {
460
        msg->setArrivalTime(t);
461
    }
462
    simulation.insertMsg(msg);
463
}
464

    
465
void cSimpleModule::wait(simtime_t t)
466
{
467
    if (!usesActivity())
468
        throw cRuntimeError(eNORECV);
469
    if (t<0)
470
        throw cRuntimeError(eNEGTIME);
471

    
472
    timeoutmsg->setArrivalTime(simTime()+t);
473
    EVCB.messageScheduled(timeoutmsg);
474
    simulation.insertMsg(timeoutmsg);
475

    
476
    simulation.transferToMain();
477
    if (stack_cleanup_requested)
478
        throw cStackCleanupException();
479

    
480
    cMessage *newmsg = simulation.msg_for_activity;
481

    
482
    if (newmsg!=timeoutmsg)
483
        throw cRuntimeError("message arrived during wait() call ((%s)%s); if this "
484
                            "should be allowed, use waitAndEnqueue() instead of wait()",
485
                            newmsg->getClassName(), newmsg->getFullName());
486
}
487

    
488
void cSimpleModule::waitAndEnqueue(simtime_t t, cQueue *queue)
489
{
490
    if (!usesActivity())
491
        throw cRuntimeError(eNORECV);
492
    if (t<0)
493
        throw cRuntimeError(eNEGTIME);
494
    if (!queue)
495
        throw cRuntimeError("waitAndEnqueue(): queue pointer is NULL");
496

    
497
    timeoutmsg->setArrivalTime(simTime()+t);
498
    EVCB.messageScheduled(timeoutmsg);
499
    simulation.insertMsg(timeoutmsg);
500

    
501
    for(;;)
502
    {
503
        simulation.transferToMain();
504
        if (stack_cleanup_requested)
505
            throw cStackCleanupException();
506

    
507
        cMessage *newmsg = simulation.msg_for_activity;
508

    
509
        if (newmsg==timeoutmsg)
510
            break;
511
        else
512
            queue->insert(newmsg);
513
    }
514
}
515

    
516
//-------------
517

    
518
cMessage *cSimpleModule::receive()
519
{
520
    if (!usesActivity())
521
        throw cRuntimeError(eNORECV);
522

    
523
    simulation.transferToMain();
524
    if (stack_cleanup_requested)
525
        throw cStackCleanupException();
526

    
527
    cMessage *newmsg = simulation.msg_for_activity;
528
    return newmsg;
529
}
530

    
531
cMessage *cSimpleModule::receive(simtime_t t)
532
{
533
    if (!usesActivity())
534
        throw cRuntimeError(eNORECV);
535
    if (t<0)
536
        throw cRuntimeError(eNEGTOUT);
537

    
538
    timeoutmsg->setArrivalTime(simTime()+t);
539
    EVCB.messageScheduled(timeoutmsg);
540
    simulation.insertMsg(timeoutmsg);
541

    
542
    simulation.transferToMain();
543
    if (stack_cleanup_requested)
544
        throw cStackCleanupException();
545

    
546
    cMessage *newmsg = simulation.msg_for_activity;
547

    
548
    if (newmsg==timeoutmsg)  // timeout expired
549
    {
550
        take(timeoutmsg);
551
        return NULL;
552
    }
553
    else  // message received OK
554
    {
555
        take(cancelEvent(timeoutmsg));
556
        return newmsg;
557
    }
558
}
559

    
560
//-------------
561

    
562
void cSimpleModule::activity()
563
{
564
    // default thread function
565
    // the user must redefine this for the module to do anything useful
566
    throw cRuntimeError("Redefine activity() or specify zero stack size to use handleMessage()");
567
}
568

    
569
void cSimpleModule::handleMessage(cMessage *)
570
{
571
    // handleMessage is an alternative to activity()
572
    // this is the default version
573
    throw cRuntimeError("Redefine handleMessage() or specify non-zero stack size to use activity()");
574
}
575

    
576
//-------------
577

    
578
void cSimpleModule::endSimulation()
579
{
580
    // throwing an exception from a worker thread is not caught and the simulation
581
    // is not cleanly shut down. Workaround: signal shutdown directly to scheduler
582
    //throw cTerminationException(eENDSIM);
583
    simulation.signalShutdown();
584
}
585

    
586
bool cSimpleModule::snapshot(cObject *object, const char *label)
587
{
588
    return simulation.snapshot(object ? object : &simulation, label);
589
}
590

    
591
bool cSimpleModule::hasStackOverflow() const
592
{
593
    return coroutine ? coroutine->hasStackOverflow() : false;
594
}
595

    
596
unsigned cSimpleModule::getStackSize() const
597
{
598
    return coroutine ? coroutine->getStackSize() : 0;
599
}
600

    
601
unsigned cSimpleModule::getStackUsage() const
602
{
603
    return coroutine ? coroutine->getStackUsage() : 0;
604
}
605

    
606
void cSimpleModule::initLocalRNGs()
607
{
608
    //TODO
609
    unsigned nrNumGens;
610
    unsigned long * seeds;
611

    
612
    //
613
    // how many local random number generators do we need?
614
    // default is 1
615
    //
616
    nrNumGens = ev.getConfig()->parseLong(
617
            ev.getConfig()->getPerObjectConfigValue(getFullPath().c_str(),
618
                    "number-local-rngs"), "1", 1);
619

    
620
    //
621
    // read the seeds from ini file
622
    //
623
    const char * seedString = ev.getConfig()->getPerObjectConfigValue(
624
            getFullPath().c_str(), "local-rng-seed");
625

    
626
    //
627
    // setup seeds for each RNG
628
    //
629
    seeds = new unsigned long[nrNumGens];
630
    cStringTokenizer tokenizer(seedString);
631
    const char * token = NULL;
632
    for (unsigned i = 0; i < nrNumGens; i++)
633
    {
634
        if (tokenizer.hasMoreTokens())
635
        {
636
            token = tokenizer.nextToken();
637
            if (strcmp(token, "auto") == 0)
638
            {
639
                token = NULL;
640
            }
641
        }
642

    
643
        EnvirBase* e = dynamic_cast<EnvirBase*> (&ev);
644
        if (e != NULL)
645
        {
646
            if (token)
647
            {
648
                //if the seed is set in the ini for this module, use ini-settings
649
                //with fallback to autogeneration if parsing failes
650
                seeds[i] = ev.getConfig()->parseLong(token, "",
651
                        e->getSeedGenerator()->intRand());
652
            }
653
            else
654
            {
655
                //else use SeedGenerator for automatic seed generation.
656
                seeds[i] = e->getSeedGenerator()->intRand();
657
            }
658
            token = NULL;
659
        }
660
        else
661
        {
662
            throw cRuntimeError(
663
                    "cSimpleModule: Failed to initialize Number Generator, dynamic cast to EnvirBase failed.");
664
        }
665
    }
666

    
667
    //
668
    // generate the required number of RNGs with the corresponding seeds
669
    //
670
    setupSeeds(nrNumGens,seeds);
671
    delete[] seeds;
672

    
673
}
674

    
675
void cSimpleModule::setMessageMetaData(cMessage* msg)
676
{
677
    msg->setEventDuration(SimTime::simTimeUninitialized);
678
    msg->setSchedulingOrderId(scheduledMessageCount++);
679
    msg->setParentExecutionOrderId(executionOrderId);
680
    msg->setParentStartTime(now);
681
}
682

    
683
int cSimpleModule::scheduleAt(simtime_t t, cMessage *msg)
684
{
685
    setMessageMetaData(msg);
686

    
687
    //
688
    // check current state
689
    //
690
    if (executionState == asynchronous)
691
    {
692
        if (msg==NULL)
693
                        throw cRuntimeError("scheduleAt(): message pointer is NULL");
694
        if (t < t_end)
695
                throw cRuntimeError("scheduleAt(): event cannot be scheduled at "
696
                                "%s because this precedes the end of the processing duration "
697
                                "at %s of the scheduling event.",
698
                                t.str().c_str(), t_end.str().c_str());
699

    
700
        if (t == now && currentEventPriority < msg->getSchedulingPriority()) {
701
            throw cRuntimeError("scheduleAt(): priority of scheduled Event larger than scheduling event and equal starting times => scheduling in the past");
702
        }
703
        // insert this message in the FES. When the scheduler arrives at this
704
        // message it will trigger the processing of the associated task
705
        msg->setSentFrom(this, -1, t_end);
706
        msg->setArrival(this, -1, t);
707

    
708
        simulation.msgQueue.insert(msg);
709
        return 0;
710
    }
711
    else // synch execution
712
    {
713
                if (msg==NULL)
714
                        throw cRuntimeError("scheduleAt(): message pointer is NULL");
715
                if (t<simTime())
716
                        throw cRuntimeError(eBACKSCHED, msg->getClassName(), msg->getName(), SIMTIME_DBL(t));
717
                if (msg->getOwner()!=this)
718
                {
719
                        if (this!=simulation.getContextModule())
720
                                throw cRuntimeError("scheduleAt() of module (%s)%s called in the context of "
721
                                                "module (%s)%s: method called from the latter module "
722
                                                "lacks Enter_Method() or Enter_Method_Silent()?",
723
                                                getClassName(), getFullPath().c_str(),
724
                                                simulation.getContextModule()->getClassName(),
725
                                                simulation.getContextModule()->getFullPath().c_str());
726
                        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
727
                                throw cRuntimeError("scheduleAt(): message (%s)%s is currently scheduled, "
728
                                                "use cancelEvent() before rescheduling",
729
                                                msg->getClassName(), msg->getName());
730
                        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
731
                                throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, it is "
732
                                                "currently scheduled as self-message for ANOTHER module",
733
                                                msg->getClassName(), msg->getName());
734

    
735
                        else if (msg->getOwner()==&simulation.msgQueue)
736
                                throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, it is "
737
                                                "currently in scheduled-events, being underway between two modules",
738
                                                msg->getClassName(), msg->getName());
739
                        else
740
                                throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, "
741
                                                "it is currently contained/owned by (%s)%s",
742
                                                msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
743
                                                msg->getOwner()->getFullPath().c_str());
744
                }
745

    
746
                // set message parameters and schedule it
747
                msg->setSentFrom(this, -1, simTime());
748
                msg->setArrival(this, -1, t);
749
                EVCB.messageSent_OBSOLETE( msg ); //XXX obsolete but needed for Tkenv
750
                EVCB.messageScheduled(msg);
751
                simulation.insertMsg(msg);
752
                return 0;
753
    }
754
}
755

    
756

    
757
void cSimpleModule::callHandleAsyncMessage(cMessage* msg)
758
{
759
    //
760
    // set execution state
761
    //
762
    executionState = asynchronous;
763

    
764
    //
765
    // set the simtime for this event
766
    //
767
    cThreadPool::setSimTime(msg->getArrivalTime());
768
    // corresponds to setContextModule(mod);
769
    cThreadPool::setContext(this);
770
    cThreadPool::setDefaultOwner(this);
771

    
772
    //
773
    // update meta data
774
    //
775
    prepareHandleMessage(msg);
776

    
777
    //
778
    // call the actual method
779
    //
780
    handleMessage(msg);
781
}
782

    
783

    
784
void cSimpleModule::callHandleMessage(cMessage* msg)
785
{
786
    //
787
    // set execution state
788
    //
789
    executionState = synchronous;
790

    
791
    //
792
    // update meta data
793
    //
794
    prepareHandleMessage(msg);
795

    
796
    //
797
    // call the actual method
798
    //
799
    handleMessage(msg);
800
}
801

    
802

    
803
void cSimpleModule::prepareHandleMessage(cMessage* msg)
804
{
805
    //
806
    // set some internal state
807
    //
808
    t_end = msg->getArrivalTime() + msg->getEventDuration();
809
    now = msg->getArrivalTime();
810
    currentEventPriority = msg->getSchedulingPriority();
811

    
812
    //
813
    // take ownership of the message only after the test above
814
    //
815
    this->take(msg);
816

    
817
    //
818
    // reset message counter
819
    // message 0 is the barriermsg
820
    //
821
    scheduledMessageCount = 1;
822

    
823
    executionOrderId = msg->getExecutionOrderId();
824

    
825
}
826

    
827
int cSimpleModule::send(cMessage *msg, int gateid)
828
{
829
    return sendDelayed(msg, 0.0, gateid);
830
}
831

    
832

    
833
int cSimpleModule::send(cMessage *msg, const char *gatename, int sn)
834
{
835
    return sendDelayed(msg, 0.0, gatename, sn);
836
}
837

    
838

    
839
int cSimpleModule::send(cMessage *msg, cGate *outputgate)
840
{
841
    return sendDelayed(msg, 0.0, outputgate);
842
}
843

    
844

    
845
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, int gateid)
846
{
847
    cGate *outgate = gate(gateid);
848
    if (outgate==NULL)
849
        throw cRuntimeError("send()/sendDelayed(): module has no gate #%d", gateid);
850

    
851
    return sendDelayed(msg, delay, outgate);
852
}
853

    
854

    
855
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, const char *gatename, int sn)
856
{
857
    cGate *outgate = gate(gatename, sn);
858
    if (outgate==NULL)
859
       throw cRuntimeError(sn<0 ? "send()/sendDelayed(): module has no gate `%s'":
860
                               "send()/sendDelayed(): module has no gate `%s[%d]'",gatename,sn);
861

    
862
    return sendDelayed(msg, delay, outgate);
863
}
864

    
865

    
866
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, cGate *outputgate)
867
{
868
    setMessageMetaData(msg);
869

    
870
    //
871
    // check current state
872
    //
873
    if (executionState == asynchronous)
874
    {
875
        //
876
        // error checking, omit ownership checks if it breaks
877
        //
878
        if (outputgate==NULL)
879
            throw cRuntimeError("send()/sendDelayed(): gate pointer is NULL");
880
        if (outputgate->getType()=='I')
881
            throw cRuntimeError("send()/sendDelayed(): cannot send via an input gate (`%s')",outputgate->getName());
882
        if (!outputgate->getNextGate())  // NOTE: without this error check, msg would become self-message
883
            throw cRuntimeError("send()/sendDelayed(): gate `%s' not connected",outputgate->getFullName());
884
        if (msg==NULL)
885
            throw cRuntimeError("send()/sendDelayed(): message pointer is NULL");
886
        if (msg->getOwner()!=this)
887
        {
888
            if (this!=simulation.getContext())
889
                throw cRuntimeError("send()/sendDelayed() of module (%s)%s called in the context of "
890
                                        "module (%s)%s: method called from the latter module "
891
                                        "lacks Enter_Method() or Enter_Method_Silent()? "
892
                                        "Also, if message to be sent is passed from that module, "
893
                                        "you'll need to call take(msg) after Enter_Method() as well",
894
                                        getClassName(), getFullPath().c_str(),
895
                                        simulation.getContext()->getClassName(),
896
                                        simulation.getContext()->getFullPath().c_str());
897
            else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
898
                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
899
                                        "currently scheduled as a self-message for this module",
900
                                        msg->getClassName(), msg->getName());
901
            else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
902
                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
903
                                        "currently scheduled as a self-message for ANOTHER module",
904
                                        msg->getClassName(), msg->getName());
905
            else if (msg->getOwner()==&simulation.msgQueue)
906
                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
907
                                        "currently in scheduled-events, being underway between two modules",
908
                                        msg->getClassName(), msg->getName());
909
            else
910
                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, "
911
                                        "it is currently contained/owned by (%s)%s",
912
                                        msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
913
                                        msg->getOwner()->getFullPath().c_str());
914
        }
915

    
916
        if (delay < 0.0)
917
            throw cRuntimeError("sendDelayed(): negative delay %s",delay.str().c_str());
918

    
919
        simtime_t arrivalTime = now + delay;
920
        if (arrivalTime < t_end) // TODO: Consider channel delay!
921
            throw cRuntimeError("sendDelayed(): send delay shorter than processing delay %s", delay.str().c_str());
922

    
923
        if (delay == 0 && currentEventPriority < msg->getSchedulingPriority()) {
924
            throw cRuntimeError("sendDelayed(): priority of scheduled Event larger than scheduling event and equal starting times => scheduling in the past");
925
        }
926
        //
927
        // set message parameters and send it
928
        //
929
        msg->setSentFrom(this, outputgate->getId(), arrivalTime);
930
        bool keepit = outputgate->deliver(msg, arrivalTime);
931
        if (!keepit)
932
            delete msg;
933

    
934
        return 0;
935
    }
936
    else // syncExecution
937
    {
938
                // error checking:
939
                if (outputgate==NULL)
940
                        throw cRuntimeError("send()/sendDelayed(): gate pointer is NULL");
941
                if (outputgate->getType()==cGate::INPUT)
942
                        throw cRuntimeError("send()/sendDelayed(): cannot send via an input gate (`%s')", outputgate->getFullName());
943
                if (!outputgate->getNextGate())  // NOTE: without this error check, msg would become self-message
944
                        throw cRuntimeError("send()/sendDelayed(): gate `%s' not connected", outputgate->getFullName());
945
                if (msg==NULL)
946
                        throw cRuntimeError("send()/sendDelayed(): message pointer is NULL");
947
                if (msg->getOwner()!=this)
948
                {
949
                        if (this!=simulation.getContextModule())
950
                                throw cRuntimeError("send()/sendDelayed() of module (%s)%s called in the context of "
951
                                                "module (%s)%s: method called from the latter module "
952
                                                "lacks Enter_Method() or Enter_Method_Silent()? "
953
                                                "Also, if message to be sent is passed from that module, "
954
                                                "you'll need to call take(msg) after Enter_Method() as well",
955
                                                getClassName(), getFullPath().c_str(),
956
                                                simulation.getContextModule()->getClassName(),
957
                                                simulation.getContextModule()->getFullPath().c_str());
958
                        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
959
                                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
960
                                                "currently scheduled as a self-message for this module",
961
                                                msg->getClassName(), msg->getName());
962
                        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
963
                                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
964
                                                "currently scheduled as a self-message for ANOTHER module",
965
                                                msg->getClassName(), msg->getName());
966
                        else if (msg->getOwner()==&simulation.msgQueue)
967
                                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
968
                                                "currently in scheduled-events, being underway between two modules",
969
                                                msg->getClassName(), msg->getName());
970
                        else
971
                                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, "
972
                                                "it is currently contained/owned by (%s)%s",
973
                                                msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
974
                                                msg->getOwner()->getFullPath().c_str());
975
                }
976
                if (delay < 0)
977
                        throw cRuntimeError("sendDelayed(): negative delay %s", SIMTIME_STR(delay));
978

    
979
                // set message parameters and send it
980
                simtime_t delayEndTime = simTime()+delay;
981
                msg->setSentFrom(this, outputgate->getId(), delayEndTime);
982
                if (msg->isPacket())
983
                        ((cPacket *)msg)->setDuration(0);
984

    
985
                EVCB.beginSend(msg);
986
                bool keepit = outputgate->deliver(msg, delayEndTime);
987
                if (!keepit)
988
                {
989
                        delete msg; //FIXME problem: tell tkenv somehow that msg has been deleted, otherwise animation will crash
990
                }
991
                else
992
                {
993
                        EVCB.messageSent_OBSOLETE(msg); //FIXME obsolete
994
                        EVCB.endSend(msg);
995
                }
996
                return 0;
997
    }
998
}
999

    
1000

    
1001
int cSimpleModule::sendDirect(cMessage *msg, simtime_t delay, simtime_t duration, cModule *mod, int inputgateid)
1002
{
1003
    cGate *togate = mod->gate(inputgateid);
1004
    if (togate==NULL)
1005
        throw cRuntimeError("sendDirect(): module `%s' has no gate #%d",
1006
                                mod->getFullPath().c_str(), inputgateid);
1007

    
1008
    return sendDirect(msg, delay, duration, togate);
1009
}
1010

    
1011

    
1012
int cSimpleModule::sendDirect(cMessage *msg, simtime_t delay, simtime_t duration, cModule *mod, const char *inputgatename, int sn)
1013
{
1014
    if (!mod)
1015
        throw cRuntimeError("sendDirect(): module ptr is NULL");
1016
    cGate *togate = mod->gate(inputgatename,sn);
1017
    if (togate==NULL)
1018
        throw cRuntimeError(sn<0 ? "sendDirect(): module `%s' has no gate `%s'":
1019
                                "sendDirect(): module `%s' has no gate `%s[%d]'",
1020
                                mod->getFullPath().c_str(), inputgatename, sn);
1021
    return sendDirect(msg, delay, duration, togate);
1022
}
1023

    
1024

    
1025
int cSimpleModule::sendDirect(cMessage *msg, simtime_t delay, simtime_t duration, cGate *togate)
1026
{
1027
    setMessageMetaData(msg);
1028

    
1029
    //
1030
    // check current state
1031
    //
1032
    if (executionState == asynchronous)
1033
    {
1034
        //
1035
        // error checking
1036
        //
1037
        // Note: it is permitted to send to an output gate. It is especially useful
1038
        // with several submodules sending to a single output gate of their parent module.
1039
        if (togate==NULL)
1040
                throw cRuntimeError("sendDirect(): destination gate pointer is NULL");
1041
        if (togate->getPreviousGate())
1042
                throw cRuntimeError("sendDirect(): module must have dedicated gate(s) for receiving via sendDirect()"
1043
                                                                " (\"from\" side of dest. gate `%s' should NOT be connected)",togate->getFullPath().c_str());
1044
        if (msg==NULL)
1045
                throw cRuntimeError("sendDirect(): message pointer is NULL");
1046
        if (msg->getOwner()!=this)
1047
        {
1048
                if (this!=simulation.getContext())
1049
                        throw cRuntimeError("sendDirect() of module (%s)%s called in the context of "
1050
                                                                        "module (%s)%s: method called from the latter module "
1051
                                                                        "lacks Enter_Method() or Enter_Method_Silent()? "
1052
                                                                        "Also, if message to be sent is passed from that module, "
1053
                                                                        "you'll need to call take(msg) after Enter_Method() as well",
1054
                                                                        getClassName(), getFullPath().c_str(),
1055
                                                                        simulation.getContext()->getClassName(),
1056
                                                                        simulation.getContext()->getFullPath().c_str());
1057
                else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
1058
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
1059
                                                                        "currently scheduled as a self-message for this module",
1060
                                                                        msg->getClassName(), msg->getName());
1061
                else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
1062
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
1063
                                                                        "currently scheduled as a self-message for ANOTHER module",
1064
                                                                        msg->getClassName(), msg->getName());
1065
                else if (msg->getOwner()==&simulation.msgQueue)
1066
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
1067
                                                                        "currently in scheduled-events, being underway between two modules",
1068
                                                                        msg->getClassName(), msg->getName());
1069
                else
1070
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, "
1071
                                                                        "it is currently contained/owned by (%s)%s",
1072
                                                                        msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
1073
                                                                        msg->getOwner()->getFullPath().c_str());
1074
        }
1075

    
1076
        simtime_t arrivalTime = now + delay;
1077
        if (arrivalTime < t_end) // TODO: Consider channel delay!
1078
            throw cRuntimeError("sendDirect(): send delay shorter than processing delay %s",delay.str().c_str());
1079

    
1080
        if (delay == 0 && currentEventPriority < msg->getSchedulingPriority()) {
1081
            throw cRuntimeError("sendDirect(): priority of scheduled Event larger than scheduling event and equal starting times => scheduling in the past");
1082
        }
1083

    
1084
        // set message parameters and send it
1085
        msg->setSentFrom(this, -1, now);
1086

    
1087
        bool keepit = togate->deliver(msg, arrivalTime);
1088
        if (!keepit)
1089
                delete msg;
1090
        return 0;
1091
    }
1092
    else  // sync execution
1093
    {
1094
            return cSimpleModule::sendDirect(msg, delay, duration, togate);
1095
    }
1096
}