We are developing a new interrupt system (in c++), to replace the old inconsistant one.
It works like this:
-
A programmer who wants to make his method interruptable accepts a
BreakFlagobject as an argument to the method. -
At points during the method the programmer checks
break_flag.is_set(), and exits and tidies up if it is.
We want to be able te test that all the orginal calls to break_flag.is_set() remain in the same order, after some change is made to the program. (We count multiple calls in the same loop as one call, so that two operations of different size appear to be the same)
Our first approach was to use the call stack to identify each instance. For example in the code:
void A(BreakFlag& flag) {
flag.is_set();
B(flag);
C(flag);
}
void B(BreakFlag& flag) {
flag.is_set();
C(flag);
}
void C(BreakFlag& flag) {
flag.is_set();
}
We would look at the call stack each time is_set is called and generate the following sequence for this method:
A
A, B
A, B, C
A, C
We could then use this to check that this sequence of checks occers every time we test the program, so that somebody could not come along and do this:
void A(BreakFlag& flag) {
flag.is_set();
B(flag);
C(flag);
}
void B(BreakFlag& flag) {
flag.is_set();
//C(flag); // I HAVE COMMENTED THIS OUT BECAUSE I DONT LIKE FLAG!
}
void C(BreakFlag& flag) {
flag.is_set();
}
Because it would result in the sequence:
A
A, B
A, C
Which would not match our original.
The problem that we have, is that the compiler sometimes optomizes in such a way that the call stack changes, which causes our proposed system to register a false failed test since the call stack is not what it is expecting, even though it is correct.
We could solve it by doing something similar to the following:
#define CHECK_FLAG(x) actually_check_flag(__LINE__,__FILE__,x)
Which would allow us to essentially ID each instance in code.
Other than that we cant think of any way to ID each call to .is_set that wont change in optomized code, can you?
You can use RAII to maintain a call stack manually. On entry to each function construct a guard object:
The constructor of
BreakFlagStackGuardpushes the function name"B"onto aflag.stack, while the destructor~BreakFlagStackGuard()(which will be called whenBreturns) pops it.Whatever the compiler does to optimise it will need to respect your guard objects so you can guarantee that the observed call stack reflects the source code.