No timeout with netPost and unshared reference-variables

Discussions about product bugs & problems!
Note: This is no replacement for the Official ETM Support!
3 posts • Page 1 of 1
a.decelis
Posts:30
Joined: Thu Jun 25, 2015 6:42 pm

No timeout with netPost and unshared reference-variables

Post by a.decelis »

Hi,

WinCC OA 3.14 P13 - Linux

I am netPosting to a server which occasionally restarts, so netPost gets stuck. I searched the doc and found no timeout parameter for this function.
The netPost queries run in a separate WCCOActrl manager.

Tried to do program async (and cancellable) netPosts in separate thread, via startThread. So the calling thread will start another, which will do the netPost, whilst the first one regularly polls the result variables (polling thread acting like sort of a pulsed-active barrier). waitThread is a no-no, because does not have a timeout either. (Or does it?)
A problem emerges here: there is not a (straight) way, or I have not found it, to share chosen variables (memory) between two threads.

The following code prints "1" to the log.

Code: Select all

int myValue;

main() {
    myValue = 1;
    waitThread(startThread("myFunc", myValue)); // pass by reference does not work through start thread. Probably to avoid the non-sense if main() finished before the started thread, and the variable would've gone out of scope.
    DebugN(myValue);
}

void myFunc(int& val) {
    val = 42;
}
My thoughts so far:
  • Use function-declared (local) variables -> don´t get shared
  • Use library variables -> probably my next step: declare some mapping in a .ctl file, and use synchronized access to support simultaneous netPosts (each call gets a unique key used to access the mapping).
  • Use datapoint(s) for synchronization: the whole point of trying to use shared variables is to avoid unrelated communications with the event manager.
I'll update about my conclussions here. Meanwhile, any thoughts on the topic (timeout for netPost and memory sharing between threads) will be appreciated.

mkoller
Posts:741
Joined: Fri Sep 17, 2010 9:03 am

Re: No timeout with netPost and unshared reference-variables

Post by mkoller »

1) if you want variables which are seen by all threads of a script, simply define the variable outside of any function, e.g. before main(), like:

Code: Select all

int sharedVar;
main()
{
  DebugN(sharedVar);
  startThread("work");
}
work()
{
  DebugN(sharedVar);
}
2) to your original problem with netPost():
- yes, there is no timeout you can set. The system uses the TCP stacks timeout, which can be long (2 hours or so)
- yes, waitThread() also has no timeout

you can do the following, using a semaphore which can wait (no busy loop!) for a given time:

Code: Select all

main()
{
  int tid = startThread("postThread");

  const time TIMEOUT = 5;  // seconds
  if ( semAcquire("post", 1, TIMEOUT) == 1 )  // timeout
  {
    DebugN("thread still running - stopping it");
    stopThread(tid);
  }
}

postThread()
{
  DebugN("netPost result", netPost("http://192.168.129.22/test", "data"));
  semRelease("post");
}

a.decelis
Posts:30
Joined: Thu Jun 25, 2015 6:42 pm

Re: No timeout with netPost and unshared reference-variables

Post by a.decelis »

Well, finally managed to do it. Thanks Martin Koller for your (alway) useful hints towards the correct solution.

Just a few comments/thoughts on the matter, just in case it could help someone else in the future.

stopThread can lead to race conditions, because thread ids are not unique.

This code:

Code: Select all

main()
{
  int tid = startThread("postThread");

  const time TIMEOUT = 5;  // seconds
  if ( semAcquire("post", 1, TIMEOUT) == 1 )  // timeout
  {
    DebugN("thread still running - stopping it");
    stopThread(tid);
  }
}

postThread()
{
  DebugN("netPost result", netPost("http://192.168.129.22/test", "data"));
  semRelease("post");
}


is subject to a race condition: if the "postThread" ends just after the timeout, the "main" thread will try to stop a thread that no longer exists. Furthermore, as thread ids are not unique but very easily repeatable, "main" thread could end stopping another,unrelated thread.

To solve this, I used the synchronization keyword over an unsigned variable (as is explained in the documentation): the "post" thread-id is saved to another variable. Then, when timeout ocurrs, the "main" thread has to re-read the thread-id and stop it, whilst with the "postThread" has to "erase" it's thread-id from the shared variable.
Thread-id re-reading & stopping, as well as Thread-id erasing, in this shared variable, is made atomic via use of the synchronized keyword: this way, the race condition disappears.
To ease storing and retrieving the thread-ids, I generated unique(*) transaction ids, thus avoiding the "repeating-thread-ids" problem.

(*) ok, not unique actually, but it's mathematically guaranteed that a transaction id won't be repeated while in use.

Two more catches: first one, the "main" thread needs to know if netPost succeeds and, in that case, what is the gotten result.

Second catch: using a single "post" semaphore , only one "netPost" operation can be made simultaneously. And I needed more to be concurrently made.

To solve this last, I used a dynamic array of a fixed size (with synchronized accesses only via synchronized keyword), in which bookeeping of used semaphores is made. This way, I can define maximum number of well-known semaphores and launch that many simultaneous netposts. Any new netPost query, first search the array of available semaphores and picks the first available.

Final solution involved using: a (ulong) seed for the transaction ids, an (unsigned) mutex for synchronization, an array to register the available semaphores, and three mappings:
[ol][*]transaction id -> thread id (int)[*][*]transactionId -> netPost result (int)[*][*]transactionId -> data gotten with netPost[*][/ol]


Another solution could've been expanding the functions with a C++ external DLL. I just thought this particular approach would take less time to implement. And that seems to have been the case.

Thanks for reading.

3 posts • Page 1 of 1