Re: Lahman, how ya doing?
- From: glhansen@xxxxxxxxxxxxxxxxxxxxx (Gregory L. Hansen)
- Date: Sun, 15 May 2005 01:55:22 +0000 (UTC)
In article <Dishe.851$mv5.19@trndny07>,
H. S. Lahman <hsl@xxxxxxxxxxxxxxxxx> wrote:
>Responding to Hansen...
>
>> Backing up a little, I want to return to some technical details.
>
>And you said you were winding down!
Well, call it a loose end.
>
>>>>>>>class EventElement
>>>>>>>{
>>>>>>>private:
>>>>>>> Object* recipient;
>>>>>>> int event_id;
>>>>>>> int tick_count;
>>>>>>>public:
>>>>>>> EventElement (Object* r, int e, int t)
>>>>>>> {recipient = r; event_id = e; tick_count = t;};
>>>>>>> int getTickCount() {return tick_count;};
>>>>>>>}
>>>>>>>
>>>>>>>class Timer
>>>>>>>{
>>>>>>>private:
>>>>>>> EventQueue* queueManager;
>>>>>>> EventElement eventList[MAX_EVENT_COUNT];
>>>>>>> int tick_count;
>>>>>>> int next_event;
>>>>>>>public:
>>>>>>> Timer (EvetnQueue* e)
>>>>>>> {queueManager = e; next_event = 0; tick_count = 0;};
>>>>>>> void add_event (Object* o, int e, int t);
>>>>>>> void tick()
>>>>>>> void reset() {tick_count = 0;};
>>>>>>>}
>>>>>>>
>>>>>>>void Timer::add_event (Object* o, int e, int t)
>>>>>>>{
>>>>>>> EventElement event = new [] (o, e, t);
>>>>>>> eventList[next_event] = event;
>>>>>>> next_event++;
>>>>>>> if (next_event == MAX_EVENT_COUNT)
>>>>>>> // signal exception.
>>>>>>>};
>>>>>>>
>>>>>>>void Timer::tick()
>>>>>>>{
>>>>>>> tick_count++;
>>>>>>> for (int i = 0; i < next_event; i++)
>>>>>>> {
>>>>>>> int eventTickCount = eventList[i].getTickCount();
>>>>>>> if ((tick_count MOD eventTickCount) == 0)
>>>>>>> queueManager->push(&eventList[i]);
>>>>>>> }
>>>>>>>}
>>
>>
>> Here in Timer::tick() you're pushing pointers on to the queue from a pool
>> of events held by Timer. Not relevant to my simulation, but generalizing
>> to a case where any amount of time can pass between when an event is
>> pushed on to the queue and when it is popped off and processed, it seems
>> you can have two identical events going to the same object. I guess that
>> still doesn't matter the way you've set it up, but if I'm passing data
>> with an event (and you've opined on that practice!) I can imagine the
>> content of the first being modified by preparing the second. So I'd want
>> to make a copy for the queue, and destroy the copy at the end. Fire and
>> forget, but somewhat different queue behavior, and all events must be
>> processed that way if any of them are.
>
>Actually, I wouldn't have the same event going to the same object on a
>given tick. I might send the same event to multiple objects on a tick,
>though. And I would certainly send the same events to the same objects
>on different ticks.
Remember here that I'm generalizing beyond the simulation, to a system
that might have any kind of delay between the sending and receiving of an
event. You wouldn't send two of the same event to the same object on a
given tick, but you might send two of the same event to the same object
on two different ticks before it receives the first event.
>
>Remember an event is just a message and it is defined by {message ID,
>data packet}. The message ID will be chosen to differentiate the
>semantics of what the event announces (e.g., a ubiquitous 1-tick event
>vs. a special 60th-tick event). The data packet signature (content and
>format) is also fixed for the event.
>
>As a practical matter one includes the target object address with the
>event when it is pushed on the event queue. But that is really an
>orthogonal issue related to the nature of the collaboration. That is,
>collaboration is about deciding what message is sent, who sends it, and
>who receives it. There is nothing to prevent having the same event be
>associated with multiple {sender, receiver} pairs (just as there is
>nothing to prevent the same event triggering different transitions in a
>state machine). In particular, broadcasts where a sender sends an event
>to multiple receivers are not uncommon.
Well, it's just that with the particular implementation you gave, you
were pushing pointers to a limited set of events, stored in Timer, on to
the queue. Each object to receive events has its own event template (or
whatever you want to call it), so that's not a problem.
>> And if I don't pass data in the event, but data still needs to be passed,
>> I don't know how to pass it. Using Timer as a specific example of the
>> more generic question, it *could* be given an accessor function
>> get_time(), a receiving object *could* get the time directly through that.
>> But if it's to process event1 using time1 and then event2 using time2, and
>> Timer has time3 by the time event1 hits the object and time4 by the time
>> event2 hits it, I can't think of an easy alternative other than to stuff
>> the data you need into the event so that it's all there.
>
>You only need to pass data on an event if data integrity requires that
>when the event is consumed the value accessed be the value when the
>event was sent (as opposed to it current value). That's because the
>software must already support a means for the responding action to
>access the current value synchronously. That's because in the OOA/D
>paradigm one accesses knowledge synchronously on an as-needed basis. So
>one uses event data packets when one /doesn't/ want the current value.
>
>I assume what you are angling for here is the issue in the other
>message: providing an absolute value of current time for computing a
>slope. If absolute time is measured in terms of number of ticks, then
>you could deal with the skip problem by having Timer increment a tick
>count for actual ticks processed. Whoever needs the absolute time could
>then ask Timer for the current value of that tick count.
That is what I was angling for, and I think I've resolved that in the
other message. So, send data in an event when the value needed is what
existed when the event was sent, and not when it is consumed. In my
specific application, an event queue is hardly needed since the events are
consumed immediately, so one way or the other is all good.
>> So what happens if the queue empties before...
>>
>>
>>>Things are more complicated in a truly asynchronous system where the
>>>queue may have to wait for an external event. (Or for events generated
>>>by a real- or scaled-time timer.) But then the push just needs to
>>>restart the queue operation if the event count is exactly 1 after the
>>>push. Since you are using simulation ticks rather than scaling, this
>>
>>
>> Oh, okay. I suppose somewhere in the bowels of the queue it will be
>> looping until it finds a quit event directed to queue?
>
>Not exactly. There can be times when the queue is completely comatose.
But something must be looping or the program will hit the return 0 at the
end of main(), and quit.
> As a simplified version of an queue manager consider:
>
>class EventQueue
>{
>private:
> int event_count;
> Event* next_pop_event;
> Event* nex_push_event;
> Event* (event_list[MAX_EVENTS]);
> void pop ()
> void start ()
>public:
> void push (Event* e);
>}
>
>EventQueue::pop()
>{
> // do processing to invoke responding action
> // I think I already went over this part in another message
> // This will essentially be a synchronous cal to the
> // responding state action that will return when the action
> // is complete.
>
> event_count = event_count - 1;
>
> // update next_pop_event for FIFO in event_list rung buffer
>}
>
>EventQueue::start()
>{
> // launch thread for popping events around following code
> // so that start and push can return while queue is emptied
> while (event_count > 0)
> pop();
>}
>
>EventQueue::push(Event* e)
>{
> // push e into event_list ring buffer
>
> event_count = event_count + 1;
> if (event_count == 1)
> start();
>}
>
>The point here is that when the events have been exhausted, the thread
>initiated from start terminates and the queue is completely idle until
>somebody pushes an event.
>
>[Caveat. This is oversimplified because to be thread-safe in concurrent
>processing one would have to pause the pop thread, provide some
>temporary buffer for pushed events that pop checks, or provide some
>other means of synchronizing the update of event_count.]
But good enough for my immediate needs.
>>>doesn't matter because you just just need an event to Timer to trigger
>>>tick() when all the tick's processing is completed. In your case there
>>>are a couple of easy options: (1) have Timer put a self-directed event
>>>on the queue as the last event or (2) have the event queue manager call
>>>tick() when it is empty. (Tick() can generate an event for a graceful
>>>exit if, say, the maximum simulation ticks have executed to provide an
>>>exit.)
>>
>>
>> Hey, I just told you about (1)! Some of what you've been telling me is
>> starting to make more sense in retrospect.
>
>While (2) is valid, it has a problem. Once one gets the event queue
>manager right, one wants to reuse that effort across applications. If
>the event queue gets into the business of generating special events to
>particular application objects, it won't be reusable.
Well, I did see the results of a survey of programmers about the
advantages of OOP, and code reuse was at the bottom. It was still an
advantage to be included in a list, but it was the least of the
advantages. Some shops that have tried to make generic and eminently
reuseable libraries have found it so time-consuming that it was just
abandoned.
--
"Outside the camp you shall have a place set aside to be used as a
latrine. You shall keep a trowel in your equipment and with it, when you
go outside to ease nature, you shall first dig a hole and afterward cover
up your excrement." -- Deuteronomy 23:13-14
.
- Follow-Ups:
- Re: Lahman, how ya doing?
- From: H. S. Lahman
- Re: Lahman, how ya doing?
- References:
- Re: Lahman, how ya doing?
- From: H. S. Lahman
- Re: Lahman, how ya doing?
- From: Gregory L. Hansen
- Re: Lahman, how ya doing?
- From: H. S. Lahman
- Re: Lahman, how ya doing?
- Prev by Date: Re: Lahman, how ya doing?
- Next by Date: Re: A Java Brainteaser - a Static Factory method Narrative
- Previous by thread: Re: Lahman, how ya doing?
- Next by thread: Re: Lahman, how ya doing?
- Index(es):
Relevant Pages
|