2012-04-01 8 views
6

Ho derivato uno streambuf personalizzato per l'I/O del socket di rete bufferizzato, l'overflow di underflow, l'overflow e la sincronizzazione in modo che l'underflow sia reciprocamente sicuro con il set degli altri due (ho buffer interni di input e output separati) . Funziona bene, ma voglio usarlo per l'I/O full duplex in cui un thread può essere inserito mentre un altro è in uscita, quindi mi piacerebbe usare un istream per il thread ricevente e ostream per il mittente, mentre condivido la rete streambuf come quello che abstracts tutte le cose del socket. La mia domanda è: fino a che punto i membri dello streambuf interessati dalle operazioni di input su un istream sono disgiunti dai membri dello streambuf interessati dalle operazioni di output su un ostream se i buffer di input e output sono separati?istream e ostream con streambuf condiviso reciprocamente thread-safe per l'I/O duplex?

Sarebbe meglio essere in grado di farlo piuttosto che dover separare il socket dalla mia astrazione streambuf in modo che il socket possa essere condiviso tra istream e ostream con streambuf separati - quindi avrei anche bisogno di due versioni di lo streambuf - uno con un singolo buffer interno (da usare solo in istream o solo ostream), e uno con due buffer interni come ora, per l'uso in iostream ... fa schifo come se fossero classi aggiuntive e duplicazione del codice.

+0

A prima vista ho avuto qualche dubbio, ma questa sembra la migliore domanda che ho visto qui da un po '. +1 – Potatoswatter

risposta

2

Non è prevista alcuna garanzia speciale per std::streambuf (o std::basic_streambuf<...>) che offre maggiori garanzie rispetto a quanto generalmente fornito. Cioè, puoi avere più thread che leggono lo stato dell'oggetto in qualsiasi momento, ma se c'è un thread che modifica lo stato dell'oggetto non ci devono essere altri thread che accedono all'oggetto. Sia i caratteri di lettura che di scrittura modificano lo stato del buffer del flusso, cioè da un punto di vista formale non è possibile utilizzarli senza sincronizzazione esterna.

Internamente i due buffer sono completamente separati e non hanno nulla a che fare l'uno con l'altro. Le operazioni sui buffer di flusso li modificano in un modo piuttosto strutturato e non posso immaginare che qualsiasi implementazione abbia un'interazione esplicita tra i due insiemi di puntatori. Cioè, in termini pratici non penso ci sia alcuna sincronizzazione necessaria tra lettura e scrittura. Tuttavia, non mi ero reso conto prima che i due gruppi di puntatori del buffer potessero effettivamente condividere le stesse linee di cache che potrebbero almeno causare problemi di prestazioni. Non penso che questo dovrebbe causare problemi di correttezza.

L'unica risorsa eventualmente condivisa tra i due buffer di flusso è l'oggetto std::locale che deve essere considerato senza stato, tuttavia. Inoltre, std::streambuf non fa alcun uso di questo oggetto stesso: è il buffer del flusso che può utilizzare alcune delle facce (ad esempio il facet std::codecvt<...>). Poiché le impostazioni internazionali vengono modificate tramite una chiamata alla funzione virtuale imbue(), sarà possibile intercettare questa modifica ed eseguire qualsiasi operazione di sincronizzazione necessaria se il proprio buffer di flusso utilizza la locale.

In breve, lo standard non garantisce che possa funzionare per utilizzare thread concorrenti per leggere e scrivere utilizzando lo stesso buffer di flusso.In pratica, DS9k è probabilmente l'unico sistema in cui fallirebbe e i due thread potrebbero essere sincronizzati in modo efficace a causa dei puntatori del buffer che finiscono nelle linee della cache condivisa.

+0

" ... l'oggetto std :: locale che deve essere stateless ... " - vale la pena notare che ha bisogno di due membri 'mbstate_t', separati per input e output. Cos'è DS9k? – Potatoswatter

+0

The [Death Station 9000 (DS9k)] (http://en.wikipedia.org/wiki/User:CompuHacker/CHDS9000) è un'ipotetica e deliberatamente il più inutile possibile implementazione dello standard: laddove viene concessa qualsiasi libertà, usa questo per causare il maggior danno possibile –

+0

Vedo che istream ha un membro sync(), che chiamerà la sincronizzazione di streambuf (). Non ho visto nulla su nessuno altri membri di istream che chiamano sync() stesso quindi presumo che sia un problema solo se l'utente lo chiama, e io posso ignorarlo per non fare nulla o far sì che lo streambuf tenti di leggere altri dati dal socket nel buffer . –

2

Le sequenze di input e output sono essenzialmente indipendenti. C'è una bella figura a cppreference.com:

Diagram of streambuf members

L'unica cosa in comune tra le sequenze di ingresso e di uscita è l'oggetto locale che contiene la sfaccettatura codecvt utilizzata per eseguire una versione di testo codifica.

In teoria, la modifica della codifica del testo in midstream non sarebbe sicura dal punto di vista del thread, ma in pratica le librerie non supportano comunque tale operazione!

Dovresti essere a posto.

+0

Cambiare il midstream locale porterebbe a comportamenti incoerenti (almeno per quanto riguarda il codectt potrebbe essere in mid translation (do_in/do_out) di un chunk di input/output). Quindi cambiare un locale dopo che qualsiasi cosa è stata letta/scritta da/a uno stream sembrerebbe una cattiva idea. Una conseguenza di ciò è che la classe stream_buf consente alle classi derivate di memorizzare nella cache i valori di facet recuperati da una locale. Inoltre alcuni stream mettono restrizioni/condizioni sulla modifica del locale se non sono all'inizio (cioè dopo aver letto/scritto sono state fatte). –

+0

@Loki: Cambiare il locale midstream è come chiudere il file e riaprirlo con una diversa localizzazione. Non ci sono barriere concettuali; scrivendo una sequenza non valida risulta un'eccezione. È supportato dallo standard ma da poche o eventuali implementazioni. – Potatoswatter

+0

Non vedo alcun riferimento all'apertura e chiusura dei flussi quando si utilizza imbue() o pubimbue() nello standard. Ci sono barriere concettuali come ho spiegato sopra. –

Problemi correlati