/* ZMessageStream.hpp Author: James Russell Created: 2/4/2013 Purpose: Messaging stream class, which acts as an event loop for the entire simulation. Processes both untargeted (or 'simulation' targeted) messages and messages that are intended for a specific entity. Handles organization and sorting so that the messages can be processed in a thread safe fashion at a later time. License: Copyright 2013, 762 Studios */ #pragma once #ifndef _ZMESSAGESTREAM_HPP #define _ZMESSAGESTREAM_HPP #include #include #include "ZSimulationDefs.hpp" //Forward Declarations class ZSimulation; /* Message structure. Messages are in the form of a POD struct of a set size payload. Message layout definitions (see ZSimulationDefs.hpp for examples) can be used to interpret the data. */ struct ZMessage { mID Type; // type of the message eID Sender; // the entity who sent the message (ZSIM_EID_SYSTEM means system or simulation) eID Target; // the target of the message (ZSIM_EID_SYSTEM calls the default handler) char Payload[ZSIM_MESSAGE_SIZE]; // the message payload, which can be interpreted via layout definitions }; /* Message handler function profile. */ typedef void (*ZMessageHandler)(eID _sender, eID _target, void* _payload, ZSimulation& _sim); /* Message streaming class, used to send messages from one entity to another or to the simulation. */ class ZMessageStream { public: // c'tor ZMessageStream(); // d'tor ~ZMessageStream(); // message 'Send' method, which will copy data from a struct into the message (MT must be a POD type) template void Send(mID _type, eID _sender, eID _target, MT _payload) { ZMessage* msg; #if SST_COMPILER == SST_COMPILER_MSVC #pragma warning(push) #pragma warning(disable:4127) #endif ZASSERT(sizeof(MT) <= ZSIM_MESSAGE_SIZE, "Message sent with payload too large!"); #if SST_COMPILER == SST_COMPILER_MSVC #pragma warning(pop) #endif // Synchronized Section { ZLock lock(AllocatorMutex); msg = MessageAllocator.Allocate(); } ZASSERT(msg != NULL, "Unable to allocate message!"); msg->Type = _type; msg->Sender = _sender; msg->Target = _target; MemCopy(uint8_t, msg->Payload, &_payload, sizeof(MT)); // Synchronized Section { ZLock lock(BufferMutex); MessageBuffers[CurrentBufferIndex].PushBack(msg); } } /* Pumps the message queue, processing the messages that have been sent since last time this method was called. Returns true if additional messages were generated during the processing of the current set of messages. Messages that are targeted at a specific entity will be delivered to that entity. If that entity cannot be found, the message is dropped. Messages that target the reserved eID of '0' will be handled by the message stream handlers. Only one thread should ever be calling this method. Multiple threads calling this method will result in that thread waiting until the previous has finished. Returns true if messages are still available for processing, false if the current queue is empty. */ bool ProcessMessages(ZSimulation& _sim); /* External message handlers. Used to return an entity targeted message or process a targeted message that had no installed handler. */ void HandleMessage(ZMessage* _msg, ZSimulation& _sim); void ReturnMessage(ZMessage* _msg); /* Push and Pop Methods for Handlers. */ void PushHandler(mID _type, ZMessageHandler _handler); ZMessageHandler PopHandler(mID _type); protected: ZMutex AllocatorMutex; // lock for allocators ZMutex BufferMutex; // lock for buffers ZMutex HandlerMutex; // lock for handlers ZMutex ProcessingMutex; // lock for processing int CurrentBufferIndex; // index to our current message buffer ZRingBuffer MessageBuffers[2]; // the current set of messages (one receives messages while the other is processed) ZHashMap> MessageHandlers; // set of handler functions ZSlabAllocator MessageAllocator; // slab allocator for messages private: DISABLE_COPY_AND_ASSIGN(ZMessageStream); }; #endif