Commit 90607a25 authored by Nathan Bronson's avatar Nathan Bronson Committed by Facebook GitHub Bot

optimized helper for constructing aliasing std::weak_ptr

Summary:
`std::shared_ptr` has an aliasing constructor, but `std::weak_ptr`
has none.  This diff implements a helper function that implements the
functionality.  For libstdc++ it uses ABI-specific hacks to avoid the
need to construct a temporary `std::shared_ptr`.

Reviewed By: yfeldblum

Differential Revision: D23580416

fbshipit-source-id: 71712270519b0e7522d8019a5c356679c7dc5397
parent 704c7ec4
......@@ -346,7 +346,55 @@ std::shared_ptr<T> to_shared_ptr(std::unique_ptr<T, D>&& ptr) {
*/
template <typename T>
std::weak_ptr<T> to_weak_ptr(const std::shared_ptr<T>& ptr) {
return std::weak_ptr<T>(ptr);
return ptr;
}
#if __GLIBCXX__
namespace detail {
void weak_ptr_set_stored_ptr(std::weak_ptr<void>& w, void* ptr);
template <typename Tag, void* std::__weak_ptr<void>::*WeakPtr_Ptr_Field>
struct GenerateWeakPtrInternalsAccessor {
friend void weak_ptr_set_stored_ptr(std::weak_ptr<void>& w, void* ptr) {
w.*WeakPtr_Ptr_Field = ptr;
}
};
// Each template instantiation of GenerateWeakPtrInternalsAccessor must
// be a new type, to avoid ODR problems. We do this by tagging it with
// a type from an anon namespace.
namespace {
struct MemoryAnonTag {};
} // namespace
template struct GenerateWeakPtrInternalsAccessor<
MemoryAnonTag,
&std::__weak_ptr<void>::_M_ptr>;
} // namespace detail
#endif
/**
* to_weak_ptr_aliasing
*
* Like to_weak_ptr, but arranges that lock().get() on the returned
* pointer points to ptr rather than r.get().
*
* Equivalent to:
*
* to_weak_ptr(std::shared_ptr<U>(r, ptr))
*
* For libstdc++, ABI-specific tricks are used to optimize the
* implementation.
*/
template <typename T, typename U>
std::weak_ptr<U> to_weak_ptr_aliasing(const std::shared_ptr<T>& r, U* ptr) {
#if __GLIBCXX__
std::weak_ptr<void> wv(r);
detail::weak_ptr_set_stored_ptr(wv, ptr);
return reinterpret_cast<std::weak_ptr<U>&&>(wv);
#else
return std::shared_ptr<U>(r, ptr);
#endif
}
/**
......
......@@ -25,7 +25,9 @@
#include <folly/ConstexprMath.h>
#include <folly/String.h>
#include <folly/lang/Keep.h>
#include <folly/memory/Arena.h>
#include <folly/portability/Asm.h>
#include <folly/portability/GMock.h>
#include <folly/portability/GTest.h>
......@@ -91,6 +93,73 @@ TEST(to_weak_ptr, example) {
EXPECT_EQ(3, (to_weak_ptr(decltype(s)(s)).lock(), s.use_count())) << "rvalue";
}
// These are here to make it easy to double-check the assembly
// for to_weak_ptr_aliasing
extern "C" FOLLY_KEEP void check_to_weak_ptr_aliasing(
std::shared_ptr<void> const& s,
void* a) {
auto w = folly::to_weak_ptr_aliasing(s, a);
asm_volatile_memory();
asm_volatile_pause();
}
extern "C" FOLLY_KEEP void check_to_weak_ptr_aliasing_fallback(
std::shared_ptr<void> const& s,
void* a) {
auto w = folly::to_weak_ptr(std::shared_ptr<void>(s, a));
asm_volatile_memory();
asm_volatile_pause();
}
TEST(to_weak_ptr_aliasing, active) {
using S = std::pair<int, int>;
auto s = std::make_shared<S>();
s->first = 10;
s->second = 20;
EXPECT_EQ(s.use_count(), 1);
auto w = folly::to_weak_ptr_aliasing(s, &s->second);
EXPECT_EQ(s.use_count(), 1);
EXPECT_EQ(*w.lock(), s->second);
auto locked = w.lock();
EXPECT_EQ(s.use_count(), 2);
locked.reset();
auto w2 = w;
EXPECT_EQ(*w2.lock(), s->second);
w2.reset();
auto w3 = std::move(w);
EXPECT_EQ(*w3.lock(), s->second);
std::shared_ptr<int> s2(s, &s->second);
std::shared_ptr<int> s3 = w3.lock();
EXPECT_TRUE(s2 == s3);
EXPECT_FALSE(s2.owner_before(s));
EXPECT_FALSE(s.owner_before(s2));
EXPECT_FALSE(w3.owner_before(s));
EXPECT_FALSE(s.owner_before(w3));
s.reset();
s2.reset();
s3.reset();
EXPECT_TRUE(w3.expired());
}
TEST(to_weak_ptr_aliasing, inactive) {
using S = std::pair<int, int>;
std::shared_ptr<S> s;
EXPECT_EQ(s.use_count(), 0);
S tmp;
auto w = folly::to_weak_ptr_aliasing(s, &tmp.second);
EXPECT_EQ(s.use_count(), 0);
EXPECT_EQ(w.use_count(), 0);
EXPECT_TRUE(w.expired());
auto locked = w.lock();
EXPECT_EQ(locked.get(), nullptr);
EXPECT_EQ(locked.use_count(), 0);
}
TEST(copy_to_unique_ptr, example) {
std::unique_ptr<int> s = copy_to_unique_ptr(17);
EXPECT_EQ(17, *s);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment