Skip to content

Commit d52b790

Browse files
committed
Stop double protecting writable vectors
And fix memory leak when assigning a `writable::r_vector<T>&&` temporary value to an existing `writable::r_vector<T>`.
1 parent 1c9dbb6 commit d52b790

File tree

10 files changed

+42
-70
lines changed

10 files changed

+42
-70
lines changed

cpp11test/src/test-doubles.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,11 @@ context("doubles-C++") {
233233
w = z;
234234
expect_true(w.size() == 5);
235235
expect_true(w.data() != z.data());
236-
expect_true(w.is_altrep() == z.is_altrep());
236+
// Shallow duplication of objects of a very small size (like 1:5) don't result in
237+
// a new ALTREP object. Make sure we check ALTREP-ness of the newly duplicated object,
238+
// instead of just blindly inheriting the ALTREP-ness of the thing we duplicate.
239+
expect_true(w.is_altrep() != z.is_altrep());
240+
expect_true(w.is_altrep() == ALTREP(w.data()));
237241
}
238242

239243
test_that("writable::doubles(SEXP) move assignment") {

cpp11test/src/test-integers.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ context("integers-C++") {
223223
R_xlen_t after = cpp11::detail::store::count();
224224

225225
expect_true(before == 0);
226-
// TODO: This should be 1 but writable vectors are being double protected
227-
expect_true(after - before == 2);
226+
expect_true(after - before == 1);
228227
}
229228
}

cpp11test/src/test-list.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,6 @@ context("list-C++") {
178178
R_xlen_t after = cpp11::detail::store::count();
179179

180180
expect_true(before == 0);
181-
// TODO: This should be 1 but writable vectors are being double protected
182-
expect_true(after - before == 2);
181+
expect_true(after - before == 1);
183182
}
184183
}

inst/include/cpp11/doubles.hpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_allocVector, REAL
99
#include "cpp11/as.hpp" // for as_sexp
1010
#include "cpp11/named_arg.hpp" // for named_arg
11-
#include "cpp11/protect.hpp" // for SEXP, SEXPREC, REAL_ELT, R_Preserve...
11+
#include "cpp11/protect.hpp" // for safe
1212
#include "cpp11/r_vector.hpp" // for vector, vector<>::proxy, vector<>::...
1313
#include "cpp11/sexp.hpp" // for sexp
1414

@@ -84,7 +84,6 @@ template <>
8484
inline r_vector<double>::r_vector(std::initializer_list<named_arg> il)
8585
: cpp11::r_vector<double>(safe[Rf_allocVector](REALSXP, il.size())),
8686
capacity_(il.size()) {
87-
protect_ = detail::store::insert(data_);
8887
int n_protected = 0;
8988

9089
try {
@@ -100,7 +99,6 @@ inline r_vector<double>::r_vector(std::initializer_list<named_arg> il)
10099
UNPROTECT(n_protected);
101100
});
102101
} catch (const unwind_exception& e) {
103-
detail::store::release(protect_);
104102
UNPROTECT(n_protected);
105103
throw e;
106104
}

inst/include/cpp11/integers.hpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include "cpp11/as.hpp" // for as_sexp
1010
#include "cpp11/attribute_proxy.hpp" // for attribute_proxy
1111
#include "cpp11/named_arg.hpp" // for named_arg
12-
#include "cpp11/protect.hpp" // for store
12+
#include "cpp11/protect.hpp" // for safe
1313
#include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy
1414
#include "cpp11/sexp.hpp" // for sexp
1515

@@ -100,7 +100,6 @@ template <>
100100
inline r_vector<int>::r_vector(std::initializer_list<named_arg> il)
101101
: cpp11::r_vector<int>(safe[Rf_allocVector](INTSXP, il.size())),
102102
capacity_(il.size()) {
103-
protect_ = detail::store::insert(data_);
104103
int n_protected = 0;
105104

106105
try {
@@ -116,7 +115,6 @@ inline r_vector<int>::r_vector(std::initializer_list<named_arg> il)
116115
UNPROTECT(n_protected);
117116
});
118117
} catch (const unwind_exception& e) {
119-
detail::store::release(protect_);
120118
UNPROTECT(n_protected);
121119
throw e;
122120
}

inst/include/cpp11/list.hpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include "cpp11/R.hpp" // for SEXP, SEXPREC, SET_VECTOR_ELT
66
#include "cpp11/attribute_proxy.hpp" // for attribute_proxy
77
#include "cpp11/named_arg.hpp" // for named_arg
8-
#include "cpp11/protect.hpp" // for store
8+
#include "cpp11/protect.hpp" // for safe
99
#include "cpp11/r_string.hpp" // for r_string
1010
#include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy
1111
#include "cpp11/sexp.hpp" // for sexp
@@ -78,7 +78,6 @@ template <>
7878
inline r_vector<SEXP>::r_vector(std::initializer_list<SEXP> il)
7979
: cpp11::r_vector<SEXP>(safe[Rf_allocVector](VECSXP, il.size())),
8080
capacity_(il.size()) {
81-
protect_ = detail::store::insert(data_);
8281
auto it = il.begin();
8382
for (R_xlen_t i = 0; i < capacity_; ++i, ++it) {
8483
SET_VECTOR_ELT(data_, i, *it);
@@ -89,7 +88,6 @@ template <>
8988
inline r_vector<SEXP>::r_vector(std::initializer_list<named_arg> il)
9089
: cpp11::r_vector<SEXP>(safe[Rf_allocVector](VECSXP, il.size())),
9190
capacity_(il.size()) {
92-
protect_ = detail::store::insert(data_);
9391
int n_protected = 0;
9492

9593
try {
@@ -105,7 +103,6 @@ inline r_vector<SEXP>::r_vector(std::initializer_list<named_arg> il)
105103
UNPROTECT(n_protected);
106104
});
107105
} catch (const unwind_exception& e) {
108-
detail::store::release(protect_);
109106
UNPROTECT(n_protected);
110107
throw e;
111108
}

inst/include/cpp11/logicals.hpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_all...
88
#include "cpp11/attribute_proxy.hpp" // for attribute_proxy
99
#include "cpp11/named_arg.hpp" // for named_arg
10-
#include "cpp11/protect.hpp" // for store
10+
#include "cpp11/protect.hpp" // for safe
1111
#include "cpp11/r_bool.hpp" // for r_bool
1212
#include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy
1313
#include "cpp11/sexp.hpp" // for sexp
@@ -80,7 +80,6 @@ inline bool operator==(const r_vector<r_bool>::proxy& lhs, r_bool rhs) {
8080
template <>
8181
inline r_vector<r_bool>::r_vector(std::initializer_list<r_bool> il)
8282
: cpp11::r_vector<r_bool>(Rf_allocVector(LGLSXP, il.size())), capacity_(il.size()) {
83-
protect_ = detail::store::insert(data_);
8483
auto it = il.begin();
8584
for (R_xlen_t i = 0; i < capacity_; ++i, ++it) {
8685
SET_LOGICAL_ELT(data_, i, *it);
@@ -91,7 +90,6 @@ template <>
9190
inline r_vector<r_bool>::r_vector(std::initializer_list<named_arg> il)
9291
: cpp11::r_vector<r_bool>(safe[Rf_allocVector](LGLSXP, il.size())),
9392
capacity_(il.size()) {
94-
protect_ = detail::store::insert(data_);
9593
int n_protected = 0;
9694

9795
try {
@@ -107,7 +105,6 @@ inline r_vector<r_bool>::r_vector(std::initializer_list<named_arg> il)
107105
UNPROTECT(n_protected);
108106
});
109107
} catch (const unwind_exception& e) {
110-
detail::store::release(protect_);
111108
UNPROTECT(n_protected);
112109
throw e;
113110
}

inst/include/cpp11/r_vector.hpp

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,6 @@ using has_begin_fun = std::decay<decltype(*begin(std::declval<T>()))>;
214214
template <typename T>
215215
class r_vector : public cpp11::r_vector<T> {
216216
private:
217-
SEXP protect_ = R_NilValue;
218-
219217
// These are necessary because type names are not directly accessible in
220218
// template inheritance
221219
using typename cpp11::r_vector<T>::underlying_type;
@@ -224,6 +222,9 @@ class r_vector : public cpp11::r_vector<T> {
224222
using cpp11::r_vector<T>::data_p_;
225223
using cpp11::r_vector<T>::is_altrep_;
226224
using cpp11::r_vector<T>::length_;
225+
using cpp11::r_vector<T>::protect_;
226+
227+
using cpp11::r_vector<T>::get_p;
227228

228229
R_xlen_t capacity_ = 0;
229230

@@ -302,8 +303,6 @@ class r_vector : public cpp11::r_vector<T> {
302303

303304
explicit r_vector(const R_xlen_t size);
304305

305-
~r_vector();
306-
307306
r_vector(const r_vector& rhs);
308307
r_vector(r_vector&& rhs);
309308

@@ -660,27 +659,20 @@ inline typename r_vector<T>::iterator r_vector<T>::end() const {
660659

661660
template <typename T>
662661
inline r_vector<T>::r_vector(const SEXP& data)
663-
: cpp11::r_vector<T>(safe[Rf_shallow_duplicate](data)),
664-
protect_(detail::store::insert(data_)),
665-
capacity_(length_) {}
662+
: cpp11::r_vector<T>(safe[Rf_shallow_duplicate](data)), capacity_(length_) {}
666663

667664
template <typename T>
668665
inline r_vector<T>::r_vector(const SEXP& data, bool is_altrep)
669666
: cpp11::r_vector<T>(safe[Rf_shallow_duplicate](data), is_altrep),
670-
protect_(detail::store::insert(data_)),
671667
capacity_(length_) {}
672668

673669
template <typename T>
674670
inline r_vector<T>::r_vector(SEXP&& data)
675-
: cpp11::r_vector<T>(data),
676-
protect_(detail::store::insert(data_)),
677-
capacity_(length_) {}
671+
: cpp11::r_vector<T>(data), capacity_(length_) {}
678672

679673
template <typename T>
680674
inline r_vector<T>::r_vector(SEXP&& data, bool is_altrep)
681-
: cpp11::r_vector<T>(data, is_altrep),
682-
protect_(detail::store::insert(data_)),
683-
capacity_(length_) {}
675+
: cpp11::r_vector<T>(data, is_altrep), capacity_(length_) {}
684676

685677
template <typename T>
686678
template <typename Iter>
@@ -709,11 +701,6 @@ inline r_vector<T>::r_vector(const R_xlen_t size) : r_vector() {
709701
resize(size);
710702
}
711703

712-
template <typename T>
713-
inline r_vector<T>::~r_vector() {
714-
detail::store::release(protect_);
715-
}
716-
717704
#ifdef LONG_VECTOR_SUPPORT
718705
template <typename T>
719706
inline typename r_vector<T>::proxy r_vector<T>::operator[](const int pos) const {
@@ -793,22 +780,15 @@ inline typename r_vector<T>::iterator r_vector<T>::find(const r_string& name) co
793780

794781
template <typename T>
795782
inline r_vector<T>::r_vector(const r_vector<T>& rhs)
796-
: cpp11::r_vector<T>(safe[Rf_shallow_duplicate](rhs)),
797-
protect_(detail::store::insert(data_)),
798-
capacity_(rhs.capacity_) {}
783+
: cpp11::r_vector<T>(safe[Rf_shallow_duplicate](rhs)), capacity_(rhs.capacity_) {}
799784

800785
template <typename T>
801786
inline r_vector<T>::r_vector(r_vector<T>&& rhs)
802-
: cpp11::r_vector<T>(rhs), protect_(rhs.protect_), capacity_(rhs.capacity_) {
803-
rhs.data_ = R_NilValue;
804-
rhs.protect_ = R_NilValue;
805-
}
787+
: cpp11::r_vector<T>(rhs), capacity_(rhs.capacity_) {}
806788

807789
template <typename T>
808790
inline r_vector<T>::r_vector(const cpp11::r_vector<T>& rhs)
809-
: cpp11::r_vector<T>(safe[Rf_shallow_duplicate](rhs)),
810-
protect_(detail::store::insert(data_)),
811-
capacity_(rhs.length_) {}
791+
: cpp11::r_vector<T>(safe[Rf_shallow_duplicate](rhs)), capacity_(rhs.length_) {}
812792

813793
// We don't release the old object until the end in case we throw an exception
814794
// during the duplicate.
@@ -818,17 +798,21 @@ inline r_vector<T>& r_vector<T>::operator=(const r_vector<T>& rhs) {
818798
return *this;
819799
}
820800

821-
cpp11::r_vector<T>::operator=(rhs);
822-
823-
auto old_protect = protect_;
801+
SEXP old_protect = protect_;
824802

803+
// We are in writable mode, so we must duplicate the `rhs` (since it isn't a temporary
804+
// we can just take ownership of) and recompute the properties from the duplicate.
825805
data_ = safe[Rf_shallow_duplicate](rhs.data_);
826806
protect_ = detail::store::insert(data_);
807+
is_altrep_ = ALTREP(data_);
808+
data_p_ = get_p(is_altrep_, data_);
809+
length_ = rhs.length_;
827810

828-
detail::store::release(old_protect);
829-
811+
// Specific to writable
830812
capacity_ = rhs.capacity_;
831813

814+
detail::store::release(old_protect);
815+
832816
return *this;
833817
}
834818

@@ -838,17 +822,22 @@ inline r_vector<T>& r_vector<T>::operator=(r_vector<T>&& rhs) {
838822
return *this;
839823
}
840824

841-
cpp11::r_vector<T>::operator=(rhs);
842-
843825
SEXP old_protect = protect_;
844826

827+
// `rhs` is a temporary. Take ownership of its properties and then set its `protect_`
828+
// value to `R_NilValue` since we will handle that now. We don't want the `rhs`
829+
// destructor to release the object we are taking ownership of.
845830
data_ = rhs.data_;
846831
protect_ = rhs.protect_;
832+
is_altrep_ = rhs.is_altrep_;
833+
data_p_ = rhs.data_p_;
834+
length_ = rhs.length_;
847835

848-
detail::store::release(old_protect);
849-
836+
// Specific to writable
850837
capacity_ = rhs.capacity_;
851838

839+
detail::store::release(old_protect);
840+
852841
rhs.data_ = R_NilValue;
853842
rhs.protect_ = R_NilValue;
854843

inst/include/cpp11/raws.hpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "cpp11/R.hpp" // for RAW, SEXP, SEXPREC, Rf_allocVector
99
#include "cpp11/attribute_proxy.hpp" // for attribute_proxy
1010
#include "cpp11/named_arg.hpp" // for named_arg
11-
#include "cpp11/protect.hpp" // for store
11+
#include "cpp11/protect.hpp" // for safe
1212
#include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy
1313
#include "cpp11/sexp.hpp" // for sexp
1414

@@ -88,7 +88,6 @@ template <>
8888
inline r_vector<uint8_t>::r_vector(std::initializer_list<uint8_t> il)
8989
: cpp11::r_vector<uint8_t>(safe[Rf_allocVector](RAWSXP, il.size())),
9090
capacity_(il.size()) {
91-
protect_ = detail::store::insert(data_);
9291
auto it = il.begin();
9392
for (R_xlen_t i = 0; i < capacity_; ++i, ++it) {
9493
data_p_[i] = *it;
@@ -99,7 +98,6 @@ template <>
9998
inline r_vector<uint8_t>::r_vector(std::initializer_list<named_arg> il)
10099
: cpp11::r_vector<uint8_t>(safe[Rf_allocVector](RAWSXP, il.size())),
101100
capacity_(il.size()) {
102-
protect_ = detail::store::insert(data_);
103101
int n_protected = 0;
104102

105103
try {
@@ -116,7 +114,6 @@ inline r_vector<uint8_t>::r_vector(std::initializer_list<named_arg> il)
116114
UNPROTECT(n_protected);
117115
});
118116
} catch (const unwind_exception& e) {
119-
detail::store::release(protect_);
120117
UNPROTECT(n_protected);
121118
throw e;
122119
}

inst/include/cpp11/strings.hpp

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include "cpp11/as.hpp" // for as_sexp
88
#include "cpp11/attribute_proxy.hpp" // for attribute_proxy
99
#include "cpp11/named_arg.hpp" // for named_arg
10-
#include "cpp11/protect.hpp" // for store
10+
#include "cpp11/protect.hpp" // for safe
1111
#include "cpp11/r_string.hpp" // for r_string
1212
#include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy
1313
#include "cpp11/sexp.hpp" // for sexp
@@ -94,19 +94,15 @@ inline SEXP alloc_if_charsxp(const SEXP data) {
9494

9595
template <>
9696
inline r_vector<r_string>::r_vector(const SEXP& data)
97-
: cpp11::r_vector<r_string>(alloc_or_copy(data)),
98-
protect_(detail::store::insert(data_)),
99-
capacity_(length_) {
97+
: cpp11::r_vector<r_string>(alloc_or_copy(data)), capacity_(length_) {
10098
if (TYPEOF(data) == CHARSXP) {
10199
SET_STRING_ELT(data_, 0, data);
102100
}
103101
}
104102

105103
template <>
106104
inline r_vector<r_string>::r_vector(SEXP&& data)
107-
: cpp11::r_vector<r_string>(alloc_if_charsxp(data)),
108-
protect_(detail::store::insert(data_)),
109-
capacity_(length_) {
105+
: cpp11::r_vector<r_string>(alloc_if_charsxp(data)), capacity_(length_) {
110106
if (TYPEOF(data) == CHARSXP) {
111107
SET_STRING_ELT(data_, 0, data);
112108
}
@@ -120,7 +116,6 @@ template <>
120116
inline r_vector<r_string>::r_vector(std::initializer_list<named_arg> il)
121117
: cpp11::r_vector<r_string>(safe[Rf_allocVector](STRSXP, il.size())),
122118
capacity_(il.size()) {
123-
protect_ = detail::store::insert(data_);
124119
int n_protected = 0;
125120

126121
try {
@@ -136,7 +131,6 @@ inline r_vector<r_string>::r_vector(std::initializer_list<named_arg> il)
136131
UNPROTECT(n_protected);
137132
});
138133
} catch (const unwind_exception& e) {
139-
detail::store::release(protect_);
140134
UNPROTECT(n_protected);
141135
throw e;
142136
}

0 commit comments

Comments
 (0)