RemObjects DA Service threading question

net

(Tim Mayer) #1

We have a RemObjects DA service built using RemObjects Service Build.

One method appears as follows:

public virtual bool GetDevicesByProjectId(string projectId, out RemObjects.SDK.Types.Binary deviceList, out string vErrorDesc) {
        **RemObjects.SDK.IMessage @__LocalMessage = this.@__GetMessage();**
        try {
            @__LocalMessage.InitializeRequestMessage(ClientChannel, "ProjectManagerWebService", ActiveInterfaceName, "GetDevicesByProjectId");
            @__LocalMessage.WriteAnsiString("projectId", projectId);
            @__LocalMessage.FinalizeMessage();
            ClientChannel.Dispatch(@__LocalMessage);
            bool _Result = @__LocalMessage.ReadBoolean("Result");
            deviceList = ((RemObjects.SDK.Types.Binary)(@__LocalMessage.Read("deviceList", typeof(RemObjects.SDK.Types.Binary), RemObjects.SDK.StreamingFormat.Default)));
            vErrorDesc = @__LocalMessage.ReadAnsiString("vErrorDesc");
            return _Result;
        }
        finally {
            this.@__ClearMessage(@__LocalMessage);
        }
    }

Everything seems to work fine. However, very rarely, we encounter the following error:

BinMessage: Unexpected end of stream.

With the following stack trace:

 at RemObjects.SDK.BinSerializer.ReadInt32()
 at RemObjects.SDK.BinSerializer.ReadBoolean(String name)
 at RemObjects.SDK.Message.ReadBoolean(String name)
 at ..ProjectManagerWebService.ProjectManagerService_Proxy.GetDevicesByProjectId(String projectId, Binary& deviceList, String& vErrorDesc)

We are using multiple threads within our application, so I’m wondering if the call to __GetMessage() is returning a common object, that would ultimately be shared by multiple threads. If that’s the case, then I could see how multiple threads all making service operation calls simultaneously could result in a conflict - and eventually an odd exception.

Thanks,

Tim


(antonk) #2

The __GetMessage code is very simple:

	protected virtual IMessage ___GetMessage()
	{
		if (this.CloneMessage)
		{
			return this.fMessage.Clone();
		}

		return this.fMessage;
	}

So you can just set CloneMessage to true so the message object will always be cloned. For async service proxies the message object is cloned always.

I’ll log an issue to double-check and ensure that the CloneMessage is set to true by default for sync service proxies as well.


(RemObjects) #3

Thanks, logged as bugs://80959


(Tim Mayer) #4

Thanks. Setting CloneMessage to true works great.


(antonk) #5

Forgot to ask - which exactly client channel type do you use?


(Tim Mayer) #6

Under normal conditions we use LocalClientChannel, but it is configurable in our software to use NamedPipeClientChannel.


(antonk) #7

Thanks for the info.


(RemObjects) #8

bugs://80959 got closed with status fixed.


(estebanp) #9

Shouldnt this change be applied to the Delphi side of things?

Looking at the code generated I think the synchronous versions TROPROXY when created passing a channel and a message (as we do ours) creates them without clonning it. The ones created using a IRORemoteService clones them or not depending on the message setting as it should be.

(uROProxy)
constructor TROProxy.Create(const aRemoteService: IRORemoteService);
begin
{$IFNDEF TROPROXY_IGNORES_REMOTESERVICE_SERVICENAME}
Create(aRemoteService.ServiceName, aRemoteService.Message, aRemoteService.Channel);
{$ELSE}
Create(aRemoteService.Message, aRemoteService.Channel);
{$ENDIF}
fCloneMessage := aRemoteService.CloneMessage;
end;

Compare to
(uROBaseProxy)

constructor TROBaseProxy.Create(const aMessage: IROMessage; const aTransportChannel: IROTransportChannel);
begin
inherited Create;
fMessage := pointer(aMessage);
fTransportChannel := pointer(aTransportChannel);
fCloneMessage := False;
end;

uROAsync inherits the above too.

Same goes for services created using an uri.


(EvgenyK) #10

fCloneMessage parameter is used in __GetMessage method:

function TROProxy.__GetMessage: IROMessage;
begin
  if fCloneMessage then
    Result := (__Message as IROMessageCloneable).Clone
  else
    Result := __Message;
end;

(estebanp) #11

Hi Evgeny,

I’m confused on how this works and correct me if im mistaken, the only place where a developer can access and alter the property CloneMessage is on the TRORemoteService class. But If Im not using that class and I’m creating the proxy via a URI or specifying directly the transport and channel (as seen on uROBaseProxy) the fCloneMessage private property is set to false by default as seen above and as you mentioned everything else works based on this private property.


(EvgenyK) #12

Hi,

In general, this option is needed only for TRORemoteService class.

in all other cases, you can pass cloned message w/o that property:

xxx :=  CoNewService.Create((Message as IROMessageCloneable).Clone, Channel);

(estebanp) #13

Thank you Evegeny for the quick response.

so yes, back to Anton’s quote.

So you can just set CloneMessage to true so the message object will always be cloned. For async service proxies the message object is cloned always.

I’ll log an issue to double-check and ensure that the CloneMessage is set to true by default for sync service proxies as well.

Shouldnt we have that option set by default to true as Anton is saying he did on all the generated proxies? Is there a harm on not doing so? It is not very obvious for the average user that you have to cast an interface on the instance and then call a function and pass it to the create proxy to be able to make it work.

I only see the positive side, but probably Im missing the negative, what problems will cause to have it on true by default on the other options?


(estebanp) #14

Looking at the code it seems that the intended purpose was to actually use the fCloneMessage property for all circumstances as a flag for message cloning instead of creating a clone of the message and pass it as part of the constructor.

We built highly threaded systems with hundred thousands of requests/messages, etc, concurrency issues are common. Anything to alleviate them helps. But again, I dont know if the message ids are kept or other variables that could mess things up.


(EvgenyK) #15

cloned message takes owner’s properties, because it calls Assign:

function TROMessage.Clone: IROMessage;
begin
  result := TROMessageClass(ClassType).CreateRefCountedClone(self) as IROMessage;
end;

constructor TROMessage.CreateRefCountedClone(iMessage: TROMessage);
begin
  Create();
  fReferenceCounted := True;
  Assign(iMessage);
end;