Ottenere un rilevamento in fase di compilazione diverso da quello proposto da Nicholas Wilson sarà estremamente difficile se non impossibile, ma presupponendo che lo "sfondo" si riferisca realmente alle funzioni e non a più thread (non ho visto alcun riferimento ai thread in la domanda, quindi presumo che sia solo una formulazione strana) potresti banalmente usare una bandiera globale e un oggetto armadietto, e assert
o lanciare un'eccezione. Oppure, emettere un messaggio di debug. Questo, ovviamente, sarà solo runtime - ma dovresti essere in grado di isolare molto velocemente i criminali. Sarà anche un sovraccarico molto basso per le build di debug (quasi garantito per l'esecuzione dalla cache L1), e nessuna per le build di rilascio.
Utilizzando CaptureStackBackTrace, si dovrebbe essere in grado di acquisire l'indirizzo della funzione incriminata, che uno strumento come addr2line
(o qualunque sia l'equivalente MS) può tradurre direttamente in una riga del codice. Probabilmente esiste anche una funzione di toolhelp che può eseguire direttamente questa traduzione (anche se non lo saprei).
Quindi, qualcosa di simile (non testato!) Potrebbe fare il trucco:
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
};
L'unico vero problema (oltre a non essere a tempo di compilazione) è che devi marcare ogni funzione lento e in tempo reale con entrambi i marcatori, ma dal momento che il compilatore non può sapere magicamente quale è ciò, non c'è comunque molta scelta.
Si noti che la "bandiera" globale è in realtà un contatore , non una bandiera. La ragione di ciò è che una funzione lenta potrebbe immediatamente chiamare un'altra funzione lenta che ritorna e cancella il flag - assumendo erroneamente una funzione veloce ora (l'approccio con le sezioni critiche suggerito da xgbi potrebbe in questo caso deadlock!). Un contatore impedisce che ciò accada. In presenza di fili, è possibile sostituire int
con std::atomic_int
.
EDIT:
Come è chiaro ora che ci sono davvero 2 discussioni in esecuzione, e importa solo che uno di loro (il filo "veloce") non significa mai chiamare una funzione "lento", c'è un'altra soluzione semplice e funzionante (ad esempio utilizzando l'API Win32, ma potrebbe essere eseguita con POSIX in entrambi i casi):
All'avvio del thread "veloce" (il thread "lento" non ha bisogno di farlo), store l'ID del thread da qualche parte, sia come variabile globale, sia come membro dell'oggetto che contiene tutte le funzioni veloce/lento, ovunque sia accessibile:
global::fast_thread_id = GetCurrentThreadId();
La macro per salvare su chiamate di funzione "sgraditi" potrebbe quindi apparire come:
#define CHECK_FAST_THREAD assert(GetCurrentThreadID() != global::fast_thread_id)
Questa macro viene poi aggiunto a qualsiasi funzione di "slow" che non dovrebbe mai essere chiamato dal thread "veloce" . Se il thread veloce chiama una funzione che non deve chiamare, il trigger asserisce ed è noto quale funzione viene chiamata.
È questo codice multi-threaded? –
Sì @AdrianMcCarthy – user2409054