Skip to content

Commit 23db0bc

Browse files
authored
Tensor view keeps original tensor alive. (#15056)
Summary: TensorPtr view created with TensorPtr should keep it alive to match ATen behavior. Differential Revision: D84512176
1 parent b9451c9 commit 23db0bc

File tree

3 files changed

+84
-7
lines changed

3 files changed

+84
-7
lines changed

extension/apple/ExecuTorch/Exported/ExecuTorchTensor.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ - (instancetype)initWithNativeInstance:(void *)nativeInstance {
129129
- (instancetype)initWithTensor:(ExecuTorchTensor *)otherTensor {
130130
ET_CHECK(otherTensor);
131131
auto tensor = make_tensor_ptr(
132-
**reinterpret_cast<TensorPtr *>(otherTensor.nativeInstance)
132+
*reinterpret_cast<TensorPtr *>(otherTensor.nativeInstance)
133133
);
134134
return [self initWithNativeInstance:&tensor];
135135
}

extension/tensor/tensor_ptr.h

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -339,13 +339,16 @@ inline TensorPtr make_tensor_ptr(
339339
* @param sizes Optional sizes override.
340340
* @param dim_order Optional dimension order override.
341341
* @param strides Optional strides override.
342+
* @param deleter A custom deleter function for managing the lifetime of the
343+
* original Tensor.
342344
* @return A TensorPtr aliasing the same storage with requested metadata.
343345
*/
344346
inline TensorPtr make_tensor_ptr(
345347
const executorch::aten::Tensor& tensor,
346348
std::vector<executorch::aten::SizesType> sizes = {},
347349
std::vector<executorch::aten::DimOrderType> dim_order = {},
348-
std::vector<executorch::aten::StridesType> strides = {}) {
350+
std::vector<executorch::aten::StridesType> strides = {},
351+
std::function<void(void*)> deleter = nullptr) {
349352
if (sizes.empty()) {
350353
sizes.assign(tensor.sizes().begin(), tensor.sizes().end());
351354
}
@@ -373,16 +376,18 @@ inline TensorPtr make_tensor_ptr(
373376
tensor.mutable_data_ptr(),
374377
std::move(dim_order),
375378
std::move(strides),
376-
tensor.scalar_type()
379+
tensor.scalar_type(),
377380
#ifndef USE_ATEN_LIB
378-
,
379-
tensor.shape_dynamism()
381+
tensor.shape_dynamism(),
382+
#else // USE_ATEN_LIB
383+
executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND,
380384
#endif // USE_ATEN_LIB
381-
);
385+
std::move(deleter));
382386
}
383387

384388
/**
385389
* Convenience overload identical to make_tensor_ptr(*tensor_ptr, ...).
390+
* Keeps the original TensorPtr alive until the returned TensorPtr is destroyed.
386391
*
387392
* @param tensor_ptr The source tensor pointer to alias.
388393
* @param sizes Optional sizes override.
@@ -396,7 +401,11 @@ inline TensorPtr make_tensor_ptr(
396401
std::vector<executorch::aten::DimOrderType> dim_order = {},
397402
std::vector<executorch::aten::StridesType> strides = {}) {
398403
return make_tensor_ptr(
399-
*tensor_ptr, std::move(sizes), std::move(dim_order), std::move(strides));
404+
*tensor_ptr,
405+
std::move(sizes),
406+
std::move(dim_order),
407+
std::move(strides),
408+
[tensor_ptr](void*) {});
400409
}
401410

402411
/**

extension/tensor/test/tensor_ptr_test.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,74 @@ TEST_F(TensorPtrTest, TensorUint8dataTooLargeExpectDeath) {
10381038
ET_EXPECT_DEATH({ auto _ = make_tensor_ptr({2, 2}, std::move(data)); }, "");
10391039
}
10401040

1041+
TEST_F(TensorPtrTest, MakeViewFromTensorPtrKeepsSourceAlive) {
1042+
bool freed = false;
1043+
auto* data = new float[6]{1, 2, 3, 4, 5, 6};
1044+
auto tensor = make_tensor_ptr(
1045+
{2, 3},
1046+
data,
1047+
{},
1048+
{},
1049+
executorch::aten::ScalarType::Float,
1050+
executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND,
1051+
[&freed](void* p) {
1052+
freed = true;
1053+
delete[] static_cast<float*>(p);
1054+
});
1055+
auto view = make_tensor_ptr(tensor);
1056+
tensor.reset();
1057+
EXPECT_FALSE(freed);
1058+
EXPECT_EQ(view->const_data_ptr<float>()[0], 1.0f);
1059+
view->mutable_data_ptr<float>()[0] = 42.0f;
1060+
EXPECT_EQ(view->const_data_ptr<float>()[0], 42.0f);
1061+
view.reset();
1062+
EXPECT_TRUE(freed);
1063+
}
1064+
1065+
TEST_F(TensorPtrTest, MakeViewFromTensorDoesNotKeepAliveByDefault) {
1066+
bool freed = false;
1067+
auto* data = new float[2]{7.0f, 8.0f};
1068+
auto tensor = make_tensor_ptr(
1069+
{2, 1},
1070+
data,
1071+
{},
1072+
{},
1073+
executorch::aten::ScalarType::Float,
1074+
executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND,
1075+
[&freed](void* p) {
1076+
freed = true;
1077+
delete[] static_cast<float*>(p);
1078+
});
1079+
auto view = make_tensor_ptr(*tensor);
1080+
auto raw = view->const_data_ptr<float>();
1081+
EXPECT_EQ(raw, data);
1082+
tensor.reset();
1083+
EXPECT_TRUE(freed);
1084+
view.reset();
1085+
}
1086+
1087+
TEST_F(TensorPtrTest, MakeViewFromTensorWithDeleterKeepsAlive) {
1088+
bool freed = false;
1089+
auto* data = new float[3]{1.0f, 2.0f, 3.0f};
1090+
auto tensor = make_tensor_ptr(
1091+
{3},
1092+
data,
1093+
{},
1094+
{},
1095+
executorch::aten::ScalarType::Float,
1096+
executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND,
1097+
[&freed](void* p) {
1098+
freed = true;
1099+
delete[] static_cast<float*>(p);
1100+
});
1101+
auto view = make_tensor_ptr(*tensor, {}, {}, {}, [tensor](void*) {});
1102+
tensor.reset();
1103+
EXPECT_FALSE(freed);
1104+
EXPECT_EQ(view->const_data_ptr<float>()[2], 3.0f);
1105+
view.reset();
1106+
EXPECT_TRUE(freed);
1107+
}
1108+
10411109
TEST_F(TensorPtrTest, VectorFloatTooSmallExpectDeath) {
10421110
std::vector<float> data(9, 1.f);
10431111
ET_EXPECT_DEATH({ auto _ = make_tensor_ptr({2, 5}, std::move(data)); }, "");

0 commit comments

Comments
 (0)