[Boost-bugs] [Boost C++ Libraries] #9601: Unable to load archives with pointer instances which contain self reference cycles in 1.55

Subject: [Boost-bugs] [Boost C++ Libraries] #9601: Unable to load archives with pointer instances which contain self reference cycles in 1.55
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2014-01-22 18:06:13


#9601: Unable to load archives with pointer instances which contain self reference
cycles in 1.55
------------------------------+---------------------------
 Reporter: brandon.kohn | Owner: ramey
     Type: Bugs | Status: new
Milestone: To Be Determined | Component: serialization
  Version: Boost 1.55.0 | Severity: Problem
 Keywords: |
------------------------------+---------------------------
 First the original pointer is loaded like so:
 {{{
 #!c++
 // because the following operation could move the items
 // don't use co after this
 // add to list of serialized objects so that we can properly handle
 // cyclic strucures
 //! Note: t's value is undefined at this point. It's whatever value it had
 when serialization was called.
 object_id_vector.push_back(aobject(t, cid));

 // remember that that the address of these elements could change
 // when we make another call so don't use the address
 bpis_ptr->load_object_ptr(
     ar,
     t,
     m_pending.version
 );
 BOOST_ASSERT(NULL != t);
 //! Note: The object_id_vector element's address is not updated until the
 load is complete.
 [1] object_id_vector[ui].address = t;
 }}}

 load_object_ptr will eventually lead to:
 {{{
 #!c++
 template<class Archive, class T>
 BOOST_DLLEXPORT void pointer_iserializer<Archive, T>::load_object_ptr(
     basic_iarchive & ar,
     void * & x,
     const unsigned int file_version
 ) const
 {
 ...
 //! This serializes an instance of T
     ar_impl >> boost::serialization::make_nvp(NULL, * t);
 }
 }}}
 If t contains a cyclic reference to itself
 then the following is called:
 {{{
 #!c++
 // extra line to evade borland issue
 const bool tracking = co.tracking_level;
 // if we're tracking and the pointer has already been read
 if(tracking && ! track(ar, t))
     // we're done
     return bpis_ptr;
 }}}
 track will check if the oid is already loaded
 and assign the address of the previously loaded pointer.
 {{{
 #!c++
 bool
 basic_iarchive_impl::track(
     basic_iarchive & ar,
     void * & t
 ){
     ...
     // if its a reference to a old object
     if(object_id_type(object_id_vector.size()) > oid){
         // we're done
 //! Note this line: ***
 [2] t = object_id_vector[oid].address;
         return false;
     }
     return true;
 }
 }}}
 Recall back at the original scope [1]:
 remember that that the address of these elements could change
 {{{
 #!c++
 // when we make another call so don't use the address
 bpis_ptr->load_object_ptr(
     ar,
     t,
     m_pending.version
 );
 BOOST_ASSERT(NULL != t);
 //! The assignment to object_id_vector[ui].address takes place after the
 cycle is loaded.
 //! This means means the value assigned to t at [2] is undefined.
 object_id_vector[ui].address = t;
 }}}

 Here's a unit test that illustrates the issue:
 {{{
 #!c++
 namespace TestPointerSerializationReferenceCycle
 {
     struct PointerHolder
     {
         PointerHolder(PointerHolder* ptr = nullptr)
         : Ptr(ptr)
         {}

         PointerHolder* Ptr;

         template <typename Archive>
         void serialize(Archive& ar, const unsigned int v)
         {
             ar & Ptr;
         }
     };
 }

 BOOST_AUTO_TEST_CASE(TestPointerReferenceCycleLoad)
 {
     using namespace TestPointerSerializationReferenceCycle;

     std::stringstream buff(std::stringstream::in |
 std::stringstream::out);

     //! Write it.
     {
         PointerHolder* pPtrHldr = new PointerHolder();
         pPtrHldr->Ptr = pPtrHldr;

         boost::archive::text_oarchive ar(buff);
         ar & pPtrHldr;
     }

     //! Read it.
     {
         buff.seekg(0);

         PointerHolder* pPtrHldr =
 reinterpret_cast<PointerHolder*>(0xBAADF00D);

         boost::archive::text_iarchive ar(buff);
         ar & pPtrHldr;

         BOOST_CHECK(pPtrHldr != nullptr);
         if (pPtrHldr != nullptr)
         {
             BOOST_CHECK(pPtrHldr->Ptr == pPtrHldr);//! At this point
 pPtrHldr->Ptr == 0xBAADFOOD.
         }
     }
 }
 }}}

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/9601>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:15 UTC