0

I have seen this pattern pop up in some code I am working with and i understand what its intention is, and i understand why people like it but there is something about it that doesn't sit right with me. The pattern is the Guard class. The idea is you have some critical section which you preceed by creating an instance of Guard g(somemutex) which acquires (or attempts to acquire) the somemutex, then when g goes out of scope it release the mutex in g's destructor. This is really cool because it lets you put in returns anywhere in the functions and not need to worry about remembering to release the lock.

My concern is with optimization. If the items in the critical section are marked as volatile then the compiler can not move them across the function call of the constructor. Similarly if the item is veiwable outside the compileation unit (ie if a class has a public member, that member may reasonably changed outside the unit in which it is defined.) the compiler can not assume it can move the item. HOWEVER if the item is private to a class and not declared volatile then there is no reason for the compiler to think that any code outside the compilation unit can affect it, and there is no reason to believe that it must appear before or after the constructor of Guard. for instance:

void foo(int i){
    Guard<mutex> g(mymutex);
    notVolatilePrivateVar = i;
}
//gets compiled to look like...
void foo(int i){
    notVolatilePrivateVar = i;
    Guard<mutex> g(mymutex);
}

Now I am not asking if it is likely to happen, i am asking if there is a guarantee that is can not happen.

Similarly with multiple issue cpu's such things could happen, but i would think that it is even less likely to occur because the cpu doesn't know anything at all about the variables or what the function is.

EDIT: I have previously read that question Compiler reordering around mutex boundaries? but it doesn't address my qualm exactly. In that instance _field is a variable that is of unknown scope. In this case the scope is known. The variable is a private member of the class. In this case the only way to modify it is by calling public functions of the class. There is no way that the guard class can possibly have a pointer or reference to the calling function because we don't pass it into the constructor, this means the compiler can know with certainty that creating g can not modify the variable. Indeed it doesn't modify it.

Community
  • 1
  • 1
  • In general, compilers do not know what OS calls do. I do not think any compiler would move code across an OS call. Unless a compiler is doing entire program optimizations, the compiler does not usually know what code a function, ctor, or dtor contains so the compilers assume the worst. – brian beuning Sep 20 '13 at 21:53

1 Answers1

0

Since notVolatilePrivateVar is not LOCAL to the function foo, it must be treated as a "global" variable (there may be another instance of a pointer that has the same value as this that could access the notVolatilePrivateVar entity.

In other words, as long as the compiler doesn't know the exact implementation of mutex, OR the implementation of mutex has a "compiler barrier" in the implementation [which it should have if it's an inline function, otherwise it wouldn't be safe against this sort of thing].

A "compiler barrier" is a construct that tells the compiler that "you must not move stuff from after this point so that it goes before this point, or stuff before this point to after this point", and its purpose is exactly to prevent for example mutex implementations from "leaking" across simple variables that they are meant to protect.

In other words, the compiler must not "break" the promise of the Guard as long as the mutex is correctly designed.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227