My gamedev code is typically based apon several objects which work together to achieve their goals, and this leads to a high degree of "object interdependancy", which imho is bad, because it means that the objects are no longer 'truly modular' and are less portable between projects.
I needed some kind of mechanism which would pass Messages between arbitrary objects, freeing them of these interdependancies.
This mechanism would act as the "glue" which binds various objects together in my applications, while being totally naive as to the nature of the objects with which it is communicating, acting as a medium.

I have attached a beta version of my MessageQueue object.
It is a very flexible and powerful mechanism which can be used for:
-inter-object messaging between arbitrary objects or groups of objects
-executing arbitrary code in response to arbitrary events (callbacks)
-linearly driving arbitrary series of code functions (VM and Scripting)
-indirectly calling methods of 'foreign objects'
-delayed event execution (at some future Time)
There's probably many more uses that I haven't considered!

This object forms the 'core' of your engine as the other objects must 'register' to receive one or more events and supply appropriate callback functions..  it could very well drive the entire application !!

Note that the code isn't totally optimized, I intend to rework it once I've received a little feedback..
Please let me know if you have any suggestions for refinements or improvements, or if you find novel ways to use this object :)

Attachments:
Posted on 2006-11-17 20:01:33 by Homer
Sounds great!
If there could be some more specific examples that instantiate the objects in the MessageQueue.inc for beginners to easily give it a try, that would be perfect!

Regards,
Guidry
Posted on 2006-11-17 23:15:55 by guidry
Uhm, a year or so ago I had pointed something like this out - my ATC "HookVector" :) . Such code (including your MessageQueue) is really handy ;)  (been using it for quite some time). It really can drive a whole app beautifully, as you suggested ;) .
The "dDelay" is just useful for games.
But, making a new thread for the callback?! In more complex apps/games, this will later drag you down (eventually crashing apps without good thread sync). In games, I'd recommend calling a method "TrySendEvents" of all MessageQueue (hold all event-objects in a DataCollection/ObjVector), in your main thread, in your game-loop. In apps, it's easy- you don't need the "dDelay", so you call all callbacks (I call them "hooks") right when the event is fired.
I've had enough serious troubles with thread sync, thus I'm doing everything possible to keep as few as possible :) .

P.S: Just one dArg is not going to suffice. 4 are what one will need ;)
Posted on 2006-11-17 23:29:04 by Ultrano
Thanks for the responses, I appreciate them.

In regards to the Worker processing messages in its own thread - this has one immediately obvious advantage in my mind : it means that the PROCESSING of Messages is performed asynchronously with respect to the thread(s) which POSTED those Messages, meaning that a 'caller' never has to wait for an event to complete (a separate notification can be posted by the 'Callee' to indicate Completion), basically I borrowed from the concepts behind IOCP, the main difference being that MessageQueue supports the notion of 'Permanent Messages' (not automatically dequeued) whereas IOCP dequeues all Messages by default (GetQueuedCompletionStatus).
Since there is only ONE Worker thread, Messages are guaranteed to A) be processed in order and B) to COMPLETE processing in order.
Should we add more Worker Threads, a Message numbering system must be introduced.
Most importantly, having a dedicated Worker thread or threads means that we can implement a kind of 'lazy polling' .. note the inclusion of a 'wakeup' event, and the use of the WaitForSingleObject api... when theres no Messages pending, the Worker thread uses zero cpu time, so if you're purely using MessageQueue for event notifications (no Permanent messages), the Worker will automatically toggle between Wake and Sleep states on demand.

In regards to Thread Synchronization, OA32 has a new calling mechanism (xOCall) which is designed specifically for this purpose - should it become desirable to enforce synchronization between the Worker and any other arbitrary threads, the calling convention can be modified to emulate xOCall.
In regards to the one parameter in the Message struct, if you need more, you can store a Pointer to a custom structure there, provided you note that your Callback is responsible for deallocating it.
Passing the entire Message structure to callbacks isn't the most efficient mechanism imagineable, but it's the most flexible and powerful .. for example it becomes possible to define a single Callback which is responsible for processing several Messages in a given object (rather than several callbacks for single Messages), it allows callbacks to identify who sent them Messages and thus implement Security schemes (should this be desirable), and it means that callbacks have a standard prototype which may assist in design and debugging.

I'd be happy to create a simple demo showing how this object MIGHT be used, but really no one example is going to do it justice.

Note that this object becomes your 'official timekeeper', your main application queries the MessageQueue object in this respect.
One notable feature missing from the current version is the ability to define 'Pulsed' Messages - those which do not expire, and which fire at a given ELAPSED time.

Posted on 2006-11-20 00:50:37 by Homer