Project

General

Profile

Statistics
| Branch: | Revision:

root / src / sim / csimplemodule.cc @ 8b911557

History | View | Annotate | Download (39.9 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
#include "eventtype.h"
49

    
50

    
51
USING_NAMESPACE
52

    
53

    
54
bool cSimpleModule::stack_cleanup_requested;
55
cSimpleModule *cSimpleModule::after_cleanup_transfer_to;
56

    
57
Register_PerRunConfigOption(CFGID_ASYNCMODULE_PARZERODUR, "parallelize-zero-duration-events", CFG_BOOL, "false", "Parallelize Events with zero duration");
58
Register_PerObjectConfigOption(CFGID_NUM_LOCAL_RNGS, "number-local-rngs", CFG_INT, "1", "Number of RNGS per module");
59
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'");
60

    
61

    
62
void cSimpleModule::activate(void *p)
63
{
64
    cSimpleModule *mod = (cSimpleModule *)p;
65

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

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

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

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

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

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

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

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

    
197
    // for an activity() module, timeoutmsg will be created in scheduleStart()
198
    // which must always be called
199
    timeoutmsg = NULL;
200

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

    
212
        //
213
        // find out if events with 0 durations should also be parallelized?
214
        //
215
        parZeroDur = ev.getConfig()->getAsBool(CFGID_ASYNCMODULE_PARZERODUR);
216

    
217
        //
218
        // init state flag to non-busy since no thread is active yet
219
        //
220
        AO_store(&busy, 0);
221

    
222
        //
223
        // needs to be initialized here for use during model initialization
224
        //
225
        scheduledMessageCount = 0;
226
    currentEventPriority = 0;
227

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

    
237
}
238

    
239
cSimpleModule::cSimpleModule(unsigned stksize) :
240
                currentEventType(NULL),
241
                executionState(asynchronous),
242
                t_end(0.0)
243
{
244
    coroutine = NULL;
245
    setFlag(FL_USESACTIVITY, stksize!=0);
246
    setFlag(FL_ISTERMINATED, false);
247
    setFlag(FL_STACKALREADYUNWOUND, false);
248

    
249
    // for an activity() module, timeoutmsg will be created in scheduleStart()
250
    // which must always be called
251
    timeoutmsg = NULL;
252

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

    
264
        //
265
        // find out if events with 0 durations should also be parallelized?
266
        //
267
        parZeroDur = ev.getConfig()->getAsBool(CFGID_ASYNCMODULE_PARZERODUR);
268

    
269
        //
270
        // init state flag to non-busy since no thread is active yet
271
        //
272
        AO_store(&busy, 0);
273

    
274
        //
275
        // needs to be initialized here for use during model initialization
276
        //
277
        scheduledMessageCount = 0;
278
    currentEventPriority = 0;
279

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

    
289
}
290

    
291
cSimpleModule::~cSimpleModule()
292
{
293
    if (simulation.getContext()==this)
294
        throw cRuntimeError(this, "cannot delete itself, only via deleteModule()");
295

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

    
309
        // delete timeoutmsg if not currently scheduled (then it'll be deleted by message queue)
310
        if (timeoutmsg && !timeoutmsg->isScheduled())
311
            delete timeoutmsg;
312

    
313
        // deallocate coroutine
314
        delete coroutine;
315
    }
316

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

    
321
    //for (cMessageHeap::Iterator iter(simulation.msgQueue); !iter.end(); iter++)
322
    //{
323
    //    cMessage *msg = iter();
324
    //    if (msg->getArrivalModuleId() == getId())
325
    //        delete simulation.msgQueue.get( msg );
326
    //}
327
}
328

    
329
std::string cSimpleModule::info() const
330
{
331
    std::stringstream out;
332
    out << "id=" << getId();
333
    return out.str();
334
}
335

    
336
void cSimpleModule::forEachChild(cVisitor *v)
337
{
338
    cModule::forEachChild(v);
339
}
340

    
341
void cSimpleModule::setId(int n)
342
{
343
    cModule::setId(n);
344

    
345
    if (timeoutmsg)
346
        timeoutmsg->setArrival(this, n);
347
}
348

    
349
void cSimpleModule::halt()
350
{
351
    if (!usesActivity())
352
        throw cRuntimeError("halt() can only be invoked from activity()-based simple modules");
353

    
354
    setFlag(FL_ISTERMINATED, true);
355
    simulation.transferToMain();
356
    assert(stack_cleanup_requested);
357
    throw cStackCleanupException();
358
}
359

    
360
#define BUFLEN 512
361

    
362
void cSimpleModule::error(const char *fmt...) const
363
{
364
    char buf[BUFLEN];
365
    VSNPRINTF(buf, BUFLEN, fmt);
366
    throw cRuntimeError(eUSER, buf);
367
}
368

    
369
#undef BUFLEN
370

    
371
//---------
372

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

    
386
        Enter_Method_Silent("scheduleStart()");
387

    
388
        // create timeoutmsg, used as internal timeout message
389
        char buf[24];
390
        sprintf(buf,"starter-%d", getId());
391
        timeoutmsg = new cMessage(buf,MK_STARTER);
392

    
393
        // initialize message fields
394
        timeoutmsg->setSentFrom(NULL, -1, 0);
395
        timeoutmsg->setArrival(this, -1, t);
396

    
397
        // use timeoutmsg as the activation message; insert it into the FES
398
        EVCB.messageScheduled(timeoutmsg);
399
        simulation.insertMsg(timeoutmsg);
400
    }
401

    
402
    for (SubmoduleIterator submod(this); !submod.end(); submod++)
403
        submod()->scheduleStart(t);
404
}
405

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

    
419
    // else fall back to the base class implementation
420
    cModule::deleteModule();
421
}
422

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

    
425
cMessage *cSimpleModule::cancelEvent(cMessage *msg)
426
{
427
    // make sure we really have the message and it is scheduled
428
    if (msg==NULL)
429
        throw cRuntimeError("cancelEvent(): message pointer is NULL");
430

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

    
441
    return msg;
442
}
443

    
444
void cSimpleModule::cancelAndDelete(cMessage *msg)
445
{
446
    if (msg)
447
        delete cancelEvent(msg);
448
}
449

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

    
471
void cSimpleModule::wait(simtime_t t)
472
{
473
    if (!usesActivity())
474
        throw cRuntimeError(eNORECV);
475
    if (t<0)
476
        throw cRuntimeError(eNEGTIME);
477

    
478
    timeoutmsg->setArrivalTime(simTime()+t);
479
    EVCB.messageScheduled(timeoutmsg);
480
    simulation.insertMsg(timeoutmsg);
481

    
482
    simulation.transferToMain();
483
    if (stack_cleanup_requested)
484
        throw cStackCleanupException();
485

    
486
    cMessage *newmsg = simulation.msg_for_activity;
487

    
488
    if (newmsg!=timeoutmsg)
489
        throw cRuntimeError("message arrived during wait() call ((%s)%s); if this "
490
                            "should be allowed, use waitAndEnqueue() instead of wait()",
491
                            newmsg->getClassName(), newmsg->getFullName());
492
}
493

    
494
void cSimpleModule::waitAndEnqueue(simtime_t t, cQueue *queue)
495
{
496
    if (!usesActivity())
497
        throw cRuntimeError(eNORECV);
498
    if (t<0)
499
        throw cRuntimeError(eNEGTIME);
500
    if (!queue)
501
        throw cRuntimeError("waitAndEnqueue(): queue pointer is NULL");
502

    
503
    timeoutmsg->setArrivalTime(simTime()+t);
504
    EVCB.messageScheduled(timeoutmsg);
505
    simulation.insertMsg(timeoutmsg);
506

    
507
    for(;;)
508
    {
509
        simulation.transferToMain();
510
        if (stack_cleanup_requested)
511
            throw cStackCleanupException();
512

    
513
        cMessage *newmsg = simulation.msg_for_activity;
514

    
515
        if (newmsg==timeoutmsg)
516
            break;
517
        else
518
            queue->insert(newmsg);
519
    }
520
}
521

    
522
//-------------
523

    
524
cMessage *cSimpleModule::receive()
525
{
526
    if (!usesActivity())
527
        throw cRuntimeError(eNORECV);
528

    
529
    simulation.transferToMain();
530
    if (stack_cleanup_requested)
531
        throw cStackCleanupException();
532

    
533
    cMessage *newmsg = simulation.msg_for_activity;
534
    return newmsg;
535
}
536

    
537
cMessage *cSimpleModule::receive(simtime_t t)
538
{
539
    if (!usesActivity())
540
        throw cRuntimeError(eNORECV);
541
    if (t<0)
542
        throw cRuntimeError(eNEGTOUT);
543

    
544
    timeoutmsg->setArrivalTime(simTime()+t);
545
    EVCB.messageScheduled(timeoutmsg);
546
    simulation.insertMsg(timeoutmsg);
547

    
548
    simulation.transferToMain();
549
    if (stack_cleanup_requested)
550
        throw cStackCleanupException();
551

    
552
    cMessage *newmsg = simulation.msg_for_activity;
553

    
554
    if (newmsg==timeoutmsg)  // timeout expired
555
    {
556
        take(timeoutmsg);
557
        return NULL;
558
    }
559
    else  // message received OK
560
    {
561
        take(cancelEvent(timeoutmsg));
562
        return newmsg;
563
    }
564
}
565

    
566
//-------------
567

    
568
void cSimpleModule::activity()
569
{
570
    // default thread function
571
    // the user must redefine this for the module to do anything useful
572
    throw cRuntimeError("Redefine activity() or specify zero stack size to use handleMessage()");
573
}
574

    
575
void cSimpleModule::handleMessage(cMessage *)
576
{
577
    // handleMessage is an alternative to activity()
578
    // this is the default version
579
    throw cRuntimeError("Redefine handleMessage() or specify non-zero stack size to use activity()");
580
}
581

    
582
//-------------
583

    
584
void cSimpleModule::endSimulation()
585
{
586
    // throwing an exception from a worker thread is not caught and the simulation
587
    // is not cleanly shut down. Workaround: signal shutdown directly to scheduler
588
    //throw cTerminationException(eENDSIM);
589
    simulation.signalShutdown();
590
}
591

    
592
bool cSimpleModule::snapshot(cObject *object, const char *label)
593
{
594
    return simulation.snapshot(object ? object : &simulation, label);
595
}
596

    
597
bool cSimpleModule::hasStackOverflow() const
598
{
599
    return coroutine ? coroutine->hasStackOverflow() : false;
600
}
601

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

    
607
unsigned cSimpleModule::getStackUsage() const
608
{
609
    return coroutine ? coroutine->getStackUsage() : 0;
610
}
611

    
612
void cSimpleModule::initLocalRNGs()
613
{
614
    //TODO
615
    unsigned nrNumGens;
616
    unsigned long * seeds;
617

    
618
    //
619
    // how many local random number generators do we need?
620
    // default is 1
621
    //
622
    nrNumGens = ev.getConfig()->parseLong(
623
            ev.getConfig()->getPerObjectConfigValue(getFullPath().c_str(),
624
                    "number-local-rngs"), "1", 1);
625

    
626
    //
627
    // read the seeds from ini file
628
    //
629
    const char * seedString = ev.getConfig()->getPerObjectConfigValue(
630
            getFullPath().c_str(), "local-rng-seed");
631

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

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

    
673
    //
674
    // generate the required number of RNGs with the corresponding seeds
675
    //
676
    setupSeeds(nrNumGens,seeds);
677
    delete[] seeds;
678

    
679
}
680

    
681
void cSimpleModule::setMessageMetaData(cMessage* msg)
682
{
683
    msg->setEventDuration(SimTime::simTimeUninitialized);
684
    msg->setSchedulingOrderId(scheduledMessageCount++);
685
    msg->setParentExecutionOrderId(executionOrderId);
686
    msg->setParentStartTime(now);
687
}
688

    
689
int cSimpleModule::scheduleAt(simtime_t t, cMessage *msg)
690
{
691
    setMessageMetaData(msg);
692

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

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

    
714
                if(currentEventType) currentEventType->newSuccEventInstance(msg,t-now);
715

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

    
743
                        else if (msg->getOwner()==&simulation.msgQueue)
744
                                throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, it is "
745
                                                "currently in scheduled-events, being underway between two modules",
746
                                                msg->getClassName(), msg->getName());
747
                        else
748
                                throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, "
749
                                                "it is currently contained/owned by (%s)%s",
750
                                                msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
751
                                                msg->getOwner()->getFullPath().c_str());
752
                }
753

    
754
                // set message parameters and schedule it
755
                msg->setSentFrom(this, -1, simTime());
756
                msg->setArrival(this, -1, t);
757

    
758
                if(currentEventType) currentEventType->newSuccEventInstance(msg,t-now);
759

    
760
                EVCB.messageSent_OBSOLETE( msg ); //XXX obsolete but needed for Tkenv
761
                EVCB.messageScheduled(msg);
762
                simulation.insertMsg(msg);
763
                return 0;
764
    }
765
}
766

    
767

    
768
void cSimpleModule::callHandleAsyncMessage(cMessage* msg)
769
{
770
    //
771
    // set execution state
772
    //
773
    executionState = asynchronous;
774

    
775
    //
776
    // set the simtime for this event
777
    //
778
    cThreadPool::setSimTime(msg->getArrivalTime());
779
    // corresponds to setContextModule(mod);
780
    cThreadPool::setContext(this);
781
    cThreadPool::setDefaultOwner(this);
782

    
783
    //
784
    // update meta data
785
    //
786
    prepareHandleMessage(msg);
787

    
788
    //
789
    // call the actual method
790
    //
791
    handleMessage(msg);
792
}
793

    
794

    
795
void cSimpleModule::callHandleMessage(cMessage* msg)
796
{
797
    //
798
    // set execution state
799
    //
800
    executionState = synchronous;
801

    
802
    //
803
    // update meta data
804
    //
805
    prepareHandleMessage(msg);
806

    
807
    //
808
    // call the actual method
809
    //
810
    handleMessage(msg);
811
}
812

    
813

    
814
void cSimpleModule::prepareHandleMessage(cMessage* msg)
815
{
816
    //
817
    // set some internal state
818
    //
819
    t_end = msg->getArrivalTime() + msg->getEventDuration();
820
    now = msg->getArrivalTime();
821
    currentEventPriority = msg->getSchedulingPriority();
822

    
823
    //
824
    // take ownership of the message only after the test above
825
    //
826
    this->take(msg);
827

    
828
    //
829
    // reset message counter
830
    // message 0 is the barriermsg
831
    //
832
    scheduledMessageCount = 1;
833

    
834
    executionOrderId = msg->getExecutionOrderId();
835

    
836
        currentEventType=getEventType(msg);
837
        currentEventType->isCalled();
838

    
839
}
840

    
841
int cSimpleModule::send(cMessage *msg, int gateid)
842
{
843
    return sendDelayed(msg, 0.0, gateid);
844
}
845

    
846

    
847
int cSimpleModule::send(cMessage *msg, const char *gatename, int sn)
848
{
849
    return sendDelayed(msg, 0.0, gatename, sn);
850
}
851

    
852

    
853
int cSimpleModule::send(cMessage *msg, cGate *outputgate)
854
{
855
    return sendDelayed(msg, 0.0, outputgate);
856
}
857

    
858

    
859
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, int gateid)
860
{
861
    cGate *outgate = gate(gateid);
862
    if (outgate==NULL)
863
        throw cRuntimeError("send()/sendDelayed(): module has no gate #%d", gateid);
864

    
865
    return sendDelayed(msg, delay, outgate);
866
}
867

    
868

    
869
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, const char *gatename, int sn)
870
{
871
    cGate *outgate = gate(gatename, sn);
872
    if (outgate==NULL)
873
       throw cRuntimeError(sn<0 ? "send()/sendDelayed(): module has no gate `%s'":
874
                               "send()/sendDelayed(): module has no gate `%s[%d]'",gatename,sn);
875

    
876
    return sendDelayed(msg, delay, outgate);
877
}
878

    
879

    
880
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, cGate *outputgate)
881
{
882
    setMessageMetaData(msg);
883

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

    
930
        if (delay < 0.0)
931
            throw cRuntimeError("sendDelayed(): negative delay %s",delay.str().c_str());
932

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

    
937
        if (delay == 0 && currentEventPriority < msg->getSchedulingPriority()) {
938
            throw cRuntimeError("sendDelayed(): priority of scheduled Event larger than scheduling event and equal starting times => scheduling in the past");
939
        }
940
        //
941
        // set message parameters and send it
942
        //
943
        msg->setSentFrom(this, outputgate->getId(), arrivalTime);
944
        bool keepit = outputgate->deliver(msg, arrivalTime);
945

    
946
                if(currentEventType) currentEventType->newSuccEventInstance(msg,delay);
947

    
948
        if (!keepit)
949
            delete msg;
950

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

    
996
                // set message parameters and send it
997
                simtime_t delayEndTime = simTime()+delay;
998
                msg->setSentFrom(this, outputgate->getId(), delayEndTime);
999
                if (msg->isPacket())
1000
                        ((cPacket *)msg)->setDuration(0);
1001

    
1002
                EVCB.beginSend(msg);
1003
                bool keepit = outputgate->deliver(msg, delayEndTime);
1004
                if(currentEventType) currentEventType->newSuccEventInstance(msg,delay);
1005

    
1006
                if (!keepit)
1007
                {
1008
                        delete msg; //FIXME problem: tell tkenv somehow that msg has been deleted, otherwise animation will crash
1009
                }
1010
                else
1011
                {
1012
                        EVCB.messageSent_OBSOLETE(msg); //FIXME obsolete
1013
                        EVCB.endSend(msg);
1014
                }
1015
                return 0;
1016
    }
1017
}
1018

    
1019

    
1020
int cSimpleModule::sendDirect(cMessage *msg, simtime_t delay, simtime_t duration, cModule *mod, int inputgateid)
1021
{
1022
    cGate *togate = mod->gate(inputgateid);
1023
    if (togate==NULL)
1024
        throw cRuntimeError("sendDirect(): module `%s' has no gate #%d",
1025
                                mod->getFullPath().c_str(), inputgateid);
1026

    
1027
    return sendDirect(msg, delay, duration, togate);
1028
}
1029

    
1030

    
1031
int cSimpleModule::sendDirect(cMessage *msg, simtime_t delay, simtime_t duration, cModule *mod, const char *inputgatename, int sn)
1032
{
1033
    if (!mod)
1034
        throw cRuntimeError("sendDirect(): module ptr is NULL");
1035
    cGate *togate = mod->gate(inputgatename,sn);
1036
    if (togate==NULL)
1037
        throw cRuntimeError(sn<0 ? "sendDirect(): module `%s' has no gate `%s'":
1038
                                "sendDirect(): module `%s' has no gate `%s[%d]'",
1039
                                mod->getFullPath().c_str(), inputgatename, sn);
1040
    return sendDirect(msg, delay, duration, togate);
1041
}
1042

    
1043

    
1044
int cSimpleModule::sendDirect(cMessage *msg, simtime_t delay, simtime_t duration, cGate *togate)
1045
{
1046
    setMessageMetaData(msg);
1047

    
1048
    //
1049
    // check current state
1050
    //
1051
    if (executionState == asynchronous)
1052
    {
1053
        //
1054
        // error checking
1055
        //
1056
        // Note: it is permitted to send to an output gate. It is especially useful
1057
        // with several submodules sending to a single output gate of their parent module.
1058
        if (togate==NULL)
1059
                throw cRuntimeError("sendDirect(): destination gate pointer is NULL");
1060
        if (togate->getPreviousGate())
1061
                throw cRuntimeError("sendDirect(): module must have dedicated gate(s) for receiving via sendDirect()"
1062
                                                                " (\"from\" side of dest. gate `%s' should NOT be connected)",togate->getFullPath().c_str());
1063
        if (msg==NULL)
1064
                throw cRuntimeError("sendDirect(): message pointer is NULL");
1065
        if (msg->getOwner()!=this)
1066
        {
1067
                if (this!=simulation.getContext())
1068
                        throw cRuntimeError("sendDirect() of module (%s)%s called in the context of "
1069
                                                                        "module (%s)%s: method called from the latter module "
1070
                                                                        "lacks Enter_Method() or Enter_Method_Silent()? "
1071
                                                                        "Also, if message to be sent is passed from that module, "
1072
                                                                        "you'll need to call take(msg) after Enter_Method() as well",
1073
                                                                        getClassName(), getFullPath().c_str(),
1074
                                                                        simulation.getContext()->getClassName(),
1075
                                                                        simulation.getContext()->getFullPath().c_str());
1076
                else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
1077
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
1078
                                                                        "currently scheduled as a self-message for this module",
1079
                                                                        msg->getClassName(), msg->getName());
1080
                else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
1081
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
1082
                                                                        "currently scheduled as a self-message for ANOTHER module",
1083
                                                                        msg->getClassName(), msg->getName());
1084
                else if (msg->getOwner()==&simulation.msgQueue)
1085
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
1086
                                                                        "currently in scheduled-events, being underway between two modules",
1087
                                                                        msg->getClassName(), msg->getName());
1088
                else
1089
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, "
1090
                                                                        "it is currently contained/owned by (%s)%s",
1091
                                                                        msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
1092
                                                                        msg->getOwner()->getFullPath().c_str());
1093
        }
1094

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

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

    
1103
        // set message parameters and send it
1104
        msg->setSentFrom(this, -1, now);
1105

    
1106
                if(currentEventType) currentEventType->newSuccEventInstance(msg,delay);
1107

    
1108
        bool keepit = togate->deliver(msg, arrivalTime);
1109
        if (!keepit)
1110
                delete msg;
1111
        return 0;
1112
    }
1113
    else  // sync execution
1114
    {
1115
            return cSimpleModule::sendDirect(msg, delay, duration, togate);
1116
    }
1117
}