Skip to content

Commit ecc9bb1

Browse files
authored
Serialize std::shared_ptr and std::weak_ptr (sstsimulator#1352)
1 parent 6db353e commit ecc9bb1

File tree

11 files changed

+1050
-11
lines changed

11 files changed

+1050
-11
lines changed

src/sst/core/Makefile.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ nobase_dist_sst_HEADERS = \
108108
serialization/impl/ser_buffer_accessor.h \
109109
serialization/impl/serialize_insertable.h \
110110
serialization/impl/serialize_optional.h \
111+
serialization/impl/ser_shared_ptr_tracker.h \
112+
serialization/impl/serialize_shared_ptr.h \
111113
serialization/impl/serialize_tuple.h \
112114
serialization/impl/serialize_unique_ptr.h \
113115
serialization/impl/serialize_utility.h \
@@ -236,10 +238,12 @@ sst_core_sources = \
236238
serialization/serializable.cc \
237239
serialization/serializer.cc \
238240
serialization/impl/unpacker.cc \
241+
serialization/impl/ser_shared_ptr_tracker.cc \
239242
serialization/statics.cc \
240243
serialization/impl/mapper.cc \
241244
serialization/impl/serialize_array.cc \
242245
serialization/impl/serialize_trivial.cc \
246+
serialization/impl/serialize_shared_ptr.cc \
243247
sstinfo.h \
244248
interfaces/TestEvent.cc \
245249
interfaces/stdMem.cc \

src/sst/core/serialization/impl/packer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "sst/core/serialization/impl/get_array_size.h"
2121
#include "sst/core/serialization/impl/ser_buffer_accessor.h"
22+
#include "sst/core/serialization/impl/ser_shared_ptr_tracker.h"
2223

2324
#include <cstdint>
2425
#include <cstring>
@@ -28,7 +29,7 @@
2829

2930
namespace SST::Core::Serialization::pvt {
3031

31-
class ser_packer : public ser_buffer_accessor
32+
class ser_packer : public ser_buffer_accessor, public ser_shared_ptr_packer
3233
{
3334
std::set<uintptr_t> pointer_set;
3435

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2009-2025 NTESS. Under the terms
2+
// of Contract DE-NA0003525 with NTESS, the U.S.
3+
// Government retains certain rights in this software.
4+
//
5+
// Copyright (c) 2009-2025, NTESS
6+
// All rights reserved.
7+
//
8+
// This file is part of the SST software package. For license
9+
// information, see the LICENSE file in the top level directory of the
10+
// distribution.
11+
12+
#include "sst_config.h"
13+
14+
#include "sst/core/output.h"
15+
#include "sst/core/serialization/serializer.h"
16+
17+
#include <cstddef>
18+
#include <memory>
19+
#include <utility>
20+
21+
namespace SST::Core::Serialization::pvt {
22+
23+
// Get the tag associated with a shared pointer's owner, and whether it is new
24+
std::pair<size_t, bool>
25+
ser_shared_ptr_packer::get_shared_ptr_owner_tag(const std::weak_ptr<const void>& ptr)
26+
{
27+
// Look for the owner of the control block in the map, creating a new owner tag if it doesn't exist
28+
// If there is no control block because ptr is empty, a tag of 0 is returned from the map
29+
auto [it, is_new_tag] = shared_ptr_map.try_emplace(ptr, owner_tag);
30+
31+
// If a new entry was added, increment the owner tag for the next time a new tag is added
32+
if ( is_new_tag ) ++owner_tag;
33+
34+
// Return the tag of the control block's owner and whether it is new
35+
return { it->second, is_new_tag };
36+
}
37+
38+
// Get a reference to the std::shared_ptr<void> owner associated with a tag, and whether it is new
39+
std::pair<std::shared_ptr<void>&, bool>
40+
ser_shared_ptr_unpacker::get_shared_ptr_owner(size_t tag)
41+
{
42+
// Should never get here if the tag is 0
43+
if ( tag != 0 ) {
44+
// Get the current largest tag
45+
size_t num_owners = shared_ptr_owners.size();
46+
47+
// If the tag has been seen before, return the owner associated with the tag
48+
if ( --tag < num_owners ) return { shared_ptr_owners[tag], false };
49+
50+
// The first time a new tag is seen, it must be 1 higher than the max seen so far (restricted growth sequence)
51+
// A null pointer and a NOP deleter are used so that the new std::shared_ptr owner has a unique control block
52+
// If the returned std::shared_ptr owner is assigned to, the pointer and deleter will change to another owner
53+
if ( tag == num_owners ) return { shared_ptr_owners.emplace_back(nullptr, [](void*) {}), true };
54+
}
55+
56+
Output::getDefaultObject().fatal(
57+
__LINE__, __FILE__, __func__, 1, "Serialization Error: std::shared_ptr ownership tag is out of order");
58+
}
59+
60+
} // namespace SST::Core::Serialization::pvt
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2009-2025 NTESS. Under the terms
2+
// of Contract DE-NA0003525 with NTESS, the U.S.
3+
// Government retains certain rights in this software.
4+
//
5+
// Copyright (c) 2009-2025, NTESS
6+
// All rights reserved.
7+
//
8+
// This file is part of the SST software package. For license
9+
// information, see the LICENSE file in the top level directory of the
10+
// distribution.
11+
12+
#ifndef SST_CORE_SERIALIZATION_IMPL_SER_SHARED_PTR_TRACKER_H
13+
#define SST_CORE_SERIALIZATION_IMPL_SER_SHARED_PTR_TRACKER_H
14+
15+
#ifndef SST_INCLUDING_SERIALIZER_H
16+
#warning \
17+
"The header file sst/core/serialization/impl/ser_shared_ptr_tracker.h should not be directly included as it is not part of the stable public API. The file is included in sst/core/serialization/serializer.h"
18+
#endif
19+
20+
#include "sst/core/output.h"
21+
22+
#include <cstddef>
23+
#include <deque>
24+
#include <map>
25+
#include <memory>
26+
#include <utility>
27+
28+
namespace SST::Core::Serialization::pvt {
29+
30+
class ser_shared_ptr_packer
31+
{
32+
// Map of ownership from std::shared_ptr or std::weak_ptr to a numeric tag representing the owner
33+
//
34+
// The std::owner_less<void> functor compares two std::shared_ptr or std::weak_ptr instances with .owner_before()
35+
// to form an ordering which std::map uses to detect whether two std::shared_ptr or std::weak_ptr pointers have
36+
// the same owner.
37+
//
38+
// The keys are of type std::weak_ptr<const void> because any std::shared_ptr or std::weak_ptr can be implicitly
39+
// converted to a std::weak_ptr<const void> and its ownership can be compared with any other std::shared_ptr or
40+
// std::weak_ptr.
41+
//
42+
// The map is initialized with an empty std::weak_ptr mapped to tag 0. This empty entry compares equal to all
43+
// std::shared_ptr or std::weak_ptr with an empty control block, which is different from having a use_count() == 0,
44+
// because a std::weak_ptr with a use_count() == 0 can still have an orphaned control block shared with others.
45+
std::map<std::weak_ptr<const void>, size_t, std::owner_less<void>> shared_ptr_map = { { {}, 0 } };
46+
47+
// Running owner tag which starts at 1 and increments each time a new std::shared_ptr owner is found
48+
size_t owner_tag = 1;
49+
50+
public:
51+
ser_shared_ptr_packer() = default;
52+
ser_shared_ptr_packer(const ser_shared_ptr_packer&) = delete;
53+
ser_shared_ptr_packer& operator=(const ser_shared_ptr_packer&) = delete;
54+
~ser_shared_ptr_packer() = default;
55+
56+
// Get the tag associated with a shared pointer's owner, and whether it is new
57+
std::pair<size_t, bool> get_shared_ptr_owner_tag(const std::weak_ptr<const void>& ptr);
58+
}; // class ser_shared_ptr_packer
59+
60+
class ser_shared_ptr_unpacker
61+
{
62+
// Array of std::shared_ptr owners indexed by tag.
63+
//
64+
// When a std::shared_ptr or std::weak_ptr is deserialized, numeric tags represent different owners.
65+
//
66+
// The first time a tag is seen, a std::shared_ptr<PARENT_TYPE> is allocated, and PARENT_TYPE is deserialized.
67+
//
68+
// This array keeps an copy of the std::shared_ptr<PARENT_TYPE> owner converted to std::shared_ptr<void> around
69+
// for every deserialization of std::shared_ptr or std::weak_ptr with the same ownership tag.
70+
//
71+
// std::deque is used because it never moves std::shared_ptr around after it is inserted, unlike std::vector.
72+
std::deque<std::shared_ptr<void>> shared_ptr_owners;
73+
74+
public:
75+
ser_shared_ptr_unpacker() = default;
76+
ser_shared_ptr_unpacker(const ser_shared_ptr_unpacker&) = delete;
77+
ser_shared_ptr_unpacker& operator=(const ser_shared_ptr_unpacker&) = delete;
78+
~ser_shared_ptr_unpacker() = default;
79+
80+
// Get the std::shared_ptr associated with a tag, and whether it is new
81+
std::pair<std::shared_ptr<void>&, bool> get_shared_ptr_owner(size_t tag);
82+
}; // class ser_shared_ptr_unpacker
83+
84+
} // namespace SST::Core::Serialization::pvt
85+
86+
#endif // SST_CORE_SERIALIZATION_IMPL_SER_SHARED_PTR_TRACKER_H
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2009-2025 NTESS. Under the terms
2+
// of Contract DE-NA0003525 with NTESS, the U.S.
3+
// Government retains certain rights in this software.
4+
//
5+
// Copyright (c) 2009-2025, NTESS
6+
// All rights reserved.
7+
//
8+
// This file is part of the SST software package. For license
9+
// information, see the LICENSE file in the top level directory of the
10+
// distribution.
11+
12+
#include "sst_config.h"
13+
14+
#include "sst/core/serialization/serialize.h"
15+
16+
#include <cstddef>
17+
#include <memory>
18+
19+
namespace SST::Core::Serialization {
20+
21+
// Instantiate some templates to make sure all combinations are compilable
22+
23+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int, int>>;
24+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int, int[], int>>;
25+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int, int[], size_t>>;
26+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int, size_t>>;
27+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int[], int>>;
28+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int[], int[], int>>;
29+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int[], int[], size_t>>;
30+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int[], size_t>>;
31+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int, int>>;
32+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int, int[], int>>;
33+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int, int[], size_t>>;
34+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int, size_t>>;
35+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int[], int[], int, std::shared_ptr<int[]>>>;
36+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int[], int[], int>>;
37+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int[], int[], size_t, std::shared_ptr<int[]>>>;
38+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int[], int[], size_t>>;
39+
40+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int, int[10], int>>;
41+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int, int[10], size_t>>;
42+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int[10], int>>;
43+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int[10], int[10], int>>;
44+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int[10], int[10], size_t>>;
45+
template class serialize_impl<pvt::shared_ptr_wrapper<std::shared_ptr, int[10], size_t>>;
46+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int, int[10], int>>;
47+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int, int[10], size_t>>;
48+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int[10], int[10], int, std::shared_ptr<int[10]>>>;
49+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int[10], int[10], int>>;
50+
template class serialize_impl<
51+
pvt::shared_ptr_wrapper<std::weak_ptr, int[10], int[10], size_t, std::shared_ptr<int[10]>>>;
52+
template class serialize_impl<pvt::shared_ptr_wrapper<std::weak_ptr, int[10], int[10], size_t>>;
53+
54+
} // namespace SST::Core::Serialization

0 commit comments

Comments
 (0)