While making an edit to a class with a long history, I was stymied by a particular habit of the architect of wrapping his va_start -> va_end sequence in a mutex. The changelog for that addition (which was made some 15 years ago, and not revised since) noted that it was because va_start et. all was not reentrant.
I was not aware of any such issues with va_start, as I always thought it was just a macro for some stack-pointer math. Is there something here I’m not aware of? I don’t want to change this code if there will be side-effects.
Specifically, the function in question looks a lot like this:
void write(const char *format, ...)
{
mutex.Lock();
va_list args;
va_start(args, format);
_write(format, args);
va_end(args);
mutex.Unlock();
}
This is called from multiple threads.
As far as being serially-reentrant (ie., if
foo()usesva_startis it safe forfoo()to callbar()which also usesva_start), the answer is that’s fine – as long as theva_listinstance isn’t the same. The standard says,So, you’re OK as long as a different
va_list(referred to above asap) is used.If by reentrant you mean thread-safe (which I assume you are, since mutexes are involved), you’ll need to look to the implementation for the specifics. Since the C standard doesn’t talk about multi-threading, this issue is really up to the implementation to ensure. I could imagine that it might be difficult to make
va_startthread-safe on some oddball or small architectures, but I think if you’re working on a modern mainstream platform you’re likely to have no problems.On the more mainstream platforms as long as a different
va_listargument is being passed to theva_startmacro you should have no problem with multiple threads passing through the ‘same’va_start. And since theva_listargument is typically on the stack (and therefore different threads will have different instances) you’re generally dealing with different instances of theva_list.I think that in your example, the mutexes are unnecessary for the varargs use. However, if the
write(), it certainly would make sense for awrite()call to be serialized so that you don’t have multiplewrite()threads screwing up each other’s output.