$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
From: daniel_james_at_[hidden]
Date: 2007-12-19 18:33:31
Author: danieljames
Date: 2007-12-19 18:33:30 EST (Wed, 19 Dec 2007)
New Revision: 42191
URL: http://svn.boost.org/trac/boost/changeset/42191
Log:
Merge: When allocators aren't equal use a slow swap.
Properties modified: 
   branches/unordered/trunk/   (props changed)
Text files modified: 
   branches/unordered/trunk/boost/unordered/detail/hash_table_impl.hpp |    43 +++++++++++----------------             
   branches/unordered/trunk/libs/unordered/doc/rationale.qbk           |    61 ++++++++++++++++++++++++++++++++++----- 
   branches/unordered/trunk/libs/unordered/doc/ref.xml                 |    48 ++++++++++++++++++++++++++-----         
   branches/unordered/trunk/libs/unordered/test/exception/Jamfile.v2   |     2                                         
   branches/unordered/trunk/libs/unordered/test/unordered/Jamfile.v2   |     2                                         
   5 files changed, 113 insertions(+), 43 deletions(-)
Modified: branches/unordered/trunk/boost/unordered/detail/hash_table_impl.hpp
==============================================================================
--- branches/unordered/trunk/boost/unordered/detail/hash_table_impl.hpp	(original)
+++ branches/unordered/trunk/boost/unordered/detail/hash_table_impl.hpp	2007-12-19 18:33:30 EST (Wed, 19 Dec 2007)
@@ -1132,32 +1132,17 @@
 
             // Swap
             //
-            // Swap's behaviour when allocators aren't equal is in dispute, see
-            // this paper for full details:
-            //
-            // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2004/n1599.html
-            //
-            // It lists 3 possible behaviours:
-            //
-            // 1 - If the allocators aren't equal then throw an exception.
-            // 2 - Reallocate the elements in the containers with the
-            //     appropriate allocators - messing up exception safety in
-            //     the process.
-            // 3 - Swap the allocators, hoping that the allocators have a
-            //     no-throw swap.
-            //
-            // The paper recommends #3.
+            // Swap's behaviour when allocators aren't equal is in dispute, for
+            // details see:
+            // 
+            // http://unordered.nfshost.com/doc/html/unordered/rationale.html#swapping_containers_with_unequal_allocators
             //
             // ----------------------------------------------------------------
             //
             // Strong exception safety (might change unused function objects)
             //
-            // Can throw if hash or predicate object's copy constructor throws.
-            // If allocators are unequal:
-            //     Can throw if allocator's swap throws
-            //          (I'm assuming that the allocator's swap doesn't throw
-            //           but this doesn't seem to be guaranteed. Maybe I
-            //           could double buffer the allocators).
+            // Can throw if hash or predicate object's copy constructor throws
+            // or if allocators are unequal.
 
             void swap(BOOST_UNORDERED_TABLE& x)
             {
@@ -1170,10 +1155,18 @@
                     this->data::swap(x); // no throw
                 }
                 else {
-                    // Note: I'm not sure that allocator swap is guaranteed to be no
-                    // throw.
-                    this->allocators_.swap(x.allocators_);
-                    this->data::swap(x);
+                    // Create new buckets in separate HASH_TABLE_DATA objects
+                    // which will clean up if anything throws an exception.
+                    // (all can throw, but with no effect as these are new objects).
+                    data new_this(*this, x.min_buckets_for_size(x.size_));
+                    copy_buckets(x, new_this, this->*new_func_this);
+
+                    data new_that(x, min_buckets_for_size(this->size_));
+                    x.copy_buckets(*this, new_that, x.*new_func_that);
+
+                    // Start updating the data here, no throw from now on.
+                    this->data::swap(new_this);
+                    x.data::swap(new_that);
                 }
 
                 // We've made it, the rest is no throw.
Modified: branches/unordered/trunk/libs/unordered/doc/rationale.qbk
==============================================================================
--- branches/unordered/trunk/libs/unordered/doc/rationale.qbk	(original)
+++ branches/unordered/trunk/libs/unordered/doc/rationale.qbk	2007-12-19 18:33:30 EST (Wed, 19 Dec 2007)
@@ -116,19 +116,64 @@
 This is 
 [@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#431
 Issue 431: Swapping containers with unequal allocators].
+
 Howard Hinnant wrote about this in
 [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1599.html N1599]
 and suggested swapping both the allocators and the containers' contents.
+But the committee have now decided that `swap` should do a fast swap if the
+allocator is Swappable and a slow swap using copy construction otherwise. To
+make this distinction requires concepts.
 
-There is currently a further issue - if the allocator's swap does throw there's
-no guarantee what state the allocators will be in. The only solution seems to
-be to double buffer the allocators. But I'm assuming that it won't throw for
-now.
+In
+[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2387.pdf
+N2387, Omnibus Allocator Fix-up Proposals],
+Pablo Halpern suggests that there are actually two distinct allocator models,
+"Moves with Value" and "Scoped" which behave differently:
+
+[:
+When allocators are allowed to have state, it is necessary to have a model for
+determining from where an object obtains its allocator. Weâve identified two such
+models: the âMoves with Valueâ allocator model and the âScopedâ allocator model.
+
+In the âMoves with Valueâ allocator model, the copy constructor of an allocator-aware
+class will copy both the value and the allocator from its argument. This is the model
+specified in the C++03 standard. With this model, inserting an object into a container
+usually causes the new container item to copy the allocator from the object that was
+inserted. This model can be useful in special circumstances, e.g., if the items within a
+container use an allocator that is specially tuned to the itemâs type.
+
+In the âScopedâ allocator model, the allocator used to construct an object is determined
+by the context of that object, much like a storage class. With this model, inserting an
+object into a container causes the new container item to use the same allocator as the
+container. To avoid allocators being used in the wrong context, the allocator is never
+copied during copy or move construction. Thus, it is possible using this model to use
+allocators based on short-lived resources without fear that an object will transfer its
+allocator to a copy that might outlive the (shared) allocator resource. This model is
+reasonably safe and generally useful on a large scale. There was strong support in the
+2005 Tremblant meeting for pursuing an allocator model that propagates allocators
+from container to contained objects.
+]
+
+With these models the choice becomes clearer:
+
+[:
+I introduced the âMoves with Valueâ allocator model and the
+âScopedâ allocator model. In the former case, the allocator is copied when the container
+is copy-constructed. In the latter case it is not. Swapping the allocators is the right thing
+to do if the containers conform to the âMoves with Valueâ allocator model and
+absolutely the wrong thing to do if the containers conform to the âScopedâ allocator
+model. With the two allocator models well-defined, the desired behavior becomes clear.
+]
+
+The proposal is that allocators are swapped if the allocator follows the
+"Moves with Value" model and the allocator is swappable. Otherwise a slow swap
+is used. Since containers currently only support the "Moves with Value" model
+this is consistent with the committee's current recommendation (although it
+suggests using a trait to detect if the allocator is swappable rather than a
+concept).
 
-Update: The committee have now decided that `swap` should do a fast swap if the
-allocator is Swappable and a slow swap using copy construction otherwise. To
-make this distinction requires concepts. For now I'm sticking with the current
-implementation.
+Since there is currently neither have a swappable trait or concept for
+allocators this implementation always performs a slow swap.
 
 [h3 Are insert and erase stable for unordered_multiset and unordered_multimap?]
 
Modified: branches/unordered/trunk/libs/unordered/doc/ref.xml
==============================================================================
--- branches/unordered/trunk/libs/unordered/doc/ref.xml	(original)
+++ branches/unordered/trunk/libs/unordered/doc/ref.xml	2007-12-19 18:33:30 EST (Wed, 19 Dec 2007)
@@ -385,8 +385,12 @@
               </parameter>
               <type>void</type>
               <throws>
-                <para>Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>key_equal</code> or <code>hasher</code>.</para>
+                <para>If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>key_equal</code> or <code>hasher</code>.</para>
               </throws>
+              <notes>
+                <para>For a discussion of the behaviour when allocators aren't equal see 
+                  <link linkend="unordered.rationale.swapping_containers_with_unequal_allocators">the implementation details</link>.</para>
+              </notes>
             </method>
           </method-group>
           <method-group name="observers">
@@ -610,8 +614,12 @@
                 <para><code>x.swap(y)</code></para>
               </effects>
               <throws>
-                <para>Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>Hash</code> or <code>Pred</code>.</para>
+                <para>If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>Hash</code> or <code>Pred</code>.</para>
               </throws>
+              <notes>
+                <para>For a discussion of the behaviour when allocators aren't equal see 
+                  <link linkend="unordered.rationale.swapping_containers_with_unequal_allocators">the implementation details</link>.</para>
+              </notes>
             </function>
           </free-function-group>
         </class>
@@ -988,8 +996,12 @@
               </parameter>
               <type>void</type>
               <throws>
-                <para>Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>key_equal</code> or <code>hasher</code>.</para>
+                <para>If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>key_equal</code> or <code>hasher</code>.</para>
               </throws>
+              <notes>
+                <para>For a discussion of the behaviour when allocators aren't equal see 
+                  <link linkend="unordered.rationale.swapping_containers_with_unequal_allocators">the implementation details</link>.</para>
+              </notes>
             </method>
           </method-group>
           <method-group name="observers">
@@ -1213,8 +1225,12 @@
                 <para><code>x.swap(y)</code></para>
               </effects>
               <throws>
-                <para>Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>Hash</code> or <code>Pred</code>.</para>
+                <para>If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>Hash</code> or <code>Pred</code>.</para>
               </throws>
+              <notes>
+                <para>For a discussion of the behaviour when allocators aren't equal see 
+                  <link linkend="unordered.rationale.swapping_containers_with_unequal_allocators">the implementation details</link>.</para>
+              </notes>
             </function>
           </free-function-group>
         </class>
@@ -1608,8 +1624,12 @@
               </parameter>
               <type>void</type>
               <throws>
-                <para>Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>key_equal</code> or <code>hasher</code>.</para>
+                <para>If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>key_equal</code> or <code>hasher</code>.</para>
               </throws>
+              <notes>
+                <para>For a discussion of the behaviour when allocators aren't equal see 
+                  <link linkend="unordered.rationale.swapping_containers_with_unequal_allocators">the implementation details</link>.</para>
+              </notes>
             </method>
           </method-group>
           <method-group name="observers">
@@ -1869,8 +1889,12 @@
                 <para><code>x.swap(y)</code></para>
               </effects>
               <throws>
-                <para>Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>Hash</code> or <code>Pred</code>.</para>
+                <para>If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>Hash</code> or <code>Pred</code>.</para>
               </throws>
+              <notes>
+                <para>For a discussion of the behaviour when allocators aren't equal see 
+                  <link linkend="unordered.rationale.swapping_containers_with_unequal_allocators">the implementation details</link>.</para>
+              </notes>
             </function>
           </free-function-group>
         </class>
@@ -2255,8 +2279,12 @@
               </parameter>
               <type>void</type>
               <throws>
-                <para>Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>key_equal</code> or <code>hasher</code>.</para>
+                <para>If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>key_equal</code> or <code>hasher</code>.</para>
               </throws>
+              <notes>
+                <para>For a discussion of the behaviour when allocators aren't equal see 
+                  <link linkend="unordered.rationale.swapping_containers_with_unequal_allocators">the implementation details</link>.</para>
+              </notes>
             </method>
           </method-group>
           <method-group name="observers">
@@ -2482,8 +2510,12 @@
                 <para><code>x.swap(y)</code></para>
               </effects>
               <throws>
-                <para>Doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>Hash</code> or <code>Pred</code>.</para>
+                <para>If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of <code>Hash</code> or <code>Pred</code>.</para>
               </throws>
+              <notes>
+                <para>For a discussion of the behaviour when allocators aren't equal see 
+                  <link linkend="unordered.rationale.swapping_containers_with_unequal_allocators">the implementation details</link>.</para>
+              </notes>
             </function>
           </free-function-group>
         </class>
Modified: branches/unordered/trunk/libs/unordered/test/exception/Jamfile.v2
==============================================================================
--- branches/unordered/trunk/libs/unordered/test/exception/Jamfile.v2	(original)
+++ branches/unordered/trunk/libs/unordered/test/exception/Jamfile.v2	2007-12-19 18:33:30 EST (Wed, 19 Dec 2007)
@@ -21,5 +21,5 @@
         [ run erase_tests.cpp framework ]
         [ run rehash_tests.cpp framework ]
         [ run swap_tests.cpp framework : : :
-            <define>BOOST_UNORDERED_SWAP_METHOD=3 ]
+            <define>BOOST_UNORDERED_SWAP_METHOD=2 ]
     ;
Modified: branches/unordered/trunk/libs/unordered/test/unordered/Jamfile.v2
==============================================================================
--- branches/unordered/trunk/libs/unordered/test/unordered/Jamfile.v2	(original)
+++ branches/unordered/trunk/libs/unordered/test/unordered/Jamfile.v2	2007-12-19 18:33:30 EST (Wed, 19 Dec 2007)
@@ -27,5 +27,5 @@
         [ run bucket_tests.cpp ]
         [ run load_factor_tests.cpp ]
         [ run rehash_tests.cpp ]
-        [ run swap_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=3 ]
+        [ run swap_tests.cpp : : : <define>BOOST_UNORDERED_SWAP_METHOD=2 ]
     ;