Example of a rudimentary message queue
I'd like to share a rudimentary message queue that I once wrote for my own researches. This might help to have an idea of a possible message queue implementation. There is no claim for completeness, integrity or absence of errors, and probably in most cases there are better solutions than using a custom message queue.
public void run()
{
while (running)
{
mainLoopWaitHandle.WaitOne();
EventHandlerFunction f = null;
while (running)
{
f = popEvent();
if (f == null) break;
f();
}
}
}
private void pushEvent(EventHandlerFunction handlerFunction)
{
lock (eventQueueLock)
{
int b = (queueInIndex + 1) & 255;
if (b == queueOutIndex)
{
throw new Exception("Buffer overflow in event queue.");
}
eventQueue[queueInIndex] = handlerFunction;
queueInIndex = b;
mainLoopWaitHandle.Set();
}
}
private EventHandlerFunction popEvent()
{
EventHandlerFunction ret = null;
lock(eventQueueLock)
{
int b = (queueOutIndex + 1) & 255;
if (queueOutIndex == queueInIndex)
{
mainLoopWaitHandle.Reset();
return null;
}
ret = eventQueue[queueOutIndex];
eventQueue[queueOutIndex] = null;
queueOutIndex = b;
}
return ret;
}
The main thread starts using the message queue by running run()
. run()
is a blocking method that does not return until the class attribute running
is set to false
. This can be done from an invoker method, explained below.
For invoking a method on the main thread, there are actually two methods needed. One EventHandlerFunction (let's say method A()
) that is actually getting invoked on the main thread, and method B()
that is executed on the caller's thread. I see this analogous to the UI's functions where B()
is a method of the form, and A()
get's Invoke
d from within B()
.
B()
invokes A()
by calling
pushEvent(A);
while pushEvent()
and popEvent()
are thread save.
The purpose of method B()
is to store any objects or parameters on some data structure for data transfer, that represents the parameters (or todo's) for method A()
. This structure can be a List<>
with work items for example. Method A() and B() both need to take care of proper locking this structure for thread safety.
Method A()
should also take into account that a buffer could run full either on the message queue or it's own data transfer structure) and needs to care about the consequences (e.g. drop the invoke or block the call until there is room on the stack).
Hope it helps. Contributions welcome.