Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

istream and ostream with shared streambuf mutually thread-safe for duplex I/O?

I've derived a custom streambuf for buffered network socket I/O, overriding underflow, overflow, and sync so that underflow is mutually thread-safe with the set of the other two, (I have separate input and output internal buffers). That works fine, but I want to use this for full duplex I/O where one thread can input while another is outputting, so I'd like to use an istream for the receiving thread and ostream for the sending one, while sharing the network streambuf as that abstracts all the socket stuff. My question is, to what extent are the streambuf members affected by input operations on an istream disjoint from the streambuf members affected by output operations on an ostream if the input and output buffers are separate?

It would be better to be able to do that rather than have to separate the socket stuff from my streambuf abstraction so the socket can be shared between istream and ostream with separate streambufs--then I'd also need two versions of the streambuf--one with a single internal buffer (for use in either istream only or ostream only), and one with two internal buffers as I have now, for use in iostream... sucks as that's additional classes and code duplication.

like image 808
Display Name Avatar asked Apr 01 '12 09:04

Display Name


2 Answers

There is no special guarantee given for std::streambuf (or std::basic_streambuf<...>) which gives more guarantees than what is generally given. That is, you can have multiple threads reading the object's state at any time but if there is one thread modifying the object's state there shall be no other thread accessing the object. Both reading and writing characters modify the stream buffer's state, i.e. from a formal point of view you can't use them without external synchronization.

Internally the two buffers are entirely separate and have nothing to do with each other. The operations on stream buffers modify them in a rather structured way and I can't imagine that any implementation would have an explicit interaction between the two sets of pointers. That is, in practical terms I don't think there is any synchronization necessary between reading and writing. However, I hadn't realized before that the two sets of buffer pointers may actually share the same cache lines which may at least cause performance problems. I don't think this should cause any correctness problems.

The only resource possibly shared between the two stream buffers is the std::locale object which is meant to be stateless, however. Also, std::streambuf doesn't make any use of this object itself: it is your stream buffer which may use some of the facets (e.g. the std::codecvt<...> facet). As the locale is changed via a call to the virtual function imbue() you would be able to intercept this change and do whatever synchronization is needed if your stream buffer uses the locale.

In summary, the standard doesn't make any guarantee that it would work to use concurrent threads to read and write using the same stream buffer. In practice, the DS9k is probably the only system where it would fail and the two threads may end up effectively synchronized due to the buffer pointers ending up in shared cache lines.

like image 139
Dietmar Kühl Avatar answered Sep 21 '22 01:09

Dietmar Kühl


The input and output sequences are essentially independent. There's a nice diagram at cppreference.com:

Diagram of streambuf members

The only thing shared between the input and output sequences is the locale object which contains the codecvt facet used to perform text encoding translation.

In theory, changing the text encoding midstream would be thread-unsafe, but in practice the libraries don't support that operation at all anyway!

You should be good to go.

like image 39
Potatoswatter Avatar answered Sep 20 '22 01:09

Potatoswatter