kartik0000 / c-obix-tools

Automatically exported from code.google.com/p/c-obix-tools
0 stars 0 forks source link

Server does not invoke callback for adapter, if an other invocation of the callback is still executing #5

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
Suppose that my cot adapter wants to listen for point value changes, and
the callback operation to be performed involves real device i/o, that could
sometimes take 10 seconds to complete. 

Now, I update the value of the point by doing two consecutive writes with
short (less than 10 second) intervals. The result is that the callback is
not invoked at all for the latter write, but the value at the server has
changed!

What steps will reproduce the problem?

1. Write an adapter, which registers a listener for a point. Suppose that
the listener method looks like this:

int state_listener(int connectionId, int deviceId, int listenerId,·
    const char* newValue) {

    printf("new value %s\n", newValue);
    sleep(10);
    printf("cb ends\n");

    return OBIX_SUCCESS;
}

2. Use obix spy or similar to write a new value to the point, and then
rapidly write an other new value.

What is the expected output? What do you see instead?

From the output of the callback method I can see that the callback
invocation for the second write is lost, which is unexpected behavior. 

For callback invocations I would serial behaviour: the second callback
executed immediately after the first finishes. An other possibility would
be that I get an error for the second write.

Also, I would not expect that the point's value at the server can be
changed without my adapter "knowing about it"; because the callback is not
invoked for the latter write, the server has new value, but adapter doesn't.

The point's value at server should not change until the callback as
completed (successfully).

What version of the product are you using? On what operating system?

cot-0.1, Linux

Original issue reported on code.google.com by jan.ny...@gmail.com on 1 Mar 2010 at 9:58

GoogleCodeExporter commented 8 years ago
About expected behavior: I don't know if serial execution of callbacks is the 
right
thing, maybe the callbacks could be invoked asynchronously (in parallel) also. 
Maybe
it would be clearer that the client takes care of handling the concurrent 
callback
executions.

Original comment by jan.ny...@gmail.com on 1 Mar 2010 at 12:14

GoogleCodeExporter commented 8 years ago
First, let me explain how the subscribing works now in C oBIX Client library. 
The
current behaviour is that the library creates a Watch object at oBIX server and 
adds
to that Watch all values which are subscribed to by device adapter. Then the 
library
uses only ONE thread to poll changes from the server. So, it invokes
Watch.pollChanges at the server, parses output, and then invokes corresponding
listeners if some update is received. Therefore, if some update listener takes 
too
much time to complete (like in your example), all subsequent polling cycles are
blocked as well. There is a warning about such behaviour in documentation for
obix_registerListener function: All update listeners in such architecture 
should be
quick!

Now, two reasons, why some updates of the subscribed value can be lost:
1. Server doesn't consider that the value is changed in case when the new value 
in
write request is the same as it was (quick logical, isn't it? :). For example, 
if
someone writes value "true" to the object <bool val="true"/>, no updates will 
be sent.
2. If the value on server is changed more often than client sends its poll 
requests.
Watch.pollChanges always returns only current value of subscribed object, so if 
it
was changed several times since the last poll request, all those changes will be
ignored by the client. I consider that this is correct behaviour of oBIX server
(correct me if I'm wrong). Considering behaviour of the client library described
above, an update can be lost if subscribed value was changed twice while the 
polling
cycle at the client was blocked by some slow update listener. BUT, in any case,
client still should receive the last value of subscribed object when its slow 
update
listener will be completed and the library would be able to perform next poll 
cycle.
If not, then please describe the issue in more detail (log files with debug log
enabled would be nice).  

Original comment by Litvinov...@gmail.com on 2 Mar 2010 at 11:13

GoogleCodeExporter commented 8 years ago
I agree that in some cases it is not convenient, that polling cycle and update
listeners are executed in one thread: If some listener performs a slow 
communication
request to the physical device, all subscribing engine of the library is 
blocked as
well. 
Executing each update listener asynchronously would require generating a 
separate
thread for handling each update, which is not suitable for light-weight clients.

One option could be to use 2 threads: one for polling the server and storing all
received updates in a queue and another for invoking corresponding listeners for
updates from the queue. It will allow to perform server polling without delays, 
but
slow update listener will still block other listeners to be called in time. 

I would recommend for complex adapters to implement asynchronous update handling
right in adapter. The easiest way can be to use Periodic Task utility from C 
oBIX
Client library. The idea is to move actual update handling to a separate 
thread. For
instance, update listener could look like this:

int update_listener(int connectionId, int deviceId, int listenerId, const char* 
newValue)
{
    // newValue variable will be not available after this listener ends, so copy it
    char* updatedValue = strdup(newValue);

    // a check can be added whether async_update_listener is already scheduled and
    // not yet executed. If so, that old task can be removed using ptask_cancel 

    // execute async_update_listener in a separate thread right now and only once
    ptask_schedule(communicationThread, &async_update_listener, updatedValue, 0, 1);
    return OBIX_SUCCESS;
}

This will make update_listener quick enough not to block library's polling 
cycle. 

Original comment by Litvinov...@gmail.com on 2 Mar 2010 at 12:05