Skip to content

Commit 86a6a3e

Browse files
authored
Fix memory leak with move assignment operator (#366)
1 parent 67a92bb commit 86a6a3e

File tree

4 files changed

+42
-1
lines changed

4 files changed

+42
-1
lines changed

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# cpp11 (development version)
22

3+
* Fixed a memory leak with the `cpp11::writable::r_vector` move assignment
4+
operator (#338).
5+
36
* The approach for the protection list managed by cpp11 has been tweaked
47
slightly. In 0.4.6, we changed to an approach that creates one protection list
58
per compilation unit, but we now believe we've found an approach that is

cpp11test/src/test-integers.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "cpp11/doubles.hpp"
33
#include "cpp11/function.hpp"
44
#include "cpp11/integers.hpp"
5+
#include "cpp11/protect.hpp"
56
#include "cpp11/strings.hpp"
67

78
#include <testthat.h>
@@ -207,4 +208,22 @@ context("integers-C++") {
207208
int y = NA_INTEGER;
208209
expect_true(cpp11::is_na(y));
209210
}
211+
212+
test_that("writable integer vector temporary isn't leaked (#338)") {
213+
R_xlen_t before = cpp11::detail::store::count();
214+
215+
// +1 from `x` allocation
216+
cpp11::writable::integers x(1);
217+
218+
// Calls move assignment operator `operator=(r_vector<T>&& rhs)`
219+
// +1 from `rhs` allocation and move into `x`
220+
// -1 from old `x` release
221+
x = cpp11::writable::integers(1);
222+
223+
R_xlen_t after = cpp11::detail::store::count();
224+
225+
expect_true(before == 0);
226+
// TODO: This should be 1 but writable vectors are being double protected
227+
expect_true(after - before == 2);
228+
}
210229
}

cpp11test/src/test-list.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "cpp11/integers.hpp"
33
#include "cpp11/list.hpp"
44
#include "cpp11/logicals.hpp"
5+
#include "cpp11/protect.hpp"
56
#include "cpp11/raws.hpp"
67
#include "cpp11/strings.hpp"
78

@@ -162,4 +163,22 @@ context("list-C++") {
162163
expect_true(Rf_xlength(y) == 0);
163164
expect_true(y != R_NilValue);
164165
}
166+
167+
test_that("writable list vector temporary isn't leaked (#338)") {
168+
R_xlen_t before = cpp11::detail::store::count();
169+
170+
// +1 from `x` allocation
171+
cpp11::writable::list x(1);
172+
173+
// Calls move assignment operator `operator=(r_vector<T>&& rhs)`
174+
// +1 from `rhs` allocation and move into `x`
175+
// -1 from old `x` release
176+
x = cpp11::writable::list(1);
177+
178+
R_xlen_t after = cpp11::detail::store::count();
179+
180+
expect_true(before == 0);
181+
// TODO: This should be 1 but writable vectors are being double protected
182+
expect_true(after - before == 2);
183+
}
165184
}

inst/include/cpp11/r_vector.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ inline r_vector<T>& r_vector<T>::operator=(r_vector<T>&& rhs) {
843843
SEXP old_protect = protect_;
844844

845845
data_ = rhs.data_;
846-
protect_ = detail::store::insert(data_);
846+
protect_ = rhs.protect_;
847847

848848
detail::store::release(old_protect);
849849

0 commit comments

Comments
 (0)