Well, I’m sorry, but when all I receive is a IROMessage
instance, I do not know which class implements the interface, and as such I have no knowledge of the other interfaces it may implement.
Granted, in the SDK, there are no message classes that do not inherit from TROMessage
but there is no guarantee this will stay forever. It’s the basic principle behind using interfaces, one should only work with what the interface provides and not make any assumptions about the underlying class.
Now, if that property was to be changed to IROMessageCloneable
, then that would be an all different story. Even better, I would also make IROMessageCloneable
inherit from IROMessage
so that pretty much any variable of type IROMessage
could be replaced by IROMessageCloneable
, avoiding the use of the as
operator in many places. Obviously, the Clone
method would also return a IROMessageCloneable
instance.
Well, yes, if I am to use the event writer in only one thread at at time. To illustrate, here is the situation:
Client1 → EventSinkWriter1 → MessageClone1
Client2 → EventSinkWriter2 → MessageClone2
Client3 → EventSinkWriter3 → MessageClone3
…
ClientN → EventSinkWriterN → MessageCloneN
As already discussed, each event sink has its clone of the original message. So if I have multiple threads each working with a single client, there is no issue whatsoever.
But if I have many threads all calling the same event sink, then there are multiple crashes, as can be seen in this sample application: ROMultithreadedEvents.zip (121.8 KB)
After having compiled the client and server application, debug the server and start the client application on the side.
Now, click Connect
then Classic event storm
. This gives 51 events, arriving out of order as expected with events.
But, if you now click on Parallel event storm
, then the server creates multiple threads that all call TClient.NotifySomethingHappened
at the same time, which leads to parallel usage of the message clone held by the FEventSinkWriter
instance inside TClient
.
And there, you will see crashes all over the place because the calls are stepping on each others toes.
Now, if inside TMyClientEventSink_Writer.SomethingHappened
we make sure that lMessage
is a unique instance, then we have no parallel issue. And one way to make this happen is to change the _GetMessage
method as suggested above. However, this prevents the client from processing the events as it, too, uses TROEventProxy
inside TROEventReceiver
. So the real solution is to change the code inside TMyClientEventSink_Writer.SomethingHappened
so that lMessage
receives __Message.Clone
but this has one drawback: the changes has to be done again after each regeneration of the Invk file. Hopefully in my case, this does not happen that often.
So, to sum up, I believe the fix requires the generator for events invocations to be changed to make sure lMessage
receives __Message.Clone
. And to make this easier, here are the steps that I’m suggesting ( patch file):
- Modify
IROMessageCloneable
so that it inherits from IROMessage
- Modify
IROMessageCloneable.Clone
so that it returns IROMessageCloneable
- Adapt
TROMessage.Clone
to return IROMessageCloneable
(simply change the type and remove the as
cast)
- Have
TROEventProxy
use IROMessageCloneable
instead of IROMessage
everywhere
- Adapt
TROEventWriter.Create
to the above change
- Adapt
TROEventInvoker.Create
to the above change
- Inside
TROEventReceiver.FireEvents
, change the type of the messageclone
variable to IROMessageCloneable
And after those changes are in, I believe a review is in order to change quite a few IROMessage
references to IROMessageCloneable
so as to avoid the costly as
operator calls. In particular those that are placed in locations that can be called quite often.