I have been researching and messing around with reentrancy/thread safety mainly because of a few concepts I am working on implementing. I have come up with a theory, and was just wondering if anyone who has experience with re-entrancy issues could give their insights and knowledge on the concept...
Considering that the main problem lies when a function is being called via multiple threads (and a lot of the examples used when explaining these issues tends to even come from a signal interrupt calling the method while already in action) while that method is using local static and/or global data resulting in multiple calls attempting to change and/or manipulate the data in question. That being said, the main advice for creating re-entrant functions is to ensure that this type of data is not used or when it is used have a mutex and/or semaphore locking mechanism protecting the read/write access to said data. Now the locks would generate thread-safe functions, but does not guarantee a re-entrant function. Now I know the following concept kind of goes against the above noted advice I have read, but I am curious to see if directly going against said advice can provide a fruitful method of ensuring re-entrant functions?
int some_func(int someIndexParam)
{
static bool isActive = false; // start it off as false, no other instances calling it immediately
if (isActive)
return ALREADY_RUNNING;
isActive = true;
// DO ALL YOUR WORK HERE
isActive = false;
return SUCCESS;
}
In the above concept, I am using one of the big "no-nos" in order to actually determine whether or not it would be safe to continue. Now I know that this could still potentially lead to a race condition in the rare circumstance where such a function is called almost back-to-back with another call to the same function where-as the isActive value may not be appropriately updated in time to stop the second call from proceeding, but this I would consider to be a small factor unless the function is quite frequently called by a large number of threads, but I am curious to hear other's opinions on this method of handling re-entrancy issues?
Thanks
TL;DR: it's a big no-no for a reason.
Multiple reasons in fact:
Compiler is free to rearrange things widely, as long as so called "observable behavior" as specified by the language is preserved. Language specification allows it to completely remove isActive assignments. And good compilers will do that!
No, it won't "potentially lead to a race condtition", it is a race condition. There is no "potentially" and no "leading". And remember that computers are capable of executing multiple billions operations per second (even your smartphone is), and successful programs are typically run significantly more than once and typically on significantly more than one computer. So even if probability of some event seems extremely remote, like once in trillion times, it is virtually certain it will happen, and will happen way sooner than you think.
This code is wrong for either multithreading (use mutexes or other proper synchronization primitives) or reentrant singnal handling (use proper sig atomic types, and use proper singal unmasking, etc.)
What you have done is create a make-shift mutex. Using mutexes is a common and valid and technique to protect shared resources. As you realized, as implemented yours is flawed and may fail to prevent invalid accesses in "rare" circumstances. Do not feel bad as that kind of code is notoriously difficult to get right. Do not fall into the trap of thinking concurrent accesses of resources is unlikely. Problems like the above produce horribly difficult to debug problems. Mysterious failures that only occur on random alternate Tuesdays or demoing for an important client.
All this is difficult. An over-zealous compiler might optimize out setting your flag because all paths through the code un-set it anyway. Why do the extra work? Declaring isActive as volatile will prevent the compiler from optimizing away your protection but that does not stop hardware which may issue writes out-of-order. The reason that kind of protection is a "no-no" is that it is hard. You have to understand a lot of platform specific issues such as memory barriers and CAS instructions.
TL;DR You have an excellent idea, but a better implementation is needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With