Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extension/apple/ExecuTorch/Exported/ExecuTorchTensor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ - (instancetype)initWithNativeInstance:(void *)nativeInstance {
- (instancetype)initWithTensor:(ExecuTorchTensor *)otherTensor {
ET_CHECK(otherTensor);
auto tensor = make_tensor_ptr(
**reinterpret_cast<TensorPtr *>(otherTensor.nativeInstance)
*reinterpret_cast<TensorPtr *>(otherTensor.nativeInstance)
);
return [self initWithNativeInstance:&tensor];
}
Expand Down
21 changes: 15 additions & 6 deletions extension/tensor/tensor_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,13 +338,16 @@ inline TensorPtr make_tensor_ptr(
* @param sizes Optional sizes override.
* @param dim_order Optional dimension order override.
* @param strides Optional strides override.
* @param deleter A custom deleter function for managing the lifetime of the
* original Tensor.
* @return A TensorPtr aliasing the same storage with requested metadata.
*/
inline TensorPtr make_tensor_ptr(
const executorch::aten::Tensor& tensor,
std::vector<executorch::aten::SizesType> sizes = {},
std::vector<executorch::aten::DimOrderType> dim_order = {},
std::vector<executorch::aten::StridesType> strides = {}) {
std::vector<executorch::aten::StridesType> strides = {},
std::function<void(void*)> deleter = nullptr) {
if (sizes.empty()) {
sizes.assign(tensor.sizes().begin(), tensor.sizes().end());
}
Expand Down Expand Up @@ -372,16 +375,18 @@ inline TensorPtr make_tensor_ptr(
tensor.mutable_data_ptr(),
std::move(dim_order),
std::move(strides),
tensor.scalar_type()
tensor.scalar_type(),
#ifndef USE_ATEN_LIB
,
tensor.shape_dynamism()
tensor.shape_dynamism(),
#else // USE_ATEN_LIB
executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND,
#endif // USE_ATEN_LIB
);
std::move(deleter));
}

/**
* Convenience overload identical to make_tensor_ptr(*tensor_ptr, ...).
* Keeps the original TensorPtr alive until the returned TensorPtr is destroyed.
*
* @param tensor_ptr The source tensor pointer to alias.
* @param sizes Optional sizes override.
Expand All @@ -395,7 +400,11 @@ inline TensorPtr make_tensor_ptr(
std::vector<executorch::aten::DimOrderType> dim_order = {},
std::vector<executorch::aten::StridesType> strides = {}) {
return make_tensor_ptr(
*tensor_ptr, std::move(sizes), std::move(dim_order), std::move(strides));
*tensor_ptr,
std::move(sizes),
std::move(dim_order),
std::move(strides),
[tensor_ptr](void*) {});
}

/**
Expand Down
68 changes: 68 additions & 0 deletions extension/tensor/test/tensor_ptr_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,74 @@ TEST_F(TensorPtrTest, TensorUint8dataTooLargeExpectDeath) {
ET_EXPECT_DEATH({ auto _ = make_tensor_ptr({2, 2}, std::move(data)); }, "");
}

TEST_F(TensorPtrTest, MakeViewFromTensorPtrKeepsSourceAlive) {
bool freed = false;
auto* data = new float[6]{1, 2, 3, 4, 5, 6};
auto tensor = make_tensor_ptr(
{2, 3},
data,
{},
{},
executorch::aten::ScalarType::Float,
executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND,
[&freed](void* p) {
freed = true;
delete[] static_cast<float*>(p);
});
auto view = make_tensor_ptr(tensor);
tensor.reset();
EXPECT_FALSE(freed);
EXPECT_EQ(view->const_data_ptr<float>()[0], 1.0f);
view->mutable_data_ptr<float>()[0] = 42.0f;
EXPECT_EQ(view->const_data_ptr<float>()[0], 42.0f);
view.reset();
EXPECT_TRUE(freed);
}

TEST_F(TensorPtrTest, MakeViewFromTensorDoesNotKeepAliveByDefault) {
bool freed = false;
auto* data = new float[2]{7.0f, 8.0f};
auto tensor = make_tensor_ptr(
{2, 1},
data,
{},
{},
executorch::aten::ScalarType::Float,
executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND,
[&freed](void* p) {
freed = true;
delete[] static_cast<float*>(p);
});
auto view = make_tensor_ptr(*tensor);
auto raw = view->const_data_ptr<float>();
EXPECT_EQ(raw, data);
tensor.reset();
EXPECT_TRUE(freed);
view.reset();
}

TEST_F(TensorPtrTest, MakeViewFromTensorWithDeleterKeepsAlive) {
bool freed = false;
auto* data = new float[3]{1.0f, 2.0f, 3.0f};
auto tensor = make_tensor_ptr(
{3},
data,
{},
{},
executorch::aten::ScalarType::Float,
executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND,
[&freed](void* p) {
freed = true;
delete[] static_cast<float*>(p);
});
auto view = make_tensor_ptr(*tensor, {}, {}, {}, [tensor](void*) {});
tensor.reset();
EXPECT_FALSE(freed);
EXPECT_EQ(view->const_data_ptr<float>()[2], 3.0f);
view.reset();
EXPECT_TRUE(freed);
}

TEST_F(TensorPtrTest, VectorFloatTooSmallExpectDeath) {
std::vector<float> data(9, 1.f);
ET_EXPECT_DEATH({ auto _ = make_tensor_ptr({2, 5}, std::move(data)); }, "");
Expand Down
Loading