Statistics
| Branch: | Revision:

root / src / sim / csimplemodule.cc @ e26d3d25

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

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

    
22
#include <assert.h>
23
#include <stdio.h>           // sprintf
24
#include <string.h>          // strcpy
25
#include <exception>
26
#include "csimplemodule.h"
27
#include "cgate.h"
28
#include "cmessage.h"
29
#include "ccoroutine.h"
30
#include "csimulation.h"
31
#include "carray.h"
32
#include "cmsgpar.h"
33
#include "cqueue.h"
34
#include "cenvir.h"
35
#include "cexception.h"
36
#include "commonutil.h"
37

    
38
USING_NAMESPACE
39

    
40

    
41
bool cSimpleModule::stack_cleanup_requested;
42
cSimpleModule *cSimpleModule::after_cleanup_transfer_to;
43

    
44

    
45
void cSimpleModule::activate(void *p)
46
{
47
    cSimpleModule *mod = (cSimpleModule *)p;
48

    
49
    if (stack_cleanup_requested)
50
    {
51
        // module has just been created, but already deleted
52
        mod->setFlag(FL_ISTERMINATED, true);
53
        mod->setFlag(FL_STACKALREADYUNWOUND, true);
54
        if (after_cleanup_transfer_to)
55
            simulation.transferTo(after_cleanup_transfer_to);
56
        else
57
            simulation.transferToMain();
58
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
59
        abort();
60
    }
61

    
62
    // The starter message should be the same as the timeoutmsg member of
63
    // cSimpleModule. If not, then something is wrong...
64
    cMessage *starter = simulation.msg_for_activity;
65
    if (starter!=mod->timeoutmsg)
66
    {
67
        // hand exception to cSimulation::transferTo() and switch back
68
        mod->setFlag(FL_ISTERMINATED, true);
69
        mod->setFlag(FL_STACKALREADYUNWOUND, true);
70
        simulation.exception = new cRuntimeError("scheduleStart() should have been called for dynamically created module `%s'", mod->getFullPath().c_str());
71
        simulation.transferToMain();
72
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
73
        abort();
74
    }
75

    
76
    // rename message
77
    starter->setKind(MK_TIMEOUT);
78
    char buf[24];
79
    sprintf(buf,"timeout-%d", mod->getId());
80
    starter->setName(buf);
81

    
82
    cException *exception = NULL;
83
    try
84
    {
85
        //
86
        // call activity(). At this point, initialize() has already been called
87
        // from cSimulation::startRun(), or manually in the case of dynamically
88
        // created modules.
89
        //
90
        mod->activity();
91
    }
92
    catch (cRuntimeError *e) // compat
93
    {
94
        // IMPORTANT: No transferTo() in catch blocks! See Note 2 below.
95
        exception = new cRuntimeError("%s [NOTE: exception was thrown by pointer. "
96
                                      "In OMNeT++ 4.0+, exceptions have to be thrown by value. "
97
                                      "Please delete `new' from `throw new ...' in the code]",
98
                                      e->what());
99
        delete e;
100
    }
101
    catch (cException *e) // compat
102
    {
103
        // IMPORTANT: No transferTo() in catch blocks! See Note 2 below.
104
        exception = new cRuntimeError("%s [NOTE: exception was thrown with pointer. "
105
                                      "In OMNeT++ 4.0+, exceptions have to be thrown by value. "
106
                                      "Please delete `new' from `throw new ...' in the code]",
107
                                      e->what());
108
        delete e;
109
    }
110
    catch (cException& e)
111
    {
112
        // IMPORTANT: No transferTo() in catch blocks! See Note 2 below.
113
        exception = e.dup();
114
    }
115
    catch (std::exception& e)
116
    {
117
        // IMPORTANT: No transferTo() in catch blocks! See Note 2 below.
118
        // We need to wrap std::exception into cRuntimeError.
119
        exception = new cRuntimeError("%s: %s", opp_typename(typeid(e)), e.what());
120
    }
121

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

    
128
    // When we get here, the module is already terminated. No further cStackCleanupException
129
    // will need to be thrown, as the stack has has already been unwound by an exception
130
    // or by having returned from activity() normally.
131
    mod->setFlag(FL_ISTERMINATED, true);
132
    mod->setFlag(FL_STACKALREADYUNWOUND, true);
133

    
134
    if (!exception)
135
    {
136
        // Module function terminated normally, without exception. Just mark
137
        // the module as finished, and transfer to the main coroutine (fiber).
138
        simulation.transferToMain();
139
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
140
        abort();
141
    }
142
    else if (dynamic_cast<cStackCleanupException *>(exception))
143
    {
144
        // A cStackCleanupException exception has been thrown on purpose,
145
        // to force stack unwinding in the coroutine (fiber) function, activity().
146
        // Just transfer back to whoever forced the stack cleanup (the main coroutine
147
        // or some other simple module) and nothing else to do.
148
        delete exception;
149
        if (after_cleanup_transfer_to)
150
            simulation.transferTo(after_cleanup_transfer_to);
151
        else
152
            simulation.transferToMain();
153
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
154
        abort();
155
    }
156
    else
157
    {
158
        // Some exception (likely cRuntimeError, cTerminationException, or
159
        // cDeleteModuleException) occurred within the activity() function.
160
        // Pass this exception to the main coroutine so that it can be displayed as
161
        // an error dialog or the like.
162
        simulation.exception = exception;
163
        simulation.transferToMain();
164
        fprintf(stderr, "INTERNAL ERROR: switch to the fiber of a module already terminated");
165
        abort();
166
    }
167
}
168

    
169
// legacy constructor, only for backwards compatiblity; first two args are unused
170
cSimpleModule::cSimpleModule(const char *, cModule *, unsigned stksize)
171
{
172
    coroutine = NULL;
173
    setFlag(FL_USESACTIVITY, stksize!=0);
174
    setFlag(FL_ISTERMINATED, false);
175
    setFlag(FL_STACKALREADYUNWOUND, false);
176

    
177
    // for an activity() module, timeoutmsg will be created in scheduleStart()
178
    // which must always be called
179
    timeoutmsg = NULL;
180

    
181
    if (usesActivity())
182
    {
183
       // setup coroutine, allocate stack for it
184
       coroutine = new cCoroutine;
185
       if (!coroutine->setup(cSimpleModule::activate, this, stksize+ev.getExtraStackForEnvir()))
186
           throw cRuntimeError("Cannot create coroutine with %d+%d bytes of stack space for module `%s' -- "
187
                               "see Manual for hints on how to increase the number of coroutines that can be created, "
188
                               "or rewrite modules to use handleMessage() instead of activity()",
189
                               stksize,ev.getExtraStackForEnvir(),getFullPath().c_str());
190
    }
191
}
192

    
193
cSimpleModule::cSimpleModule(unsigned stksize)
194
{
195
    coroutine = NULL;
196
    setFlag(FL_USESACTIVITY, stksize!=0);
197
    setFlag(FL_ISTERMINATED, false);
198
    setFlag(FL_STACKALREADYUNWOUND, false);
199

    
200
    // for an activity() module, timeoutmsg will be created in scheduleStart()
201
    // which must always be called
202
    timeoutmsg = NULL;
203

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

    
216
cSimpleModule::~cSimpleModule()
217
{
218
    if (simulation.getContext()==this)
219
        throw cRuntimeError(this, "cannot delete itself, only via deleteModule()");
220

    
221
    if (usesActivity())
222
    {
223
        // clean up user's objects on coroutine stack by forcing an exception inside the coroutine
224
        if ((flags&FL_STACKALREADYUNWOUND)==0)
225
        {
226
            //FIXME: check this is OK for brand new modules too (no transferTo() yet)
227
            stack_cleanup_requested = true;
228
            after_cleanup_transfer_to = simulation.getActivityModule();
229
            ASSERT(!after_cleanup_transfer_to || after_cleanup_transfer_to->usesActivity());
230
            simulation.transferTo(this);
231
            stack_cleanup_requested = false;
232
        }
233

    
234
        // delete timeoutmsg if not currently scheduled (then it'll be deleted by message queue)
235
        if (timeoutmsg && !timeoutmsg->isScheduled())
236
            delete timeoutmsg;
237

    
238
        // deallocate coroutine
239
        delete coroutine;
240
    }
241

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

    
246
    //for (cMessageHeap::Iterator iter(simulation.msgQueue); !iter.end(); iter++)
247
    //{
248
    //    cMessage *msg = iter();
249
    //    if (msg->getArrivalModuleId() == getId())
250
    //        delete simulation.msgQueue.get( msg );
251
    //}
252
}
253

    
254
std::string cSimpleModule::info() const
255
{
256
    std::stringstream out;
257
    out << "id=" << getId();
258
    return out.str();
259
}
260

    
261
void cSimpleModule::forEachChild(cVisitor *v)
262
{
263
    cModule::forEachChild(v);
264
}
265

    
266
void cSimpleModule::setId(int n)
267
{
268
    cModule::setId(n);
269

    
270
    if (timeoutmsg)
271
        timeoutmsg->setArrival(this, n);
272
}
273

    
274
void cSimpleModule::halt()
275
{
276
    if (!usesActivity())
277
        throw cRuntimeError("halt() can only be invoked from activity()-based simple modules");
278

    
279
    setFlag(FL_ISTERMINATED, true);
280
    simulation.transferToMain();
281
    assert(stack_cleanup_requested);
282
    throw cStackCleanupException();
283
}
284

    
285
#define BUFLEN 512
286

    
287
void cSimpleModule::error(const char *fmt...) const
288
{
289
    char buf[BUFLEN];
290
    VSNPRINTF(buf, BUFLEN, fmt);
291
    throw cRuntimeError(eUSER, buf);
292
}
293

    
294
#undef BUFLEN
295

    
296
//---------
297

    
298
void cSimpleModule::scheduleStart(simtime_t t)
299
{
300
    // Note: Simple modules using handleMessage() don't need starter
301
    // messages, but nevertheless, we still define scheduleStart()
302
    // for them. The benefit is that this way code that creates simple
303
    // modules dynamically doesn't have to know whether the module is
304
    // using activity() or handleMessage(); the same code (which
305
    // contains a call to scheduleStart()) can be used for both.
306
    if (usesActivity())
307
    {
308
        if (timeoutmsg!=NULL)
309
            throw cRuntimeError("scheduleStart(): module `%s' already started",getFullPath().c_str());
310

    
311
        Enter_Method_Silent("scheduleStart()");
312

    
313
        // create timeoutmsg, used as internal timeout message
314
        char buf[24];
315
        sprintf(buf,"starter-%d", getId());
316
        timeoutmsg = new cMessage(buf,MK_STARTER);
317

    
318
        // initialize message fields
319
        timeoutmsg->setSentFrom(NULL, -1, 0);
320
        timeoutmsg->setArrival(this, -1, t);
321

    
322
        // use timeoutmsg as the activation message; insert it into the FES
323
        EVCB.messageScheduled(timeoutmsg);
324
        simulation.insertMsg(timeoutmsg);
325
    }
326

    
327
    for (SubmoduleIterator submod(this); !submod.end(); submod++)
328
        submod()->scheduleStart(t);
329
}
330

    
331
void cSimpleModule::deleteModule()
332
{
333
    // if a coroutine wants to delete itself, that has to be handled from
334
    // another coroutine (i.e. from main). Control is passed there by
335
    // throwing an exception that gets transferred to the main coroutine
336
    // by activate(), and handled in cSimulation::transferTo().
337
    // execution must be immediately suspended in activity() and handleMessage()
338
    // if deleteModule() is called (i.e. we are deleting ourselves)
339
    // the exception will be handled in the doOneEvent loop and the module
340
    // will be deleted from the globalContext
341
    if (simulation.getContextModule()==this)
342
        throw cDeleteModuleException();
343

    
344
    // else fall back to the base class implementation
345
    cModule::deleteModule();
346
}
347

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

    
350
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, const char *gateName, int gateIndex)
351
{
352
    cGate *outgate;
353
    TRY(outgate = gate(gateName,gateIndex), "send()/sendDelayed()");
354
    return sendDelayed(msg, delay, outgate);
355
}
356

    
357
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, int gateId)
358
{
359
    cGate *outgate;
360
    TRY(outgate = gate(gateId), "send()/sendDelayed()");
361
    return sendDelayed(msg, delay, outgate);
362
}
363

    
364
int cSimpleModule::sendDelayed(cMessage *msg, simtime_t delay, cGate *outgate)
365
{
366
    // error checking:
367
    if (outgate==NULL)
368
       throw cRuntimeError("send()/sendDelayed(): gate pointer is NULL");
369
    if (outgate->getType()==cGate::INPUT)
370
       throw cRuntimeError("send()/sendDelayed(): cannot send via an input gate (`%s')", outgate->getFullName());
371
    if (!outgate->getNextGate())  // NOTE: without this error check, msg would become self-message
372
       throw cRuntimeError("send()/sendDelayed(): gate `%s' not connected", outgate->getFullName());
373
    if (msg==NULL)
374
        throw cRuntimeError("send()/sendDelayed(): message pointer is NULL");
375
    if (msg->getOwner()!=this)
376
    {
377
        if (this!=simulation.getContextModule())
378
            throw cRuntimeError("send()/sendDelayed() of module (%s)%s called in the context of "
379
                                "module (%s)%s: method called from the latter module "
380
                                "lacks Enter_Method() or Enter_Method_Silent()? "
381
                                "Also, if message to be sent is passed from that module, "
382
                                "you'll need to call take(msg) after Enter_Method() as well",
383
                                getClassName(), getFullPath().c_str(),
384
                                simulation.getContextModule()->getClassName(),
385
                                simulation.getContextModule()->getFullPath().c_str());
386
        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
387
            throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
388
                                "currently scheduled as a self-message for this module",
389
                                msg->getClassName(), msg->getName());
390
        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
391
            throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
392
                                "currently scheduled as a self-message for ANOTHER module",
393
                                msg->getClassName(), msg->getName());
394
        else if (msg->getOwner()==&simulation.msgQueue)
395
            throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, it is "
396
                                "currently in scheduled-events, being underway between two modules",
397
                                msg->getClassName(), msg->getName());
398
        else
399
            throw cRuntimeError("send()/sendDelayed(): cannot send message (%s)%s, "
400
                                "it is currently contained/owned by (%s)%s",
401
                                msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
402
                                msg->getOwner()->getFullPath().c_str());
403
    }
404
    if (delay < 0)
405
        throw cRuntimeError("sendDelayed(): negative delay %s", SIMTIME_STR(delay));
406

    
407
    // set message parameters and send it
408
    simtime_t delayEndTime = simTime()+delay;
409
    msg->setSentFrom(this, outgate->getId(), delayEndTime);
410
    if (msg->isPacket())
411
        ((cPacket *)msg)->setDuration(0);
412

    
413
    EVCB.beginSend(msg);
414
    bool keepit = outgate->deliver(msg, delayEndTime);
415
    if (!keepit)
416
    {
417
        delete msg; //FIXME problem: tell tkenv somehow that msg has been deleted, otherwise animation will crash
418
    }
419
    else
420
    {
421
        EVCB.messageSent_OBSOLETE(msg); //FIXME obsolete
422
        EVCB.endSend(msg);
423
    }
424
    return 0;
425
}
426

    
427
int cSimpleModule::sendDirect(cMessage *msg, cModule *mod, const char *gateName, int gateIndex)
428
{
429
    return sendDirect(msg, SIMTIME_ZERO, SIMTIME_ZERO, mod, gateName, gateIndex);
430
}
431

    
432
int cSimpleModule::sendDirect(cMessage *msg, cModule *mod, int gateId)
433
{
434
    return sendDirect(msg, SIMTIME_ZERO, SIMTIME_ZERO, mod, gateId);
435
}
436

    
437
int cSimpleModule::sendDirect(cMessage *msg, cGate *togate)
438
{
439
    return sendDirect(msg, SIMTIME_ZERO, SIMTIME_ZERO, togate);
440
}
441

    
442
int cSimpleModule::sendDirect(cMessage *msg, simtime_t propdelay, simtime_t duration,
443
                              cModule *mod, const char *gateName, int gateIndex)
444
{
445
    if (!mod)
446
        throw cRuntimeError("sendDirect(): destination module pointer is NULL");
447
    cGate *togate;
448
    TRY(togate = mod->gate(gateName, gateIndex), "sendDirect()");
449
    return sendDirect(msg, propdelay, duration, togate);
450
}
451

    
452
int cSimpleModule::sendDirect(cMessage *msg, simtime_t propdelay, simtime_t duration, cModule *mod, int gateId)
453
{
454
    if (!mod)
455
        throw cRuntimeError("sendDirect(): destination module pointer is NULL");
456
    cGate *togate;
457
    TRY(togate = mod->gate(gateId), "sendDirect()");
458
    return sendDirect(msg, propdelay, duration, togate);
459
}
460

    
461

    
462
int cSimpleModule::sendDirect(cMessage *msg, simtime_t propdelay, simtime_t duration, cGate *togate)
463
{
464
    // Note: it is permitted to send to an output gate. It is especially useful
465
    // with several submodules sending to a single output gate of their parent module.
466
    if (togate==NULL)
467
        throw cRuntimeError("sendDirect(): destination gate pointer is NULL");
468
    if (togate->getPreviousGate())
469
        throw cRuntimeError("sendDirect(): module must have dedicated gate(s) for receiving via sendDirect()"
470
                            " (\"from\" side of dest. gate `%s' should NOT be connected)",togate->getFullPath().c_str());
471
    if (propdelay<0 || duration<0)
472
        throw cRuntimeError("sendDirect(): the propagation and duration parameters cannot be negative");
473
    if (msg==NULL)
474
        throw cRuntimeError("sendDirect(): message pointer is NULL");
475
    if (msg->getOwner()!=this)
476
    {
477
        // try to give a meaningful error message
478
        if (this!=simulation.getContextModule())
479
            throw cRuntimeError("sendDirect() of module (%s)%s called in the context of "
480
                                "module (%s)%s: method called from the latter module "
481
                                "lacks Enter_Method() or Enter_Method_Silent()? "
482
                                "Also, if message to be sent is passed from that module, "
483
                                "you'll need to call take(msg) after Enter_Method() as well",
484
                                getClassName(), getFullPath().c_str(),
485
                                simulation.getContextModule()->getClassName(),
486
                                simulation.getContextModule()->getFullPath().c_str());
487
        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
488
            throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
489
                                "currently scheduled as a self-message for this module",
490
                                msg->getClassName(), msg->getName());
491
        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
492
            throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
493
                                "currently scheduled as a self-message for ANOTHER module",
494
                                msg->getClassName(), msg->getName());
495
        else if (msg->getOwner()==&simulation.msgQueue)
496
            throw cRuntimeError("sendDirect(): cannot send message (%s)%s, it is "
497
                                "currently in scheduled-events, being underway between two modules",
498
                                msg->getClassName(), msg->getName());
499
        else
500
            throw cRuntimeError("sendDirect(): cannot send message (%s)%s, "
501
                                "it is currently contained/owned by (%s)%s",
502
                                msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
503
                                msg->getOwner()->getFullPath().c_str());
504
    }
505

    
506
    // set message parameters and send it
507
    msg->setSentFrom(this, -1, simTime());
508

    
509
    EVCB.beginSend(msg);
510
    if (msg->isPacket())
511
        ((cPacket *)msg)->setDuration(duration);
512
    else if (duration!=SIMTIME_ZERO)
513
        throw cRuntimeError("sendDirect(): cannot send non-packet message (%s)%s when nonzero duration is specified",
514
                            msg->getClassName(), msg->getName());
515
    EVCB.messageSendDirect(msg, togate, propdelay, duration);
516
    bool keepit = togate->deliver(msg, simTime() + propdelay);
517
    if (!keepit)
518
    {
519
        delete msg; //FIXME problem: tell tkenv somehow that msg has been deleted, otherwise animation will crash
520
    }
521
    else
522
    {
523
        EVCB.messageSent_OBSOLETE(msg, togate); //FIXME obsolete
524
        EVCB.endSend(msg);
525
    }
526
    return 0;
527
}
528

    
529
int cSimpleModule::scheduleAt(simtime_t t, cMessage *msg)
530
{
531
    if (msg==NULL)
532
        throw cRuntimeError("scheduleAt(): message pointer is NULL");
533
    if (t<simTime())
534
        throw cRuntimeError(eBACKSCHED, msg->getClassName(), msg->getName(), SIMTIME_DBL(t));
535
    if (msg->getOwner()!=this)
536
    {
537
        if (this!=simulation.getContextModule())
538
            throw cRuntimeError("scheduleAt() of module (%s)%s called in the context of "
539
                                "module (%s)%s: method called from the latter module "
540
                                "lacks Enter_Method() or Enter_Method_Silent()?",
541
                                getClassName(), getFullPath().c_str(),
542
                                simulation.getContextModule()->getClassName(),
543
                                simulation.getContextModule()->getFullPath().c_str());
544
        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage() && msg->getArrivalModuleId()==getId())
545
            throw cRuntimeError("scheduleAt(): message (%s)%s is currently scheduled, "
546
                                "use cancelEvent() before rescheduling",
547
                                msg->getClassName(), msg->getName());
548
        else if (msg->getOwner()==&simulation.msgQueue && msg->isSelfMessage())
549
            throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, it is "
550
                                "currently scheduled as self-message for ANOTHER module",
551
                                msg->getClassName(), msg->getName());
552

    
553
         else if (msg->getOwner()==&simulation.msgQueue)
554
            throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, it is "
555
                                "currently in scheduled-events, being underway between two modules",
556
                                msg->getClassName(), msg->getName());
557
        else
558
            throw cRuntimeError("scheduleAt(): cannot schedule message (%s)%s, "
559
                                "it is currently contained/owned by (%s)%s",
560
                                msg->getClassName(), msg->getName(), msg->getOwner()->getClassName(),
561
                                msg->getOwner()->getFullPath().c_str());
562
    }
563

    
564
    // set message parameters and schedule it
565
    msg->setSentFrom(this, -1, simTime());
566
    msg->setArrival(this, -1, t);
567
    EVCB.messageSent_OBSOLETE( msg ); //XXX obsolete but needed for Tkenv
568
    EVCB.messageScheduled(msg);
569
    simulation.insertMsg(msg);
570
    return 0;
571
}
572

    
573
cMessage *cSimpleModule::cancelEvent(cMessage *msg)
574
{
575
    // make sure we really have the message and it is scheduled
576
    if (msg==NULL)
577
        throw cRuntimeError("cancelEvent(): message pointer is NULL");
578

    
579
    // now remove it from future events and return pointer
580
    if (msg->isScheduled())
581
    {
582
        if (!msg->isSelfMessage())
583
            throw cRuntimeError("cancelEvent(): message (%s)%s is not a self-message", msg->getClassName(), msg->getFullName());
584
        simulation.msgQueue.remove(msg);
585
        EVCB.messageCancelled(msg);
586
        msg->setPreviousEventNumber(simulation.getEventNumber());
587
    }
588

    
589
    return msg;
590
}
591

    
592
void cSimpleModule::cancelAndDelete(cMessage *msg)
593
{
594
    if (msg)
595
        delete cancelEvent(msg);
596
}
597

    
598
void cSimpleModule::arrived(cMessage *msg, cGate *ongate, simtime_t t)
599
{
600
    if (isTerminated())
601
        throw cRuntimeError(eMODFIN, getFullPath().c_str());
602
    if (t < simTime())
603
        throw cRuntimeError("Causality violation: message `%s' arrival time %s at module `%s' "
604
                            "is earlier than current simulation time",
605
                            msg->getName(), SIMTIME_STR(t), getFullPath().c_str());
606
    msg->setArrival(this, ongate->getId());
607
    bool isStart = ongate->getDeliverOnReceptionStart();
608
    if (msg->isPacket()) {
609
        cPacket *pkt = (cPacket *)msg;
610
        pkt->setReceptionStart(isStart);
611
        pkt->setArrivalTime(isStart ? t : t + pkt->getDuration());
612
    }
613
    else {
614
        msg->setArrivalTime(t);
615
    }
616
    simulation.insertMsg(msg);
617
}
618

    
619
void cSimpleModule::wait(simtime_t t)
620
{
621
    if (!usesActivity())
622
        throw cRuntimeError(eNORECV);
623
    if (t<0)
624
        throw cRuntimeError(eNEGTIME);
625

    
626
    timeoutmsg->setArrivalTime(simTime()+t);
627
    EVCB.messageScheduled(timeoutmsg);
628
    simulation.insertMsg(timeoutmsg);
629

    
630
    simulation.transferToMain();
631
    if (stack_cleanup_requested)
632
        throw cStackCleanupException();
633

    
634
    cMessage *newmsg = simulation.msg_for_activity;
635

    
636
    if (newmsg!=timeoutmsg)
637
        throw cRuntimeError("message arrived during wait() call ((%s)%s); if this "
638
                            "should be allowed, use waitAndEnqueue() instead of wait()",
639
                            newmsg->getClassName(), newmsg->getFullName());
640
}
641

    
642
void cSimpleModule::waitAndEnqueue(simtime_t t, cQueue *queue)
643
{
644
    if (!usesActivity())
645
        throw cRuntimeError(eNORECV);
646
    if (t<0)
647
        throw cRuntimeError(eNEGTIME);
648
    if (!queue)
649
        throw cRuntimeError("waitAndEnqueue(): queue pointer is NULL");
650

    
651
    timeoutmsg->setArrivalTime(simTime()+t);
652
    EVCB.messageScheduled(timeoutmsg);
653
    simulation.insertMsg(timeoutmsg);
654

    
655
    for(;;)
656
    {
657
        simulation.transferToMain();
658
        if (stack_cleanup_requested)
659
            throw cStackCleanupException();
660

    
661
        cMessage *newmsg = simulation.msg_for_activity;
662

    
663
        if (newmsg==timeoutmsg)
664
            break;
665
        else
666
            queue->insert(newmsg);
667
    }
668
}
669

    
670
//-------------
671

    
672
cMessage *cSimpleModule::receive()
673
{
674
    if (!usesActivity())
675
        throw cRuntimeError(eNORECV);
676

    
677
    simulation.transferToMain();
678
    if (stack_cleanup_requested)
679
        throw cStackCleanupException();
680

    
681
    cMessage *newmsg = simulation.msg_for_activity;
682
    return newmsg;
683
}
684

    
685
cMessage *cSimpleModule::receive(simtime_t t)
686
{
687
    if (!usesActivity())
688
        throw cRuntimeError(eNORECV);
689
    if (t<0)
690
        throw cRuntimeError(eNEGTOUT);
691

    
692
    timeoutmsg->setArrivalTime(simTime()+t);
693
    EVCB.messageScheduled(timeoutmsg);
694
    simulation.insertMsg(timeoutmsg);
695

    
696
    simulation.transferToMain();
697
    if (stack_cleanup_requested)
698
        throw cStackCleanupException();
699

    
700
    cMessage *newmsg = simulation.msg_for_activity;
701

    
702
    if (newmsg==timeoutmsg)  // timeout expired
703
    {
704
        take(timeoutmsg);
705
        return NULL;
706
    }
707
    else  // message received OK
708
    {
709
        take(cancelEvent(timeoutmsg));
710
        return newmsg;
711
    }
712
}
713

    
714
//-------------
715

    
716
void cSimpleModule::activity()
717
{
718
    // default thread function
719
    // the user must redefine this for the module to do anything useful
720
    throw cRuntimeError("Redefine activity() or specify zero stack size to use handleMessage()");
721
}
722

    
723
void cSimpleModule::handleMessage(cMessage *)
724
{
725
    // handleMessage is an alternative to activity()
726
    // this is the default version
727
    throw cRuntimeError("Redefine handleMessage() or specify non-zero stack size to use activity()");
728
}
729

    
730
//-------------
731

    
732
void cSimpleModule::endSimulation()
733
{
734
    throw cTerminationException(eENDSIM);
735
}
736

    
737
bool cSimpleModule::snapshot(cObject *object, const char *label)
738
{
739
    return simulation.snapshot(object ? object : &simulation, label);
740
}
741

    
742
bool cSimpleModule::hasStackOverflow() const
743
{
744
    return coroutine ? coroutine->hasStackOverflow() : false;
745
}
746

    
747
unsigned cSimpleModule::getStackSize() const
748
{
749
    return coroutine ? coroutine->getStackSize() : 0;
750
}
751

    
752
unsigned cSimpleModule::getStackUsage() const
753
{
754
    return coroutine ? coroutine->getStackUsage() : 0;
755
}
756

    
757