Monday, November 20, 2006

Cross thread calls in native C++, #4

If you haven't read the previous entries in this series, head back to http://einaros.livejournal.com/#einaros3782 before continuing.

There are a few restrictions on the use of a framework such as the one described here. Some are merely points to be wary of, while others are showstoppers.

The parameters passed to a function which will be called from another thread, should not use the TLS (Thread Local Storage) specifier, that goes without saying. A variable declared TLS (__declspec(thread)) will have one copy per thread it's accessed from. In terms of the previous example, the main thread would not necessarily see the same data as testThread, even with the value passed through the synchronized call mechanism to testFunction. In short: there's nothing stopping you from passing TLS, but through doing so you are bound to see some odd behavior. The general guideline is to be thoughtful. Don't pass anything between threads without knowing exactly what the consequences are. Even though the mechanism, or rather principle, of cross thread synchronized calls goes to great lengths to keep the task simple; there are always ways to stumble.

A couple of guidelines and requirements regarding parameter passing and returning, in an example scenario where Thread A does a synchronized call to Function F through Thread B:

  • If Function F has to return pointers or references, make them const so they cannot be touched by Thread A. Even when they are const, Thread B can free them, and thus make reads from Thread A crash. Don't use pointers or references unless you are absolutely sure this won't happen. Returned pointers or references belong to Thread B.
  • If Function F accepts pointers or references as parameters from Thrad A, make sure that they aren't referenced by Thread B, neither read nor written, once F returns. Passed pointers or references belong to Thread A.
  • Class types returned from Function F to Thread A must provide a public copy constructor, either compiler generated or user implemented. 
  • At the time of writing, the framework does not support exception transport between the threads. If Function F happens to throw an exception, this will be captured by the framework. The CallScheduler will then throw a new, unrelated, exception back to Thread A. Since this makes it difficult for Thread A to track which exception really occurred in Function F, which leads me to advise against such throwing in the first place.

I'm currently wrapping up the framework, and I'll post a demo project here within the next few days. There are still some remaining points on my TODO-list, but  I'll disregard most of them for the time being.

0 comments: