7

I am working on very large c++ project, it has lot of real time critical functions and also lot of slow background functions. These background functions should not be called from time critical functions. So is there way to detect these background functions being called from critical functions? compile time would be good but anyway I like to detect before these background functions. More info, both slow and critical functions are part of same class and share same header.

Some more information, Critical functions runs under really faster thread (>=10KHz) slower one runs under different slower thread (<=1KHz). Class member variables are protected using critical sections in slow functions since both use same class member variables. That's reason calling slow functions in critical functions will slowdown overall system performance. That's reason I like to find all these kind of functions automatically instead of manual checking.

Thanks....

user2409054
  • 131
  • 1
  • 7

4 Answers4

4

You need to leverage the linker. Separate the "realtime" and slow functions into two modules, and link them in the correct order.

For example, split the files into two directories. Create a lib from each directory (ranlib the object files together) then link your final application using:

c++ -o myapp main.o lib1/slowfns.a lib2/realtime.a

If you try to call anything from slowfns.a in realtime.a, depending on the compiler, it will fail to link (some compilers may need options to enforce this).

In addition, this lets you easily manage compile-time declarations too: make sure that the headers from the slowfns library aren't on the include path when compiling the "realtime" funcitons library for added protection.

Nicholas Wilson
  • 9,435
  • 1
  • 41
  • 80
  • This doesn't answer the question of detecting when background functions are running. – metalhead May 22 '13 at 10:58
  • Thanks Nicholas. Good suggestion. Currently I have class declaration in single header file and same class will have slow and critical functions and declared in same header file. Slow and critical functions implementations are in different files but they share same header file since both are part of same class. Also we can not split the class header. Because of this reason I may not be able to do what you have suggested. Do you have any more ideas on my current scenario? Thanks. – user2409054 May 22 '13 at 11:00
  • You'll still get the link-time protection with your setup. ranlib the two object files you're producing into two different .a files. (Just checking, what compiler is this? I'm assuming gcc/icc/xlc or similar.) – Nicholas Wilson May 22 '13 at 12:06
  • I am using Visual Studio 2012. Compiled to two separate object files but linker links them together. I also checked on vxworks with diab. – user2409054 May 22 '13 at 12:33
  • Ah, sorry. I know nothing about VxWorks, and I don't know of a way to enforce these sorts of dependencies at link-time in Visual Studio. For VxWorks, if you have the option of using GNU ld that would do it (you could then throw away the binary, and just do that step for the purposes of failing the build if the wrong function dependencies are found). On VS, all I can think of is splitting the objects into separate projects and headers and preventing the realtime code from including the slow code header. See this answer perhaps too: http://stackoverflow.com/questions/45135/linker-order-gcc – Nicholas Wilson May 22 '13 at 12:51
  • 1
    VS doesn't care about the order of libraries on the linker command line, it always searches everywhere. – Sebastian Redl May 23 '13 at 11:44
3

Don't know how to do that at compile time, but for runtime, maybe use a mutex?

static Mutex critical_mutex;
#define CALL_SLOW( f ) if( critical_mutex.try_lock() == FAIL) \
   printf("SLOW FUNCTION " #f" called while in CRITICAL\n");\
   f

#define ENTER_CRITICAL() critical_mutex.lock()
#define EXIT_CRITICAL() critical_mutex.unlock()

Whenever you use a slow function while in a critical section, the trylock will fail.

void slow_func(){
}

ENTER_CRITICAL();
CALL_SLOW( slow_func() );
EXIT_CRITICAL();

Will print:

SLOW FUNCTION slow_func() called while in CRITICAL

If you need speed, you can implement your lightweight mutex with interlockedincrement on windows or __sync* functions on linux.
Preshing has an awesome set of blog posts about this HERE.

Gui13
  • 12,993
  • 17
  • 57
  • 104
  • It's good idea but I will have to check this mutex check before calling each background function. I have thousands of background functions and it might add performance overhead with mutexs and extra checking. I will wait for more ideas. Thanks for your reply. – user2409054 May 22 '13 at 15:18
  • You can use [interlockedincrement](http://msdn.microsoft.com/en-us/library/windows/desktop/ms683614(v=vs.85).aspx) on windows or [__sync*](http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Atomic-Builtins.html) functions on linux to have a very ligtweight mutex. – Gui13 May 23 '13 at 06:56
3

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.

Damon
  • 67,688
  • 20
  • 135
  • 185
  • This is very similar to an idea I was going to suggest, but I was going to have the counter tell me if I'm in a critical path. Surely it's fine to call a critical function from a slow one, just not vice versa. Right? – Adrian McCarthy May 22 '13 at 17:18
  • @AdrianMcCarthy: I would, too, assume that since "critical" functions are fast, there should be nothing against calling that from a "slow" function. Hence only the one check. But of course one could trivially add the other way around, too. – Damon May 22 '13 at 17:27
  • 1
    My point is that the check you have is the less useful one. You should turn the logic around. – Adrian McCarthy May 22 '13 at 17:30
  • Thanks Damon and Adrian. More information, Critical functions runs under really faster thread (>=10KHz) slower one runs under different slower thread (<=1KHz). Class member variables are protected using critical sections in slow functions since both use same class member variables. That's reason calling slow functions in critical functions will slowdown overall system performance. That's reason I like to find all these kind of functions automatically instead of manual checking. – user2409054 May 23 '13 at 08:23
  • @AdrianMcCarthy: Oh indeed, you're right... good catch. I got it exactly the wrong way around (fast called from slow, not slow called from fast). But I guess the principle is still clear -- without rewriting the whole code, replacing slow with fast everywhere. :-) – Damon May 23 '13 at 09:14
  • 2
    @user2409054: So basically, you have 2 threads of which one calls slow functions (and _could_ call fast functions too, no harm there), and another thread that is supposed to _only ever_ call fast functions. For this, none of the proposed solutions (including mine) will work. However, one could make it work by using TLS for the counter. TLS is necessary because otherwise, there is no way of knowing which thread incremented/decremented the counter. A mutex/semaphore, likewise, would be no good, since any tread could have acquired it. TLS is a slight overhead, but it's only in debug mode anyway. – Damon May 23 '13 at 09:19
  • @Damon: Ideally faster functions should call faster functions only and slow functions should call slow only. Like to start with first then later I might to look 2nd one also. As I mentioned slow functions will have critical sections to protect data so if we call fast function in slow function then data is not protected correctly. – user2409054 May 23 '13 at 09:28
  • The critical section object would have to span the entire iteration, to make counting which functions are called work reliably (but then, threads run strictly exclusive, and the slow thread might starve the fast one). A scoped counter (like described above) _with TLS_ will solve any thread issues, since each tread only counts its own functions. Something like `TlsSetValue(++TlsGetValue(index));` would do. Alternatively, you could simply store the thread ID in TLS and just add an `assert` to any function that you do _not_ wish to be ever called from the fast thread. – Damon May 23 '13 at 09:46
  • @Damon, thanks, this solution is better than all others...Let me try this. – user2409054 May 23 '13 at 10:57
  • @user2409054: Wait, I must be drunk or something... you don't even need TLS for that. `GetCurrentThreadId` is already thread-specific, so you **really** only need to store the fast thread's ID once somewhere... will update again. – Damon May 23 '13 at 11:20
  • @Damon: Yes that is understandable...I do not use TLS at all... I just need fast and slow thread id's. Do you have any more thoughts on compile time findings using macros? I know it's silly but first preference given for compile time findings. – user2409054 May 23 '13 at 11:25
  • I'm inclined to bet that a compile-time check for this problem is entirely impossible, since the compiler is agnostic of threads. – Damon May 23 '13 at 11:28
  • @Damon: If all the code is in one file then with macros it is possible to detect at compile time itself but it is really really cumbersome. But any way thanks for your posts. I went ahead and implemented simple priority comparison. – user2409054 May 25 '13 at 14:57
1

If you're free to modify the code as you wish, there's a type-system-level solution that involves adding some boilerplate.

Basically, you create a new class, SlowFunctionToken. Every slow function in your program takes a reference to SlowFunctionToken. Next, you make SlowFunctionToken's default and copy constructors private.

Now only functions that already have a SlowFunctionToken can call slow functions. How do you get a SlowFunctionToken? Add friend declarations to SlowFunctionToken; specifically, friend the thread entry functions of the threads that are allowed to use slow functions. Then, create local SlowFunctionToken objects there and pass them down.

class SlowFunctionToken;

class Stuff {
public:
  void FastThread();
  void SlowThread();

  void ASlowFunction(SlowFunctionToken& sft);
  void AnotherSlowFunction(SlowFunctionToken& sft);

  void AFastFunction();
};

class SlowFunctionToken {
  SlowFunctionToken() {}
  SlowFunctionToken(const SlowFunctionToken&) {}

  friend void Stuff::SlowThread();
};

void Stuff::FastThread() {
  AFastFunction();
  //SlowFunctionToken sft; doesn't compile
  //ASlowFunction(???); doesn't compile
}

void Stuff::SlowThread() {
  SlowFunctionToken sft;
  ASlowFunction(sft);
}

void Stuff::ASlowFunction(SlowFunctionToken& sft) {
  AnotherSlowFunction(sft);
  AFastFunction(); // works, but that function can't call slow functions
}
Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157