C ++: come implementare un timeout per una chiamata di funzione arbitraria?

Devo chiamare una funzione di libreria che a volte non si risolve entro un determinato periodo, sfortunatamente. C’è un modo per chiamare la funzione ma abortire se non termina entro n secondi?

Non riesco a modificare la funzione, quindi non posso inserire direttamente la condizione di interruzione. Devo aggiungere un timeout alla funzione esternamente .

È forse una ansible soluzione per avviarlo come un thread (boost), che posso quindi terminare dopo un certo tempo? Qualcosa del genere funzionerebbe? In realtà credo che la funzione non sia thread-safe, ma non sarebbe importante se la eseguissi come unico thread singolo, giusto? Ci sono altre (migliori) soluzioni?

Potresti generare un boost::thread per chiamare l’API:

 boost::thread api_caller(::api_function, arg1, arg2); if (api_caller.timed_join(boost::posix_time::milliseconds(500))) { // API call returned within 500ms } else { // API call timed out } 

Boost non ti permette di uccidere il thread di lavoro, però. In questo esempio, è solo orfano.

Dovrai fare attenzione a ciò che fa questa chiamata API, perché potrebbe non rilasciare mai le risorse che è stata acquisita.

Penso che l’unico modo sicuro per farlo sarebbe quello di generare un processo sandbox separato che chiama la funzione di libreria come proxy per l’applicazione. Dovrai implementare un qualche tipo di IPC tra l’applicazione e il proxy. Implementare un timeout sulla lettura della risposta IPC è quindi abbastanza banale. Se la lettura non riesce a causa del timeout, è ansible terminare il proxy in modo sicuro senza mettere a repentaglio lo stato dell’applicazione.

Quello di cui stai parlando è in genere chiamato sistema “watchdog”. Il watchdog è in genere un secondo thread che controlla lo stato di tutti gli altri thread. Il watchdog è in genere configurato per essere eseguito periodicamente. Se non è stata ricevuta alcuna risposta dagli altri thread, il watchdog può avvisare l’utente, o addirittura uccidere il thread offendente se è ansible farlo in modo sicuro (dipende dall’applicazione).

Il problema con i thread è che alcune risorse non saranno in grado di liberare dopo la chiusura del thread. Se non si acquisiscono risorse da rilasciare, andare con i thread.

Il problema è che con una soluzione in-process senza supporto dalla funzione si finisce con uno stato potenzialmente non valido.

Esempio: quando si interrompe il thread mentre è in corso un’allocazione di memoria, l’heap del processo potrebbe essere danneggiato.

Quindi puoi terminare la chiamata, ma devi anche terminare la procedura. In molti casi, le probabilità di effetti collaterali distruttivi sono piccole, ma su questo non scommetterei il mio calcolo.

Puoi, come suggerisce Ben Straub, solo orfano il thread: mettilo su priorità più bassa e fallo funzionare all’infinito. Questa è ovviamente solo una soluzione limitata: se il thread consuma risorse (probabilmente), rallenterà il sistema, inoltre c’è un limite sui thread per processo (solitamente a causa dello spazio indirizzo per lo stack di thread).

In generale, preferirei la soluzione di processo esterna. Un semplice schema è questo:
Scrivi i dati di input in un file, avvia il processo esterno con il file come argomento. Il processo esterno scrive il progresso (se esiste) in un file su disco che può essere monitorato e può persino consentire al processo di riprendere da dove è stato avviato. I risultati vengono scritti su disco e il processo padre può leggerli.

Quando si termina il processo, si deve ancora fare i conti con la sincronizzazione dell’accesso a risorse esterne (come i file), e come gestire mutazioni abbandonate, file scritti a metà, ecc. Ma in genere si tratta di una soluzione robusta.

Quello di cui hai bisogno è una discussione e un object futuro che possa contenere il risultato della chiamata di funzione.

Per un esempio usando boost vedi qui .

Dovresti controllare il futuro dopo il timeout e, se non è impostato, agire di conseguenza.

Vai con un processo orfano, lanciarlo e cronometrare la sua esecuzione. Se si esaurisce il tempo, richiamare il sistema operativo per ucciderlo.

Come evitare i contatti di gara. su questo modello:

  • crea un file da archiviare in args (ovviamente, tutto viene passato come VAL). Il processo orfano è autorizzato solo a leggere i dati da questo file.

  • L’orfano elabora i dati di input, crea un file di output con i valori dei risultati e lo chiude.

  • Solo quando tutto è fatto, orphan cancella il file di input, un fatto che segnala al processo master che il lavoro è stato eseguito.

Ciò evita di leggere il problema dei file scritti a metà, dal momento che il master prima rileva l’assenza del file di input, apre per leggere il file di output, che è sicuramente completato (perché è stato chiuso prima dell’eliminazione dell’input e gli stack delle chiamate del sistema operativo sono sequenziali).

“Ho bisogno di chiamare una funzione di libreria che a volte non termina in un dato momento, sfortunatamente. C’è un modo per chiamare la funzione ma interromperla se non termina entro n secondi?”

La risposta breve è no. Questo di solito è un problema … La chiamata stessa deve terminare a un certo momento (implementando il proprio timeout), ma il blocco delle chiamate è di solito un problema (es. Gethostbyname ()) perché poi dipende dal loro timeout (o sistema), non dal tuo.

Quindi, quando ansible, cerca di fare in modo che il codice in esecuzione nel thread esca pulito quando necessario – il codice stesso deve rilevare e gestire l’errore. Può inviare un messaggio e / o impostare stati in modo che il thread principale (o un altro) sappia cosa è successo.

Preferenza personale, in sistemi altamente disponibili, mi piace che i miei thread girino spesso (senza blocco di occupato) con timeout specifici, chiamate funzioni non bloccanti e con condizioni di uscita precise sul posto. Una variabile “fatta” globale o specifica per il thread fa il trucco per un’uscita pulita.