Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions include/boost/archive/detail/basic_iarchive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// basic_iarchive.hpp:

// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// Copyright 2026 Gennaro Prota.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -68,6 +69,14 @@ class BOOST_SYMBOL_VISIBLE basic_iarchive :
virtual BOOST_ARCHIVE_DECL ~basic_iarchive();
// note: NOT part of the public API.
BOOST_ARCHIVE_DECL void next_object_pointer(void *t);
// Note: *not* part of the public API. Called by load_object_ptr once the
// object has been constructed, so that a throw while loading its members
// leaves it reclaimable by delete_created_pointers.
BOOST_ARCHIVE_DECL void object_constructed();
// Note: *not* part of the public API. Called when an owning smart pointer
// takes over the object just loaded, so that delete_created_pointers
// leaves it to that smart pointer instead of freeing it as well.
BOOST_ARCHIVE_DECL void object_adopted();
BOOST_ARCHIVE_DECL void register_basic_serializer(
const basic_iserializer & bis
);
Expand Down
11 changes: 8 additions & 3 deletions include/boost/archive/detail/iserializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,18 @@ BOOST_DLLEXPORT void pointer_iserializer<Archive, T>::load_object_ptr(
);
}
BOOST_CATCH(...){
// if we get here the load_construct failed. The heap_allocation
// will be automatically deleted so we don't have to do anything
// special here.
// The load_construct failed, so the object was never constructed.
// Since heap_allocation() has already released its guard, free the
// raw storage here, without running a destructor on it.
detail::heap_allocation<T>::invoke_delete(static_cast<T *>(t));
BOOST_RETHROW;
}
BOOST_CATCH_END

// The object exists from here on, so let the archive reclaim it if
// loading its members throws.
ar.object_constructed();

ar_impl >> boost::serialization::make_nvp(NULL, * static_cast<T *>(t));
}

Expand Down
3 changes: 3 additions & 0 deletions include/boost/archive/detail/polymorphic_iarchive_route.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ class polymorphic_iarchive_route :
void delete_created_pointers() BOOST_OVERRIDE {
ArchiveImplementation::delete_created_pointers();
}
void object_adopted() BOOST_OVERRIDE {
ArchiveImplementation::object_adopted();
}
void reset_object_address(
const void * new_address,
const void * old_address
Expand Down
6 changes: 6 additions & 0 deletions include/boost/archive/polymorphic_iarchive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// polymorphic_iarchive.hpp

// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// Copyright 2026 Gennaro Prota.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -126,6 +127,11 @@ class BOOST_SYMBOL_VISIBLE polymorphic_iarchive_impl :
virtual boost::serialization::library_version_type get_library_version() const = 0;
virtual unsigned int get_flags() const = 0;
virtual void delete_created_pointers() = 0;
// Note: not pure, so that archives written against an earlier release
// still compile. Such an archive keeps the old behaviour, in which
// delete_created_pointers also frees objects an owning smart pointer
// has taken over.
virtual void object_adopted() {}
virtual void reset_object_address(
const void * new_address,
const void * old_address
Expand Down
1 change: 1 addition & 0 deletions include/boost/serialization/scoped_ptr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ namespace serialization {
T* r;
ar >> boost::serialization::make_nvp("scoped_ptr", r);
t.reset(r);
ar.object_adopted();
}

template<class Archive, class T>
Expand Down
3 changes: 3 additions & 0 deletions include/boost/serialization/shared_ptr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ inline void load(
shared_ptr_helper_id
);
h.reset(t,r);
ar.object_adopted();
}
#else

Expand All @@ -161,6 +162,7 @@ inline void load(
shared_ptr_helper_id
);
h.reset(t,r);
ar.object_adopted();
}
#endif

Expand Down Expand Up @@ -256,6 +258,7 @@ inline void load(
shared_ptr_helper_id
);
h.reset(t,r);
ar.object_adopted();
}

template<class Archive, class T>
Expand Down
1 change: 1 addition & 0 deletions include/boost/serialization/unique_ptr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ inline void load(
ar >> BOOST_SERIALIZATION_NVP(tx);
// note that the reset automagically maintains the reference count
t.reset(tx);
ar.object_adopted();
}

// split non-intrusive serialization function member into separate
Expand Down
152 changes: 123 additions & 29 deletions src/basic_iarchive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// basic_archive.cpp:

// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// Copyright 2026 Gennaro Prota.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -31,6 +32,8 @@ namespace std{
#define BOOST_SERIALIZATION_SOURCE
#include <boost/serialization/config.hpp>

#include <boost/core/no_exceptions_support.hpp>

#include <boost/serialization/state_saver.hpp>
#include <boost/serialization/throw_exception.hpp>
#include <boost/serialization/tracking.hpp>
Expand Down Expand Up @@ -173,16 +176,44 @@ class basic_iarchive_impl {
void * object;
const basic_iserializer * bis;
version_type version;
// The object currently being loaded through a pointer: its index in
// object_id_vector, whether delete_created_pointers may reclaim it
// and whether its constructor has run yet.
std::size_t pointer_object_id;
bool pointer_reclaimable;
bool pointer_constructed;
pending() :
object(NULL),
bis(NULL),
version(0)
version(0),
pointer_object_id(0),
pointer_reclaimable(false),
pointer_constructed(false)
{}
} m_pending;

// Set while a created pointer is being loaded. Only the outermost such
// load may be reclaimed by delete_created_pointers: anything created
// below it is reachable from it, so freeing it runs the destructors of
// the objects it owns.
bool m_loading_created_pointer;

// The object which the pointer load that just finished created and
// flagged for reclamation, if any. Lets an owning smart pointer take
// that object over: see object_adopted().
struct last_created {
std::size_t object_id;
bool reclaimable;
last_created() :
object_id(0),
reclaimable(false)
{}
} m_last_created;

basic_iarchive_impl(unsigned int flags) :
m_archive_library_version(BOOST_ARCHIVE_VERSION()),
m_flags(flags)
m_flags(flags),
m_loading_created_pointer(false)
{}
void set_library_version(library_version_type archive_library_version){
m_archive_library_version = archive_library_version;
Expand Down Expand Up @@ -212,6 +243,22 @@ class basic_iarchive_impl {
next_object_pointer(void * t){
m_pending.object = t;
}
void
object_constructed(){
m_pending.pointer_constructed = true;
if(m_pending.pointer_reclaimable){
object_id_vector[m_pending.pointer_object_id].loaded_as_pointer
= true;
}
}
void
object_adopted(){
if(m_last_created.reclaimable){
object_id_vector[m_last_created.object_id].loaded_as_pointer
= false;
m_last_created.reclaimable = false;
}
}
void delete_created_pointers();
class_id_type register_type(
const basic_pointer_iserializer & bpis
Expand Down Expand Up @@ -424,6 +471,10 @@ basic_iarchive_impl::load_pointer(
m_moveable_objects.is_pointer = true;
serialization::state_saver<bool> w(m_moveable_objects.is_pointer);

// An adopting smart pointer may only take over an object which this very
// call creates, so forget any object the previous one left behind.
m_last_created.reclaimable = false;

class_id_type cid;
load(ar, cid);

Expand Down Expand Up @@ -480,39 +531,72 @@ basic_iarchive_impl::load_pointer(
// save state
serialization::state_saver<object_id_type> w_start(m_moveable_objects.start);

// An object created by an enclosing pointer load is owned by that
// object, so only the outermost one is a candidate for reclamation.
const bool root = ! m_loading_created_pointer;
serialization::state_saver<bool> n(m_loading_created_pointer);
serialization::state_saver<std::size_t> p_id(m_pending.pointer_object_id);
serialization::state_saver<bool> p_rec(m_pending.pointer_reclaimable);
serialization::state_saver<bool> p_con(m_pending.pointer_constructed);
m_loading_created_pointer = true;
m_pending.pointer_reclaimable = false;
m_pending.pointer_constructed = false;

// allocate space on the heap for the object - to be constructed later
t = bpis_ptr->heap_allocation();
BOOST_ASSERT(NULL != t);

if(! tracking){
bpis_ptr->load_object_ptr(ar, t, co.file_version);
BOOST_TRY{
if(! tracking){
bpis_ptr->load_object_ptr(ar, t, co.file_version);
}
else{
serialization::state_saver<void *> x(m_pending.object);
serialization::state_saver<const basic_iserializer *> y(m_pending.bis);
serialization::state_saver<version_type> z(m_pending.version);

m_pending.bis = & bpis_ptr->get_basic_serializer();
m_pending.version = co.file_version;

// predict next object id to be created
const size_t ui = object_id_vector.size();

serialization::state_saver<object_id_type> w_end(m_moveable_objects.end);

// add to list of serialized objects so that we can properly handle
// cyclic structures
object_id_vector.push_back(aobject(t, cid));
m_pending.pointer_object_id = ui;
m_pending.pointer_reclaimable = root;

// remember that that the address of these elements could change
// when we make another call so don't use the address. Once the
// object has been constructed load_object_ptr calls back through
// object_constructed(), which flags it for reclamation by
// delete_created_pointers should loading its members throw.
bpis_ptr->load_object_ptr(
ar,
t,
m_pending.version
);
}
}
else{
serialization::state_saver<void *> x(m_pending.object);
serialization::state_saver<const basic_iserializer *> y(m_pending.bis);
serialization::state_saver<version_type> z(m_pending.version);

m_pending.bis = & bpis_ptr->get_basic_serializer();
m_pending.version = co.file_version;

// predict next object id to be created
const size_t ui = object_id_vector.size();

serialization::state_saver<object_id_type> w_end(m_moveable_objects.end);

// add to list of serialized objects so that we can properly handle
// cyclic structures
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
);
object_id_vector[ui].loaded_as_pointer = true;
BOOST_CATCH(...){
// The constructor never ran, so load_object_ptr has freed the raw
// storage. Clear the caller's pointer: otherwise the destructor of
// an enclosing object would delete storage which is already gone.
if(! m_pending.pointer_constructed){
t = NULL;
}
BOOST_RETHROW;
}
BOOST_CATCH_END

// The load succeeded: remember what it flagged, so that a smart pointer
// adopting the object can take responsibility for freeing it.
m_last_created.object_id = m_pending.pointer_object_id;
m_last_created.reclaimable = m_pending.pointer_reclaimable
&& m_pending.pointer_constructed;

return bpis_ptr;
}
Expand All @@ -532,6 +616,16 @@ basic_iarchive::next_object_pointer(void *t){
pimpl->next_object_pointer(t);
}

BOOST_ARCHIVE_DECL void
basic_iarchive::object_constructed(){
pimpl->object_constructed();
}

BOOST_ARCHIVE_DECL void
basic_iarchive::object_adopted(){
pimpl->object_adopted();
}

BOOST_ARCHIVE_DECL
basic_iarchive::basic_iarchive(unsigned int flags) :
pimpl(new basic_iarchive_impl(flags))
Expand Down
1 change: 1 addition & 0 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ test-suite "serialization" :
[ test-bsl-run_files test_forward_list : A : : [ requires cxx11_hdr_forward_list ] ] # BOOST_NO_CXX11_HDR_FORWARD_LIST
[ test-bsl-run_files test_forward_list_ptrs : A : : [ requires cxx11_hdr_forward_list ] ] # BOOST_NO_CXX11_HDR_FORWARD_LIST
[ test-bsl-run_files test_helper_support : : : [ requires std_wstreambuf ] ]
[ test-bsl-run_files test_interrupted_pointer_reclaim ]
[ test-bsl-run_files test_interrupts ]
[ test-bsl-run_files test_list : A ]
[ test-bsl-run_files test_list_ptrs : A ]
Expand Down
Loading