The prior commit (c78662a) filed cross-pod damage failure under a "cross-TU Entity/EntityID layout divergence" (and the P5 base-region audit). A compile-time offsetof probe injected into BOTH translation units disproves that: game/reconstructed AND engine/MUNGA compute an IDENTICAL Entity layout -- sizeof(Entity)=444 offsetof(entityID)=380 (0x17C) offsetof(ownerID)=388 offsetof(simulationFlags)=32 offsetof(Mech,entityID)=380 (Entity subobject at 0) So Entity::Dispatch reads this->entityID at the SAME offset mech4's GetEntityID() does -- there is no per-TU read difference, and the logged 3:22 (mech4 candidate) vs 3:19 (engine Dispatch) cannot be one object read two ways. Those lines were different objects/messages, mis-correlated. The distinct, REAL P5 "base-region layout divergence" (HARD_PROBLEMS.md) is about raw-offset stomps at this+0x2d4..0x2f0 -- far above entityID -- and is unrelated to #47. Changes: - Revert the committed BT_MP_NET engine diagnostics (ENTITY/EVENT/ NTTMGR/RECEIVER/L4NET) to the cleana9c3e96baseline -- those are the very instruments that produced the mis-correlated data. - mech4 BT_MP_FORCE_DMG hook: dispatch via the real m->Dispatch(&td) path (no false id stamping); comment records the offsetof finding. - context/multiplayer.md: layout-divergence RULED OUT; 3:22/3:19 marked unconfirmed; leading hypotheses reframed as H2 (wire host-relative (de)serialization) vs H3 (replicant id != master's registered key), to be distinguished by a 2-node run with per-message correlation. Solo un-regressed (mech walks + targets, 0 faults). Cross-pod delivery remains open, but the investigation is redirected off the wrong (large, structural) layout-audit path onto the EntityID wire/id-assignment path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1080 lines
25 KiB
C++
1080 lines
25 KiB
C++
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "event.h"
|
|
#include "registry.h"
|
|
#include "app.h"
|
|
#include "entity.h"
|
|
#include "evtstat.h"
|
|
|
|
#if defined(TRACE_PROCESS_EVENT)
|
|
static BitTrace Process_Event("Process Event");
|
|
# define SET_PROCESS_EVENT() Process_Event.Set()
|
|
# define CLEAR_PROCESS_EVENT() Process_Event.Clear()
|
|
#else
|
|
# define SET_PROCESS_EVENT()
|
|
# define CLEAR_PROCESS_EVENT()
|
|
#endif
|
|
|
|
//#############################################################################
|
|
//########################### AbstractEvent #############################
|
|
//#############################################################################
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
AbstractEvent::AbstractEvent(
|
|
Receiver::Message *message,
|
|
Time time,
|
|
ClassID class_id
|
|
):
|
|
Node(class_id)
|
|
{
|
|
Verify(message);
|
|
alarmTime = time;
|
|
|
|
//
|
|
//--------------------------------------------------------
|
|
// Allocate some memory to store the message parameters in
|
|
//--------------------------------------------------------
|
|
//
|
|
size_t long_size = (message->messageLength+3)>>2;
|
|
messageToSend = (Receiver::Message*)new long[long_size];
|
|
Check_Pointer(messageToSend);
|
|
Register_Pointer(messageToSend);
|
|
|
|
Mem_Copy(
|
|
messageToSend,
|
|
message,
|
|
message->messageLength,
|
|
long_size*sizeof(long)
|
|
);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
AbstractEvent::~AbstractEvent()
|
|
{
|
|
if (messageToSend)
|
|
{
|
|
Unregister_Pointer(messageToSend);
|
|
delete messageToSend;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
AbstractEvent::Process()
|
|
{
|
|
Fail("AbstractEvent::Process should not be called!\n");
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
AbstractEvent::DumpData()
|
|
{
|
|
Fail("AbstractEvent::DumpData should not be called!\n");
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################### Event #################################
|
|
//#############################################################################
|
|
|
|
MemoryBlock *Event::GetAllocatedMemory()
|
|
{
|
|
static MemoryBlock allocatedMemory(sizeof(Event), 20, 10, "Events");
|
|
return &allocatedMemory;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Event::Event(
|
|
Receiver *target,
|
|
Receiver::Message *message,
|
|
Time time
|
|
):
|
|
AbstractEvent(message, time, EventClassID),
|
|
targetReceiver(this)
|
|
{
|
|
targetReceiver.Add(target);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Event::~Event()
|
|
{
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
//
|
|
//-------------------------------------------
|
|
// Delete the link to the current event queue
|
|
//-------------------------------------------
|
|
//
|
|
PlugIteratorOf<EventQueue*>
|
|
old_queue_link(this);
|
|
Node *node;
|
|
while ((node = old_queue_link.GetCurrent()) != NULL)
|
|
{
|
|
Check(node);
|
|
if (
|
|
node->GetClassID() == EventQueueClassID
|
|
|| node->GetClassID() == GeneralEventQueueClassID
|
|
)
|
|
{
|
|
GeneralEventQueue *q = Cast_Object(GeneralEventQueue*, node);
|
|
while (q->priorityLevels < 0)
|
|
{
|
|
--q;
|
|
Check(q);
|
|
}
|
|
--q->eventCount;
|
|
if (q->eventCountTrace)
|
|
{
|
|
Check(q->eventCountTrace);
|
|
q->eventCountTrace->TakeSnapshot(q->eventCount);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Event::Repost(
|
|
EventQueue* queue,
|
|
int priority,
|
|
Time new_time
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(queue);
|
|
|
|
//
|
|
//-------------------------------------------
|
|
// Delete the link to the current event queue
|
|
//-------------------------------------------
|
|
//
|
|
PlugIteratorOf<EventQueue*>
|
|
old_queue_link(this);
|
|
Node *node;
|
|
while ((node = old_queue_link.GetCurrent()) != NULL)
|
|
{
|
|
Check(node);
|
|
if (
|
|
node->GetClassID() == EventQueueClassID
|
|
|| node->GetClassID() == GeneralEventQueueClassID
|
|
|| node->GetClassID() == DeferredEventQueueClassID
|
|
)
|
|
{
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
if (node->GetClassID() != DeferredEventQueueClassID)
|
|
{
|
|
GeneralEventQueue *q = Cast_Object(GeneralEventQueue*, node);
|
|
while (q->priorityLevels < 0)
|
|
{
|
|
--q;
|
|
Check(q);
|
|
}
|
|
--q->eventCount;
|
|
if (q->eventCountTrace)
|
|
{
|
|
Check(q->eventCountTrace);
|
|
q->eventCountTrace->TakeSnapshot(q->eventCount);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
old_queue_link.Remove();
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
old_queue_link.Next();
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Now, if necessary, reset the time and stuff the event in the new event
|
|
// queue
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
if (new_time.ticks)
|
|
{
|
|
alarmTime = new_time;
|
|
}
|
|
queue->Enqueue(priority,this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Event::Defer()
|
|
{
|
|
//
|
|
//-----------------------------------------------
|
|
// See if the target has a deferred queue already
|
|
//-----------------------------------------------
|
|
//
|
|
Receiver *target = targetReceiver.GetCurrent();
|
|
Check(target);
|
|
PlugIteratorOf<DeferredEventQueue*>
|
|
blind_links(target);
|
|
DeferredEventQueue
|
|
*deferred_queue;
|
|
while ((deferred_queue = blind_links.ReadAndNext()) != NULL)
|
|
{
|
|
if (deferred_queue->GetClassID() == DeferredEventQueueClassID)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// If we couldn't find a deferred queue, then a new one must be created
|
|
// and attached to the object
|
|
//----------------------------------------------------------------------
|
|
//
|
|
if (!deferred_queue)
|
|
{
|
|
deferred_queue = new DeferredEventQueue(target);
|
|
Register_Object(deferred_queue);
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Now, simply repost the event to this event queue
|
|
//-------------------------------------------------
|
|
//
|
|
Repost(deferred_queue->deferredEvents,0);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Event::Process()
|
|
{
|
|
Check(this);
|
|
targetReceiver.GetCurrent()->Receive(this);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Event::DumpData()
|
|
{
|
|
Check(this);
|
|
|
|
Receiver *target = targetReceiver.GetCurrent();
|
|
Check(target);
|
|
Check(application);
|
|
Registry *registry = application->GetRegistry();
|
|
if (registry)
|
|
{
|
|
Check(registry);
|
|
Receiver::SharedData *shared_data =
|
|
registry->GetStaticData(target->GetClassID());
|
|
if (shared_data)
|
|
{
|
|
Check(shared_data);
|
|
Derivation *derivation = shared_data->derivedClasses;
|
|
Check(derivation);
|
|
Receiver::MessageHandlerSet *handlers =
|
|
shared_data->activeMessageHandlers;
|
|
Check(handlers);
|
|
DEBUG_STREAM << handlers->GetName(messageToSend->messageID) << " message for class " << derivation->className << std::endl << std::flush;
|
|
}
|
|
else
|
|
{
|
|
goto Vanilla_Message;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vanilla_Message:
|
|
DEBUG_STREAM << "Message " << messageToSend->messageID << " for object of classID " << target->GetClassID() << std::endl << std::flush;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
Event::ReleaseLinkHandler(
|
|
Socket*,
|
|
Plug*
|
|
)
|
|
{
|
|
Unregister_Object(this);
|
|
delete this;
|
|
}
|
|
|
|
//#############################################################################
|
|
//########################### NetworkEvent ##############################
|
|
//#############################################################################
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
NetworkEvent::NetworkEvent(
|
|
HostID host_ID,
|
|
NetworkManager::ClientID client_ID,
|
|
EventStyle event_style,
|
|
Receiver::Message *message,
|
|
Time time
|
|
):
|
|
AbstractEvent(message, time, NetworkEventClassID)
|
|
{
|
|
hostID = host_ID;
|
|
clientID = client_ID;
|
|
eventStyle = event_style;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
NetworkEvent::~NetworkEvent()
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
NetworkEvent::Process()
|
|
{
|
|
Check(this);
|
|
switch (eventStyle)
|
|
{
|
|
case SendStyle:
|
|
Check(application);
|
|
application->GetNetworkManager()->Send(
|
|
messageToSend,
|
|
clientID,
|
|
hostID
|
|
);
|
|
break;
|
|
|
|
case BroadcastStyle:
|
|
Check(application);
|
|
application->GetNetworkManager()->Broadcast(
|
|
messageToSend,
|
|
clientID
|
|
);
|
|
break;
|
|
|
|
case ExclusiveBroadcastStyle:
|
|
Check(application);
|
|
application->GetNetworkManager()->ExclusiveBroadcast(
|
|
messageToSend,
|
|
clientID
|
|
);
|
|
break;
|
|
}
|
|
|
|
Unregister_Object(this);
|
|
delete this;
|
|
}
|
|
|
|
//#############################################################################
|
|
//####################### GeneralEventQueue ############################
|
|
//#############################################################################
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
GeneralEventQueue::GeneralEventQueue(ClassID class_ID):
|
|
Node(class_ID),
|
|
pendingEvents(this),
|
|
timedEvents(this)
|
|
{
|
|
priorityLevels = -1;
|
|
eventCount = 0;
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
eventCountTrace = NULL;
|
|
#endif
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
GeneralEventQueue::GeneralEventQueue():
|
|
Node(GeneralEventQueueClassID),
|
|
pendingEvents(this),
|
|
timedEvents(this)
|
|
{
|
|
priorityLevels = -1;
|
|
eventCount = 0;
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
eventCountTrace = NULL;
|
|
#endif
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
GeneralEventQueue::~GeneralEventQueue()
|
|
{
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
if (eventCountTrace)
|
|
{
|
|
Unregister_Object(eventCountTrace);
|
|
delete eventCountTrace;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
GeneralEventQueue*
|
|
GeneralEventQueue::Make(
|
|
int priorities,
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
const char* trace_name
|
|
#else
|
|
const char*
|
|
#endif
|
|
)
|
|
{
|
|
GeneralEventQueue *queue = new GeneralEventQueue[priorities];
|
|
Register_Object(queue);
|
|
|
|
queue->priorityLevels = priorities;
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
if (trace_name)
|
|
{
|
|
Check_Pointer(trace_name);
|
|
queue->eventCountTrace =
|
|
new TraceOf<int>(
|
|
trace_name,
|
|
0,
|
|
Trace::IntegerType,
|
|
TraceSample::IntegerSnapshot
|
|
);
|
|
Register_Object(queue->eventCountTrace);
|
|
}
|
|
#endif
|
|
return queue;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
GeneralEventQueue::Post(
|
|
int priority,
|
|
Receiver *target,
|
|
Receiver::Message *message,
|
|
Time time
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(target);
|
|
Check(message);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Create a new event, then insert it at the end of the event queue. This
|
|
// means that timed events get put in sort of haphazardly!!!!
|
|
//------------------------------------------------------------------------
|
|
//
|
|
Event *event = new Event(target, message, time);
|
|
Register_Object(event);
|
|
|
|
Enqueue(priority, event);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
GeneralEventQueue::SendEvent(
|
|
int priority,
|
|
HostID host_ID,
|
|
NetworkManager::ClientID client_id,
|
|
Receiver::Message *message,
|
|
Time when
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
NetworkEvent *event =
|
|
new NetworkEvent(
|
|
host_ID, client_id, NetworkEvent::SendStyle, message, when
|
|
);
|
|
Register_Object(event);
|
|
Enqueue(priority, event);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
GeneralEventQueue::BroadcastEvent(
|
|
int priority,
|
|
NetworkManager::ClientID client_ID,
|
|
Receiver::Message *message,
|
|
Time when
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
// HACK - What is a null network address?
|
|
//
|
|
NetworkEvent *event =
|
|
new NetworkEvent(
|
|
(NetworkAddress)0,
|
|
client_ID,
|
|
NetworkEvent::BroadcastStyle,
|
|
message,
|
|
when
|
|
);
|
|
Register_Object(event);
|
|
Enqueue(priority, event);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
GeneralEventQueue::ExclusiveBroadcastEvent(
|
|
int priority,
|
|
NetworkManager::ClientID client_ID,
|
|
Receiver::Message *message,
|
|
Time when
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
//
|
|
// HACK - What is a null network address?
|
|
//
|
|
NetworkEvent *event =
|
|
new NetworkEvent(
|
|
(NetworkAddress)0,
|
|
client_ID,
|
|
NetworkEvent::ExclusiveBroadcastStyle,
|
|
message,
|
|
when
|
|
);
|
|
Register_Object(event);
|
|
Enqueue(priority, event);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
GeneralEventQueue::Enqueue(
|
|
int priority,
|
|
AbstractEvent *event
|
|
)
|
|
{
|
|
Check(event);
|
|
Verify((unsigned)priority < priorityLevels);
|
|
|
|
//
|
|
//------------------------------------------------------------
|
|
// If this is not a timed event, stuff it in the pending queue
|
|
//------------------------------------------------------------
|
|
//
|
|
if (!event->alarmTime.ticks)
|
|
{
|
|
this[priority].pendingEvents.Add(event);
|
|
}
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Otherwise, sort it into the timed queue
|
|
//----------------------------------------
|
|
//
|
|
else
|
|
{
|
|
ChainIteratorOf<AbstractEvent*>
|
|
timed_events(this[priority].timedEvents);
|
|
timed_events.Last();
|
|
AbstractEvent* next_event = timed_events.GetCurrent();
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If the timed queue is empty, or if we are later than the last entry,
|
|
// just add the event to the end
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (!next_event || next_event->alarmTime.ticks <= event->alarmTime.ticks)
|
|
{
|
|
this[priority].timedEvents.Add(event);
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Find the event to insert before. If we run off the beginning, stop
|
|
// checking and set the iterator to the first event in the queue
|
|
//--------------------------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
do
|
|
{
|
|
timed_events.Previous();
|
|
next_event = timed_events.GetCurrent();
|
|
if (!next_event)
|
|
{
|
|
timed_events.First();
|
|
break;
|
|
}
|
|
} while (next_event->alarmTime.ticks > event->alarmTime.ticks);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If we didn't run off the queue, we need to insert after the current
|
|
// event, so bump the iterator forward one event
|
|
//--------------------------------------------------------------------
|
|
//
|
|
if (next_event)
|
|
{
|
|
timed_events.Next();
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// Insert the new event ahead of where the iterator currently points
|
|
//------------------------------------------------------------------
|
|
//
|
|
timed_events.Insert(event);
|
|
}
|
|
}
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
++eventCount;
|
|
if (eventCountTrace)
|
|
{
|
|
Check(eventCountTrace);
|
|
eventCountTrace->TakeSnapshot(eventCount);
|
|
}
|
|
#endif
|
|
#if defined(USE_EVENT_STATISTICS)
|
|
event_statistics_manager.Maintain(Cast_Object(Event*, event));
|
|
#endif
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
AbstractEvent*
|
|
GeneralEventQueue::PeekAtNextEvent(int min_priority)
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
// Start at the top of the priority queue, searching backwards until we find
|
|
// an available event or run off the minimum queue priority
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
Verify(min_priority >= 0);
|
|
int priority = priorityLevels;
|
|
while (--priority >= min_priority)
|
|
{
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// See if the first event in the timed queue is ready to go. If not,
|
|
// none of the other timed events will be ready to go either
|
|
//-------------------------------------------------------------------
|
|
//
|
|
ChainIteratorOf<AbstractEvent*>
|
|
timed_events(this[priority].timedEvents);
|
|
AbstractEvent
|
|
*event = timed_events.GetCurrent();
|
|
if (event)
|
|
{
|
|
Check(event);
|
|
if (event->alarmTime.ticks <= Now().ticks)
|
|
{
|
|
return event;
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------
|
|
// Now look in the untimed queue
|
|
//------------------------------
|
|
//
|
|
ChainIteratorOf<AbstractEvent*>
|
|
events(this[priority].pendingEvents);
|
|
event = events.GetCurrent();
|
|
if (event)
|
|
{
|
|
Check(event);
|
|
return event;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
GeneralEventQueue::IsPriorityEmpty(int priority)
|
|
{
|
|
Check(this);
|
|
|
|
Verify(priority >= 0 && priority < priorityLevels);
|
|
ChainIteratorOf<AbstractEvent*> i(this[priority].pendingEvents);
|
|
ChainIteratorOf<AbstractEvent*> j(this[priority].timedEvents);
|
|
return !i.GetCurrent() && !j.GetCurrent();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
GeneralEventQueue::ProcessOneEvent(int min_priority)
|
|
{
|
|
Check(this);
|
|
Verify(min_priority >= 0);
|
|
|
|
AbstractEvent *event=PeekAtNextEvent(min_priority);
|
|
if (event)
|
|
{
|
|
SET_PROCESS_EVENT();
|
|
event->Process();
|
|
CLEAR_PROCESS_EVENT();
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
GeneralEventQueue::ProcessAllEvents(int min_priority)
|
|
{
|
|
Check(this);
|
|
Verify(min_priority >= 0);
|
|
while (ProcessOneEvent(min_priority));
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
GeneralEventQueue::FlushAllEvents(int max_priority)
|
|
{
|
|
Check(this);
|
|
Verify(max_priority < priorityLevels);
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Make sure that max_priority is set right
|
|
//-----------------------------------------
|
|
//
|
|
if (max_priority < 0)
|
|
{
|
|
max_priority = priorityLevels - 1;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Step through each of the priority queues and delete any events found
|
|
// within
|
|
//---------------------------------------------------------------------
|
|
//
|
|
for (int priority=max_priority; priority>=0; --priority)
|
|
{
|
|
|
|
//
|
|
//---------------------------------------
|
|
// Delete the events from the timed queue
|
|
//---------------------------------------
|
|
//
|
|
ChainIteratorOf<AbstractEvent*> i(this[priority].timedEvents);
|
|
AbstractEvent *event;
|
|
while ((event = i.ReadAndNext()) != NULL)
|
|
{
|
|
Unregister_Object(event);
|
|
delete event;
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Delete the events from the regular queue
|
|
//-----------------------------------------
|
|
//
|
|
ChainIteratorOf<AbstractEvent*> j(this[priority].pendingEvents);
|
|
while ((event = j.ReadAndNext()) != NULL)
|
|
{
|
|
Unregister_Object(event);
|
|
delete event;
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
GeneralEventQueue::FlushMatchingEvents(
|
|
Receiver::MessageID target_message,
|
|
int max_priority
|
|
)
|
|
{
|
|
Check(this);
|
|
Verify(max_priority < priorityLevels);
|
|
|
|
//
|
|
//-----------------------------------------
|
|
// Make sure that max_priority is set right
|
|
//-----------------------------------------
|
|
//
|
|
if (max_priority < 0)
|
|
{
|
|
max_priority = priorityLevels - 1;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Step through each of the priority queues and delete any events found
|
|
// within
|
|
//---------------------------------------------------------------------
|
|
//
|
|
for (int p=max_priority; p>=0; --p)
|
|
{
|
|
//
|
|
//-------------------------------------------
|
|
// Delete events from the regular queue first
|
|
//-------------------------------------------
|
|
//
|
|
ChainIteratorOf<AbstractEvent*> i(this[p].pendingEvents);
|
|
AbstractEvent *event;
|
|
while ((event = i.ReadAndNext()) != NULL)
|
|
{
|
|
|
|
//
|
|
//------------------------------
|
|
// Make sure the message matches
|
|
//------------------------------
|
|
//
|
|
if (
|
|
target_message == Receiver::AnyMessageID
|
|
|| target_message == event->messageToSend->messageID
|
|
)
|
|
{
|
|
Unregister_Object(event);
|
|
delete event;
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Delete events from the timed queue
|
|
//-----------------------------------
|
|
//
|
|
ChainIteratorOf<AbstractEvent*> j(this[p].timedEvents);
|
|
while ((event = j.ReadAndNext()) != NULL)
|
|
{
|
|
|
|
//
|
|
//------------------------------
|
|
// Make sure the message matches
|
|
//------------------------------
|
|
//
|
|
if (
|
|
target_message == Receiver::AnyMessageID
|
|
|| target_message == event->messageToSend->messageID
|
|
)
|
|
{
|
|
Unregister_Object(event);
|
|
delete event;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
GeneralEventQueue::DumpEventQueue()
|
|
{
|
|
Check(this);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Step through each of the priority queues and display any events found
|
|
// within
|
|
//----------------------------------------------------------------------
|
|
//
|
|
CollectionSize total_count = 0;
|
|
for (int priority=priorityLevels - 1; priority>=0; --priority)
|
|
{
|
|
DEBUG_STREAM << "EventQueue priority " << priority
|
|
<< "\n--------------------------------------------------------------\n";
|
|
|
|
ChainIteratorOf<AbstractEvent*> i(this[priority].pendingEvents);
|
|
AbstractEvent *event;
|
|
size_t count=0;
|
|
while ((event = i.ReadAndNext()) != NULL)
|
|
{
|
|
++count;
|
|
event->DumpData();
|
|
}
|
|
DEBUG_STREAM << count << " priority " << priority << " events\n\n" << std::flush;
|
|
count = 0;
|
|
ChainIteratorOf<AbstractEvent*> j(this[priority].timedEvents);
|
|
while ((event = j.ReadAndNext()) != NULL)
|
|
{
|
|
++count;
|
|
event->DumpData();
|
|
}
|
|
DEBUG_STREAM << count << " priority " << priority << " timed events\n\n" << std::flush;
|
|
total_count += count;
|
|
|
|
}
|
|
DEBUG_STREAM << total_count << " total events in queues\n\n" << std::flush;
|
|
}
|
|
|
|
//#############################################################################
|
|
//############################ EventQueue ###############################
|
|
//#############################################################################
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
EventQueue::EventQueue():
|
|
GeneralEventQueue(EventQueueClassID)
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
EventQueue::~EventQueue()
|
|
{
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
EventQueue*
|
|
EventQueue::Make(
|
|
int priorities,
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
const char* trace_name
|
|
#else
|
|
const char*
|
|
#endif
|
|
)
|
|
{
|
|
EventQueue *queue = new EventQueue[priorities];
|
|
Register_Object(queue);
|
|
|
|
queue->priorityLevels = priorities;
|
|
#if defined(TRACE_EVENT_COUNT)
|
|
if (trace_name)
|
|
{
|
|
Check_Pointer(trace_name);
|
|
queue->eventCountTrace =
|
|
new TraceOf<int>(
|
|
trace_name,
|
|
0,
|
|
Trace::IntegerType,
|
|
TraceSample::IntegerSnapshot
|
|
);
|
|
Register_Object(queue->eventCountTrace);
|
|
}
|
|
#endif
|
|
return queue;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
EventQueue::ProcessMatchingEvents(
|
|
Receiver *receiver,
|
|
Receiver::MessageID message_ID
|
|
)
|
|
{
|
|
Check(this);
|
|
Check(receiver);
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// Step through each of the priority queues and process any events
|
|
// found within
|
|
//----------------------------------------------------------------
|
|
//
|
|
for (int p=priorityLevels-1; p>=0; --p)
|
|
{
|
|
Event *event;
|
|
AbstractEvent *abstract_event;
|
|
ChainIteratorOf<AbstractEvent*> i(this[p].pendingEvents);
|
|
while ((abstract_event = i.ReadAndNext()) != NULL)
|
|
{
|
|
Check(abstract_event);
|
|
event = Cast_Object(Event*, abstract_event);
|
|
|
|
//
|
|
//------------------------------
|
|
// Make sure the message matches
|
|
//------------------------------
|
|
//
|
|
if (
|
|
(event->targetReceiver.GetCurrent() == receiver) &&
|
|
(message_ID == Receiver::AnyMessageID ||
|
|
message_ID == event->messageToSend->messageID)
|
|
)
|
|
{
|
|
event->Process();
|
|
}
|
|
}
|
|
|
|
ChainIteratorOf<AbstractEvent*> j(this[p].timedEvents);
|
|
while ((abstract_event = j.ReadAndNext()) != NULL)
|
|
{
|
|
Check(abstract_event);
|
|
event = Cast_Object(Event*, abstract_event);
|
|
|
|
//
|
|
//------------------------------
|
|
// Make sure the message matches
|
|
//------------------------------
|
|
//
|
|
if (
|
|
(event->targetReceiver.GetCurrent() == receiver) &&
|
|
(message_ID == Receiver::AnyMessageID ||
|
|
message_ID == event->messageToSend->messageID)
|
|
)
|
|
{
|
|
event->Process();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//#############################################################################
|
|
//######################## DeferredEventQueue ###########################
|
|
//#############################################################################
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DeferredEventQueue::DeferredEventQueue(Receiver* target):
|
|
Node(DeferredEventQueueClassID),
|
|
waitingReceiver(this)
|
|
{
|
|
deferredEvents = new EventQueue;
|
|
Register_Object(deferredEvents);
|
|
|
|
waitingReceiver.Add(target);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DeferredEventQueue::~DeferredEventQueue()
|
|
{
|
|
Unregister_Object(deferredEvents);
|
|
delete deferredEvents;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
DeferredEventQueue::ReleaseLinkHandler(
|
|
Socket*,
|
|
Plug*
|
|
)
|
|
{
|
|
Unregister_Object(this);
|
|
delete this;
|
|
}
|
|
|
|
#if defined(TEST_CLASS)
|
|
#include "event.tcp"
|
|
#endif
|