Professional OPC
Development Tools


UnsubscribeMultipleItems and UnsubscribeAllItems hang indefinately

31 Aug 2016 17:06 #4319 by support
Thank you. I actually think that the points 3&4 are quite objective, at least for most usage cases we know about. I can elaborate on that, and I would like to hear your arguments. We think that our design has good reasons to be the way it is, but are open to reasoning about the alternatives. If you do not mind discussing it, I will open a separate forum topic on that, because it does not directly influence your actual issues (the design is not something that can be changed in the same way, as a bug fix). Please let me know.

Regarding the hang in "unsubscribe call": You stated that you are not calling the "unsubscribe" from the event handler/callback. Therefore it should not hang. We need to get a reproducible scenario of this on our side, in order to be able to investigate (we have tests for the "unsubscribe", and it does not hang in our tests of course...). Cam you suggest how to get a repro? E.g. does the same thing happen with some smaller code, and some OPC Server we can get access to? (including our simulation server?)

I also do not fully understand the following part of your reply:

"...there is nothing to guarantee that:
a) The subscription will always be live and not require refreshing; "

QuickOPC is written in such a way that "subscribe" indicates your permanent intent to subscribe and stay subscribed, until you unsubscribe. QuickOPC is supposed to recover from any server or communication failures without your intervention or extra coding. The subscription may even not be achievable at the time of first "subscribe": The method call will still succeed, you will just get an event notification indicating a (possibly temporary) error. I can refer you to some more information about this, in the documentation. Are you saying that you are not seeing this behavior?

Best regards

Please Log in or Create an account to join the conversation.

31 Aug 2016 09:38 #4317 by sebpinski
With the 4 points listed in 13.9, I agree with the issues outline in 1&2 but 3&4 are completely subjective and based on implementation. Luckily due to the architecture of our system 1 is also circumvented as we can reprocess queued data on the remote OPC server and our code identifies duplicates.

I think the difficulty I have with working using the suggested best practices of passing a 'state' argument is that there is nothing to guarantee that:
a) The subscription will always be live and not require refreshing;
b) The previously dropped subscription has actually dropped;

Unless of course you're suggesting that I add a 'state' object that is a unique int just like the handle, but the difference being that because of the issues in 13.9.1, I will know the 'state' before the subscription is created?

Anyway, regarding the unsubscription. You've used a lot of double negatives so I'm struggling to understand whether the case will/won't cause the unsubscription to hand. I have not set anything in the SynchronizationContext, here is my constructor for the singleton class:
        private OpcDaExtensions()
            //// Prevent creation of subscriptions on read
            EasyDAClient.SharedParameters.Topic.SlowdownWeight = 0.0f;
            EasyDAClient.SharedParameters.Topic.SpeedupWeight = 0.0f;
            _opcClient = new EasyDAClient() { Isolated = true };
            //// Force asynchronous read/writes
            _opcClient.InstanceParameters.Mode.DesiredMethod = OpcLabs.EasyOpc.DataAccess.Engine.DAReadWriteMethod.Asynchronous;
            _opcClient.InstanceParameters.Mode.AllowAsynchronousMethod = true;
            _opcClient.InstanceParameters.Mode.AllowSynchronousMethod = false;
            //// Reduce timeouts on Async methods
            _opcClient.InstanceParameters.Timeouts.ReadItem = 60000;
            _opcClient.InstanceParameters.Timeouts.WriteItem = 60000;
            //// Prevent creation of subscriptions on read
            _opcClient.InstanceParameters.UpdateRates.ReadAutomatic = Timeout.Infinite;
            _opcClient.InstanceParameters.UpdateRates.WriteAutomatic = Timeout.Infinite;

All subscribes, unsubscribes, reads and writes are all methods within this class that use the single initialised EasyDaClient. I am not calling the unsubscribe from the event handler/callback.

Please Log in or Create an account to join the conversation.

30 Aug 2016 14:09 #4309 by support
OK, there are at least two things here:

First, the "hang" when the unsubscribing. This can be split into three possible cases:

a) you are not calling the UnsubscribeXXXX from the event handler. In this case, it should not hang.

b) you are calling the UnsubscribeXXXX from the event handler or callback, are you have made sure that the SynchronizationContext of the EasyDAClient object is set to a context that guarantees a thread switch. In this case, it also should not hang.

c) you are calling the UnsubscribeXXXX from the event handler or callback, but the SynchornizationContext is not set at all, or is such that it does not guarantees a usage of a separate thread. This case is not supported and the UnubscribeXXXX call may hang, this is by design. In case of Windows service, unless you specifically set the SynchronizationContext, it will be a null reference, and therefore calling the UnsubscribeXXXX from the event handler or callback may not work.

I need to know which of the three above cases a/b/c we are dealing with please.

Second, the usage or availability of Handle from inside the event handler or callback. This is explained, among others, in the Best Practices section of the Concepts document. Below is how it looks in version 5.35.

13.9 Use the state instead of handles to identify subscribed entities

The handles returned from any EasyXXClient.SubscribeXXXX methods are only meant for use as input arguments to other methods that manipulate the existing subscriptions – such as EasyXXClient.ChangeXXXX or EasyXXClient.UnsubscribeXXXX.

If you need to identify the subscribed entities inside event handlers, use the ‘state’ argument that can be passed to EasyXXClient.SubscribeXXXX methods (or a State property on XXXXArguments objects). You can pass any type of object in the state, as it is of type System.Object (and then cast the System.Object back to the original type in the event handler).

Why shouldn’t the handles be used? There are multiple reasons for that:

1. The SubscribeXXXX methods are asynchronous. There is no guaranteed relation between the moment the SubscribeXXXX method returns, and the moment when events start flowing into your event handler. The events can start flowing before the methods returns. Or, they can start flowing after the method returns, but before you get a chance to process the returned handles, and store them somewhere where the event handler can access them.

2. Similarly, UnsubscribeXXXX methods are asynchronous as well, and therefore you cannot know precisely when any event handle became invalid. You may (temporarily) receive events for a previously returned handles, even after the UnsubscribeXXXX method finishes.

3. You will need to set up and properly maintain an additional data structure – something that keeps a correspondence between the handles returned (whose values you do not know in advance), and your application data that relate to these handles. Typically, this data structure is a dictionary. Maintaining and accessing this additional data structure means extra code, and inefficient access (application data must be looked up by handle from inside the event handler).

4. Using the handles returned from SubscribeXXXX in an event handler requires coordination of the access to a data structure where the handles are stored – i.e. an inter-thread synchronization. This leads to complicated and often buggy code, with risk of deadlocks or invalid data being accessed.

All these disadvantages can simply be overcome by passing the application data needed into the SubscribeXXXX method as a state object, and then casting the State to the appropriate type and using directly inside the event handler.

In short, in most cases, the usage of Handle in unsafe, complicated and inefficient. It should be easier, safer and more efficient to use your *own* data structure, and pass it as the State into the subscriptions. By the way, this is how efficient protocols that require bi-directional calls (including OPC) usually work anyway. For example, in OPC Data Access, the client passes a "Client Handle" (this roughly corresponds to our State) to the server, and receives back a "Server Handle". When it later wants to manipulate this thing, it uses the Server Handle - because that's what the server can easily understand. But when the server sends the info back to the client, it ships it with the Client Handle - because that's what the client easily understands. There is no magic here - it is actually far more easier this way.

In version 5.40 the Handle is already gone altogether.

Best regards

Please Log in or Create an account to join the conversation.

25 Aug 2016 19:01 #4307 by support
Please be patient, I will reply when I am back in the office, next Tuesday or Wednesday.

Please Log in or Create an account to join the conversation.

25 Aug 2016 08:53 #4306 by sebpinski
I'm sure this issue has been reported before, but I'm finding it to be a nuisance again. The version of the DLLs that I'm using is 5.35.1173.1. This is for a windows service written in C#.

I'm keeping a concurrent dictionary of tags that I've explicitly subscribed to. The reason why its a dictionary (instead of a concurrentbag) is due to storing the connection handle returned by the subscribe. We're connecting to both DA and UA devices and both the easy clients are wrapped up in singleton classes so that we only have to instantiate one client of each with the isolated option.

Occasionally the subscriptions have to be refreshed, in the sense that the items we want to subscribe to are changed. I'd like to use either technique of unsubscribing all tags and resubscribing, or just unsubscribing the tags that are not needed in one call, but the UnsubscibeMultipleItems and UnsubscribeAllItems methods still hang. So we therefore do not unsubscribe anything, instead just resubscribe to the new list of tags.

We're therefore storing the handle, so that when a subscription arrives, we can check with the ConcurrentDictionary that the subscription is one that we're actually expecting, and throw away any events that are coming in, but no longer needed. As an additional side note, until the hanging is rectified, we request that you do not remove the handle from the event args, why it is deprecated is beyond me, what use is the handle provided from the subscription if you cannot access the handle on an incoming event?

I'm happy to provide the code if this would help.

Please Log in or Create an account to join the conversation.

Moderators: support
Time to create page: 0.179 seconds


 Recommend this on Google