Statistics
| Branch: | Revision:

root / src / sim / csimplemodule.cc @ 08285dff

History | View | Annotate | Download (37.4 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::setMessageMetaData(cMessage* msg)
607
{
608
    msg->setEventDuration(SimTime::simTimeUninitialized);
609
    msg->setSchedulingOrderId(scheduledMessageCount++);
610
    msg->setParentExecutionOrderId(executionOrderId);
611
    msg->setParentStartTime(now);
612
}
613

    
614
int cSimpleModule::scheduleAt(simtime_t t, cMessage *msg)
615
{
616
    setMessageMetaData(msg);
617

    
618
    //
619
    // check current state
620
    //
621
    if (executionState == asynchronous)
622
    {
623
        if (msg==NULL)
624
                        throw cRuntimeError("scheduleAt(): message pointer is NULL");
625
        if (t < t_end)
626
                throw cRuntimeError("scheduleAt(): event cannot be scheduled at "
627
                                "%s because this precedes the end of the processing duration "
628
                                "at %s of the scheduling event.",
629
                                t.str().c_str(), t_end.str().c_str());
630

    
631
        if (t == now && currentEventPriority < msg->getSchedulingPriority()) {
632
            throw cRuntimeError("scheduleAt(): priority of scheduled Event larger than scheduling event and equal starting times => scheduling in the past");
633
        }
634
        // insert this message in the FES. When the scheduler arrives at this
635
        // message it will trigger the processing of the associated task
636
        msg->setSentFrom(this, -1, t_end);
637
        msg->setArrival(this, -1, t);
638

    
639
        simulation.msgQueue.insert(msg);
640
        return 0;
641
    }
642
    else // synch execution
643
    {
644
                if (msg==NULL)
645
                        throw cRuntimeError("scheduleAt(): message pointer is NULL");
646
                if (t<simTime())
647
                        throw cRuntimeError(eBACKSCHED, msg->getClassName(), msg->getName(), SIMTIME_DBL(t));
648
                if (msg->getOwner()!=this)
649
                {
650
                        if (this!=simulation.getContextModule())
651
                                throw cRuntimeError("scheduleAt() of module (%s)%s called in the context of "
652
                                                "module (%s)%s: method called from the latter module "
653
                                                "lacks Enter_Method() or Enter_Method_Silent()?",
654
                                                getClassName(), getFullPath().c_str(),
655
                                                simulation.getContextModule()->getClassName(),
656
                                                simulation.getContextModule()->getFullPath().c_str());
657
                        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
658
                                throw cRuntimeError("scheduleAt(): message (%s)%s is currently scheduled, "
659
                                                "use cancelEvent() before rescheduling",
660
                                                msg->getClassName(), msg->getName());
661
                        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
662
                                throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, it is "
663
                                                "currently scheduled as self-message for ANOTHER module",
664
                                                msg->getClassName(), msg->getName());
665

    
666
                        else if (msg->getOwner()==&simulation.msgQueue)
667
                                throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, it is "
668
                                                "currently in scheduled-events, being underway between two modules",
669
                                                msg->getClassName(), msg->getName());
670
                        else
671
                                throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, "
672
                                                "it is currently contained/owned by (%s)%s",
673
                                                msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
674
                                                msg->getOwner()->getFullPath().c_str());
675
                }
676

    
677
                // set message parameters and schedule it
678
                msg->setSentFrom(this, -1, simTime());
679
                msg->setArrival(this, -1, t);
680
                EVCB.messageSent_OBSOLETE( msg ); //XXX obsolete but needed for Tkenv
681
                EVCB.messageScheduled(msg);
682
                simulation.insertMsg(msg);
683
                return 0;
684
    }
685
}
686

    
687

    
688
void cSimpleModule::callHandleAsyncMessage(cMessage* msg)
689
{
690
    //
691
    // set execution state
692
    //
693
    executionState = asynchronous;
694

    
695
    //
696
    // set the simtime for this event
697
    //
698
    cThreadPool::setSimTime(msg->getArrivalTime());
699
    // corresponds to setContextModule(mod);
700
    cThreadPool::setContext(this);
701
    cThreadPool::setDefaultOwner(this);
702

    
703
    //
704
    // update meta data
705
    //
706
    prepareHandleMessage(msg);
707

    
708
    //
709
    // call the actual method
710
    //
711
    handleMessage(msg);
712
}
713

    
714

    
715
void cSimpleModule::callHandleMessage(cMessage* msg)
716
{
717
    //
718
    // set execution state
719
    //
720
    executionState = synchronous;
721

    
722
    //
723
    // update meta data
724
    //
725
    prepareHandleMessage(msg);
726

    
727
    //
728
    // call the actual method
729
    //
730
    handleMessage(msg);
731
}
732

    
733

    
734
void cSimpleModule::prepareHandleMessage(cMessage* msg)
735
{
736
    //
737
    // set some internal state
738
    //
739
    t_end = msg->getArrivalTime() + msg->getEventDuration();
740
    now = msg->getArrivalTime();
741
    currentEventPriority = msg->getSchedulingPriority();
742

    
743
    //
744
    // take ownership of the message only after the test above
745
    //
746
    this->take(msg);
747

    
748
    //
749
    // reset message counter
750
    // message 0 is the barriermsg
751
    //
752
    scheduledMessageCount = 1;
753

    
754
    executionOrderId = msg->getExecutionOrderId();
755

    
756
}
757

    
758
int cSimpleModule::send(cMessage *msg, int gateid)
759
{
760
    return sendDelayed(msg, 0.0, gateid);
761
}
762

    
763

    
764
int cSimpleModule::send(cMessage *msg, const char *gatename, int sn)
765
{
766
    return sendDelayed(msg, 0.0, gatename, sn);
767
}
768

    
769

    
770
int cSimpleModule::send(cMessage *msg, cGate *outputgate)
771
{
772
    return sendDelayed(msg, 0.0, outputgate);
773
}
774

    
775

    
776
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, int gateid)
777
{
778
    cGate *outgate = gate(gateid);
779
    if (outgate==NULL)
780
        throw cRuntimeError("send()/sendDelayed(): module has no gate #%d", gateid);
781

    
782
    return sendDelayed(msg, delay, outgate);
783
}
784

    
785

    
786
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, const char *gatename, int sn)
787
{
788
    cGate *outgate = gate(gatename, sn);
789
    if (outgate==NULL)
790
       throw cRuntimeError(sn<0 ? "send()/sendDelayed(): module has no gate `%s'":
791
                               "send()/sendDelayed(): module has no gate `%s[%d]'",gatename,sn);
792

    
793
    return sendDelayed(msg, delay, outgate);
794
}
795

    
796

    
797
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, cGate *outputgate)
798
{
799
    setMessageMetaData(msg);
800

    
801
    //
802
    // check current state
803
    //
804
    if (executionState == asynchronous)
805
    {
806
        //
807
        // error checking, omit ownership checks if it breaks
808
        //
809
        if (outputgate==NULL)
810
            throw cRuntimeError("send()/sendDelayed(): gate pointer is NULL");
811
        if (outputgate->getType()=='I')
812
            throw cRuntimeError("send()/sendDelayed(): cannot send via an input gate (`%s')",outputgate->getName());
813
        if (!outputgate->getNextGate())  // NOTE: without this error check, msg would become self-message
814
            throw cRuntimeError("send()/sendDelayed(): gate `%s' not connected",outputgate->getFullName());
815
        if (msg==NULL)
816
            throw cRuntimeError("send()/sendDelayed(): message pointer is NULL");
817
        if (msg->getOwner()!=this)
818
        {
819
            if (this!=simulation.getContext())
820
                throw cRuntimeError("send()/sendDelayed() of module (%s)%s called in the context of "
821
                                        "module (%s)%s: method called from the latter module "
822
                                        "lacks Enter_Method() or Enter_Method_Silent()? "
823
                                        "Also, if message to be sent is passed from that module, "
824
                                        "you'll need to call take(msg) after Enter_Method() as well",
825
                                        getClassName(), getFullPath().c_str(),
826
                                        simulation.getContext()->getClassName(),
827
                                        simulation.getContext()->getFullPath().c_str());
828
            else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
829
                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
830
                                        "currently scheduled as a self-message for this module",
831
                                        msg->getClassName(), msg->getName());
832
            else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
833
                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
834
                                        "currently scheduled as a self-message for ANOTHER module",
835
                                        msg->getClassName(), msg->getName());
836
            else if (msg->getOwner()==&simulation.msgQueue)
837
                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
838
                                        "currently in scheduled-events, being underway between two modules",
839
                                        msg->getClassName(), msg->getName());
840
            else
841
                throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, "
842
                                        "it is currently contained/owned by (%s)%s",
843
                                        msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
844
                                        msg->getOwner()->getFullPath().c_str());
845
        }
846

    
847
        if (delay < 0.0)
848
            throw cRuntimeError("sendDelayed(): negative delay %s",delay.str().c_str());
849

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

    
854
        if (delay == 0 && currentEventPriority < msg->getSchedulingPriority()) {
855
            throw cRuntimeError("sendDelayed(): priority of scheduled Event larger than scheduling event and equal starting times => scheduling in the past");
856
        }
857
        //
858
        // set message parameters and send it
859
        //
860
        msg->setSentFrom(this, outputgate->getId(), arrivalTime);
861
        bool keepit = outputgate->deliver(msg, arrivalTime);
862
        if (!keepit)
863
            delete msg;
864

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

    
910
                // set message parameters and send it
911
                simtime_t delayEndTime = simTime()+delay;
912
                msg->setSentFrom(this, outputgate->getId(), delayEndTime);
913
                if (msg->isPacket())
914
                        ((cPacket *)msg)->setDuration(0);
915

    
916
                EVCB.beginSend(msg);
917
                bool keepit = outputgate->deliver(msg, delayEndTime);
918
                if (!keepit)
919
                {
920
                        delete msg; //FIXME problem: tell tkenv somehow that msg has been deleted, otherwise animation will crash
921
                }
922
                else
923
                {
924
                        EVCB.messageSent_OBSOLETE(msg); //FIXME obsolete
925
                        EVCB.endSend(msg);
926
                }
927
                return 0;
928
    }
929
}
930

    
931

    
932
int cSimpleModule::sendDirect(cMessage *msg, simtime_t delay, simtime_t duration, cModule *mod, int inputgateid)
933
{
934
    cGate *togate = mod->gate(inputgateid);
935
    if (togate==NULL)
936
        throw cRuntimeError("sendDirect(): module `%s' has no gate #%d",
937
                                mod->getFullPath().c_str(), inputgateid);
938

    
939
    return sendDirect(msg, delay, duration, togate);
940
}
941

    
942

    
943
int cSimpleModule::sendDirect(cMessage *msg, simtime_t delay, simtime_t duration, cModule *mod, const char *inputgatename, int sn)
944
{
945
    if (!mod)
946
        throw cRuntimeError("sendDirect(): module ptr is NULL");
947
    cGate *togate = mod->gate(inputgatename,sn);
948
    if (togate==NULL)
949
        throw cRuntimeError(sn<0 ? "sendDirect(): module `%s' has no gate `%s'":
950
                                "sendDirect(): module `%s' has no gate `%s[%d]'",
951
                                mod->getFullPath().c_str(), inputgatename, sn);
952
    return sendDirect(msg, delay, duration, togate);
953
}
954

    
955

    
956
int cSimpleModule::sendDirect(cMessage *msg, simtime_t delay, simtime_t duration, cGate *togate)
957
{
958
    setMessageMetaData(msg);
959

    
960
    //
961
    // check current state
962
    //
963
    if (executionState == asynchronous)
964
    {
965
        //
966
        // error checking
967
        //
968
        // Note: it is permitted to send to an output gate. It is especially useful
969
        // with several submodules sending to a single output gate of their parent module.
970
        if (togate==NULL)
971
                throw cRuntimeError("sendDirect(): destination gate pointer is NULL");
972
        if (togate->getPreviousGate())
973
                throw cRuntimeError("sendDirect(): module must have dedicated gate(s) for receiving via sendDirect()"
974
                                                                " (\"from\" side of dest. gate `%s' should NOT be connected)",togate->getFullPath().c_str());
975
        if (msg==NULL)
976
                throw cRuntimeError("sendDirect(): message pointer is NULL");
977
        if (msg->getOwner()!=this)
978
        {
979
                if (this!=simulation.getContext())
980
                        throw cRuntimeError("sendDirect() of module (%s)%s called in the context of "
981
                                                                        "module (%s)%s: method called from the latter module "
982
                                                                        "lacks Enter_Method() or Enter_Method_Silent()? "
983
                                                                        "Also, if message to be sent is passed from that module, "
984
                                                                        "you'll need to call take(msg) after Enter_Method() as well",
985
                                                                        getClassName(), getFullPath().c_str(),
986
                                                                        simulation.getContext()->getClassName(),
987
                                                                        simulation.getContext()->getFullPath().c_str());
988
                else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
989
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
990
                                                                        "currently scheduled as a self-message for this module",
991
                                                                        msg->getClassName(), msg->getName());
992
                else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
993
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
994
                                                                        "currently scheduled as a self-message for ANOTHER module",
995
                                                                        msg->getClassName(), msg->getName());
996
                else if (msg->getOwner()==&simulation.msgQueue)
997
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
998
                                                                        "currently in scheduled-events, being underway between two modules",
999
                                                                        msg->getClassName(), msg->getName());
1000
                else
1001
                        throw cRuntimeError("sendDirect(): cannot send message (%s)%s, "
1002
                                                                        "it is currently contained/owned by (%s)%s",
1003
                                                                        msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
1004
                                                                        msg->getOwner()->getFullPath().c_str());
1005
        }
1006

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

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

    
1015
        // set message parameters and send it
1016
        msg->setSentFrom(this, -1, now);
1017

    
1018
        bool keepit = togate->deliver(msg, arrivalTime);
1019
        if (!keepit)
1020
                delete msg;
1021
        return 0;
1022
    }
1023
    else  // sync execution
1024
    {
1025
            return cSimpleModule::sendDirect(msg, delay, duration, togate);
1026
    }
1027
}