Getting a compile-time detection other than the one proposed by Nicholas Wilson will be extremely hard if not impossible, but assuming "background" really refers to the functions, and not to multiple threads (I saw no mention of threads in the question, so I assume it's just an odd wording) you could trivially use a global flag and a locker object, and either assert
or throw an exception. Or, output a debug message. This will, of course, be runtime-only -- but you should be able to very quickly isolate the offenders. It will also be very low overhead for debug builds (almost guaranteed to run from L1 cache), and none for release builds.
Using CaptureStackBackTrace, one should be able to capture the offending function's address, which a tool like addr2line
(or whatever the MS equivalent is) can directly translate to a line in your code. There is probably even a toolhelp function that can directly do this translation (though I wouldn't know).
So, something like this (untested!) might do the trick:
namespace global { int slow_flag = 0; }
struct slow_func_locker
{
slow_func_locker() { ++global::slow_flag; }
~slow_func_locker(){ --global::slow_flag; }
};
#indef NDEBUG
#define REALTIME if(global::slow_flag) \
{ \
void* backtrace; \
CaptureStackBackTrace(0, 1, &backtrace, 0); \
printf("RT function %s called from %08x\n", __FUNCTION__, backtrace); \
}
#define SLOW_FUNC slow_func_locker slow_func_locker_;
#else
#define REALTIME
#define SLOW_FUNC
#endif
foo_class::some_realtime_function(...)
{
REALTIME;
//...
};
foo_class::some_slow_function(...)
{
SLOW_FUNC;
//...
some_realtime_function(blah); // this will trigger
};
The only real downside (apart from not being compile-time) is you have to mark each and every slow and realtime function with either marker, but since the compiler cannot magically know which is what, there's not much of a choice anyway.
Note that the global "flag" is really a counter, not a flag. The reason for this is that a slow function could immediately call another slow function that returns and clears the flag -- incorrectly assuming a fast function now (the approach with critical sections suggested by xgbi might deadlock in this case!). A counter prevents this from happening. In presence of threads, one might replace int
with std::atomic_int
, too.
EDIT:
As it is clear now that there are really 2 threads running, and it only matters that one of them (the "fast" thread) does not ever call a "slow" function, there is another simple, working solution (example using Win32 API, but could be done with POSIX either way):
When the "fast" thread starts up (the "slow" thread does not need to do this), store the thread ID somewhere, either as global variable, or as member of the object that contains all the fast/slow functions -- anywhere where it's accessible:
global::fast_thread_id = GetCurrentThreadId();
The macro to bail out on "unwelcome" function calls could then look like:
#define CHECK_FAST_THREAD assert(GetCurrentThreadID() != global::fast_thread_id)
This macro is then added to any "slow" function that should never be called from the "fast" thread. If the fast thread calls a function that it must not call, the assert triggers and it is known which function is called.