Boost C++ Libraries: Ticket #1002: [iostreams] close_impl<closable_tag> does not comply with spec https://svn.boost.org/trac10/ticket/1002 <p> I experienced a problem where a composite Device inside a tee_device was not getting closed when appropriate. </p> <p> The root cause of the problem is that the implementation of close.hpp::close_impl&lt;closable_tag&gt;::close() is not in accordance with the documentation (<a href="http://www.boost.org/libs/iostreams/doc/functions/close.html">http://www.boost.org/libs/iostreams/doc/functions/close.html</a>). </p> <p> According to the documentation, my closable composite should be closed when close() is invoked with an open mode of (in|out), but the code as written does not allow for this. The patch is attached. </p> <p> Thanks to gchen (chengang31@…), who pointed me to the problem location. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/1002 Trac 1.4.3 chad@… Mon, 28 May 2007 02:05:50 GMT attachment set https://svn.boost.org/trac10/ticket/1002 https://svn.boost.org/trac10/ticket/1002 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">close.hpp.patch</span> </li> </ul> <p> Patch for close_impl&lt;closable_tag&gt; </p> Ticket Marshall Clow Mon, 13 Aug 2007 15:30:18 GMT owner set https://svn.boost.org/trac10/ticket/1002#comment:1 https://svn.boost.org/trac10/ticket/1002#comment:1 <ul> <li><strong>owner</strong> set to <span class="trac-author">Douglas Gregor</span> </li> </ul> Ticket Marshall Clow Sun, 19 Aug 2007 15:26:36 GMT component changed https://svn.boost.org/trac10/ticket/1002#comment:2 https://svn.boost.org/trac10/ticket/1002#comment:2 <ul> <li><strong>component</strong> <span class="trac-field-old">None</span> → <span class="trac-field-new">iostreams</span> </li> </ul> Ticket Frank Birbacher Fri, 02 Nov 2007 21:28:45 GMT <link>https://svn.boost.org/trac10/ticket/1002#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/1002#comment:3</guid> <description> <p> Hopefully fixed by a similar patch, see <a class="changeset" href="https://svn.boost.org/trac10/changeset/40700" title="Test case and possible fix for Issue #1002 ">[40700]</a>. I also updated the documentation to match the implementation. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Jonathan Turkanis</dc:creator> <pubDate>Mon, 03 Dec 2007 02:28:00 GMT</pubDate> <title>owner, status changed https://svn.boost.org/trac10/ticket/1002#comment:4 https://svn.boost.org/trac10/ticket/1002#comment:4 <ul> <li><strong>owner</strong> changed from <span class="trac-author">Douglas Gregor</span> to <span class="trac-author">Jonathan Turkanis</span> </li> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">assigned</span> </li> </ul> <p> The following analysis was provide by Chad Walters. I am in the process of implementing his proposal <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/3" title="#3: Bugs: automatic conversion and overload proble (closed: fixed)">#3</a>, below. </p> <hr /> <p> I have come to the conclusion that there may need to be a somewhat larger set of changes than the patches that we have been considering so far, at least for the long run. Let me walk through that reasoning and see if you agree. </p> <p> Here are all the cases covered by the code in close.hpp, matched up against the semantics table and the rest of the close function documentation. </p> <ol><li>Non-closable devices and filters cannot end up in the closable_tag </li></ol><p> specialization of close_impl. They don't get closed by the close function -- they just get flushed. </p> <ol start="2"><li>Closable bidirectional devices and filters cannot end up in the </li></ol><p> closable_tag specialization of close_impl -- they go to the two_sequence version instead. They always get closed by the close function, regardless of the value of the which parameter, and invoke the device or filter's close(which) method. </p> <ol start="3"><li>Closable dual_use filters and details::teed_sequence devices and filters </li></ol><p> follow the same path in the code as <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/2" title="#2: Bugs: list::size should be const (closed: fixed)">#2</a> above. These are not covered explicitly in the documentation of the close function at all. I need to understand the use of dual_use a little more to really understand this case. I also note that teed_sequence appears only in tee.hpp and is actually commented out in both cases. Furthermore, close.hpp is the only place in the library where teed_sequence is used. I think this notion is basically deprecated at this point and should be removed. </p> <ol start="4"><li>A closable device or filter that is input only should be closed according </li></ol><p> to whether (which &amp; ios_base::in) != 0, and invoke the close() method. </p> <ol start="5"><li>A closable device or filter that is output only should be closed </li></ol><p> according to whether (which &amp; ios_base::out) != 0, and invoke the close() method. This is the case that I was trying to patch up -- the code was doing the wrong thing here when (which &amp; ios_base::out) != 0 &amp;&amp; (which &amp; ios_base::in) != 0. The documentation under "When is close invoked?" implies to some extent that the close function should only ever be invoked with the which parameter equal to exactly one of ios_base::in or ios_base::out, but not both. But this case was happening nonetheless. </p> <ol start="6"><li>A closable device or filter can be input and output but not be </li></ol><p> bidirectional (like the memory mapped file, as you point out). This means that it controls a single sequence. The semantics table implies that this sequence should get closed when (which &amp; ios_base::out) != 0, just like case <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/4" title="#4: Bugs: any_ptr in any library documentation? (closed: Fixed)">#4</a> above here. In addition, this would be consistent with the sequencing in the "When should close be invoked?" discussion for filtering_streambuf and filtering_stream. The close function should get invoked twice for the single sequence device or filter, first with ios_base::in and then with ios_base::out. Since this is a single sequence, it probably shouldn't be closed until the second invocation (ie: the one with ios_base::out). </p> <p> OK, let's pause here before pressing on... Hopefully everything so far makes good sense. </p> <p> Based on the above, I think that the existing code in close.hpp would basically have been correct if the which parameter could only be exactly one of ios_base::in or iosbase::out, but not both. As you pointed out, the tee_filter and tee_device code can invoke the close function with the which parameter equal to (ios_base::in | ios_base::out). This conflict is the direct source of the bug I reported. </p> <p> I think that the true problem actually lies a little deeper. It seems to me that the fundamental issue stems from the fact that the Closable concept requires that a Filter or Device provide different versions of close(), depending on whether it controls a single sequence or two sequences (as described under "Examples" in the Closable documentation): if they contain a single sequence, then they need to have a close method that doesn't have an ios_base::mode parameter; and if they contain two sequences, they must have a close method with an ios_base::mode parameter. </p> <p> However, there are several devices and filters in the iostreams library that, for simplicity's sake, provide a close method with an optional default value for the which parameter so that they can wrap other devices or filters without caring about whether the wrapped device or filter has one or two sequences. Furthermore, some of them, like tee_filter and tee_device, set that which parameter to (ios_base::in | ios_base::out). This means that close_impl can get called with this unexpected value. </p> <p> There are 3 different possible avenues to fixing the problems with closing, with increasing levels of difficulty and architectural cleanliness. </p> <ol><li>Just make the spot fix for the particular bug that I encountered in </li></ol><p> close.hpp; that is, make the close_impl for non-bidirectional closable devices and filters behave as if (ios_base::in | ios_base::out) were a valid input for the which parameter. I would adjust your patch by changing the body of your should_close function: just remove the "if (hasIn &amp;&amp; hasOut) return false;" partl otherwise the wrong thing happens for case <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6" title="#6: Bugs: tie in utility.hpp and tuple.hpp clash. (closed: Duplicate)">#6</a> above. I would also leave the documentation of the close function semantics untouched (ie: rollback the change you made) -- it is correctly stating the cases. Also, no need to change the default parameter in tee_filter and tee_device (in case something is done WRT issue <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/791" title="#791: Feature Requests: iostreams::tee_filter is for output only (closed: fixed)">#791</a>). </p> <ol start="2"><li>Change all the devices and filters in the iostreams library that provide </li></ol><p> a default value for the which parameter on their close methods to support separate close methods, one without the which parameter and one with the which parameter. Make sure the right version gets invoked by all the callers in the library, depending on whether the underlying stream has one or two sequences. There probably need to be specializations of tee_device and tee_filter, depending on whether the underlying Devices have one or two sequences. </p> <ol start="3"><li>Do all the work for <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/2" title="#2: Bugs: list::size should be const (closed: fixed)">#2</a> above. In addition, change the Closable concept </li></ol><p> and the close function so that only exactly two values for the which parameter are possible. Simply changing to a bool would really be the simplest option here, but I suppose other more complex template-based things could work as well. </p> <p> Ordinarily, I'd recommend <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/1" title="#1: Bugs: boost.build causes ftjam to segfault (closed: Wont Fix)">#1</a> for a short term fix (no API changes) for the next bugfix release and <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/3" title="#3: Bugs: automatic conversion and overload proble (closed: fixed)">#3</a> for the next major revision. However, we might be too close to 1.35.0 to do the changes for <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/3" title="#3: Bugs: automatic conversion and overload proble (closed: fixed)">#3</a> in time. I guess <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/1" title="#1: Bugs: boost.build causes ftjam to segfault (closed: Wont Fix)">#1</a> for 1.35.0 would work, with <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/3" title="#3: Bugs: automatic conversion and overload proble (closed: fixed)">#3</a> for 1.36.0 in the (distant?) future would be the minimum desirable outcome. </p> Ticket Jonathan Turkanis Wed, 26 Dec 2007 00:53:39 GMT status changed; resolution set https://svn.boost.org/trac10/ticket/1002#comment:5 https://svn.boost.org/trac10/ticket/1002#comment:5 <ul> <li><strong>status</strong> <span class="trac-field-old">assigned</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">fixed</span> </li> </ul> <p> Fixed in <a class="changeset" href="https://svn.boost.org/trac10/changeset/42047" title="I. Changed signature and specification of boost::iostreams::close(), ...">[42047]</a>. </p> Ticket