Skip to content

Commit ad9802b

Browse files
author
Terry Cojean
committed
Fix issues pointed by reviews.
1 parent 673e71f commit ad9802b

2 files changed

Lines changed: 93 additions & 28 deletions

File tree

core/test/base/array.cpp

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3333
#include <ginkgo/core/base/array.hpp>
3434

3535

36+
#include <algorithm>
37+
38+
3639
#include <gtest/gtest.h>
3740

3841

@@ -284,14 +287,43 @@ TYPED_TEST(Array, CanBeResized)
284287
}
285288

286289

287-
TYPED_TEST(Array, VewCanBeResetNotResized)
290+
TYPED_TEST(Array, ViewCannotBeResized)
288291
{
289292
TypeParam data[] = {1, 2, 3};
290293
auto view = gko::Array<TypeParam>::view(this->exec, 3, data);
291-
view.resize_and_reset(1);
292294

293-
EXPECT_EQ(view.get_const_data(), nullptr);
294-
ASSERT_EQ(view.get_num_elems(), 1);
295+
EXPECT_THROW(view.resize_and_reset(1), gko::NotSupported);
296+
EXPECT_EQ(view.get_num_elems(), 3);
297+
ASSERT_EQ(view.get_data()[0], TypeParam{1});
298+
}
299+
300+
301+
template <typename T>
302+
class my_null_deleter {
303+
public:
304+
using pointer = T *;
305+
306+
void operator()(pointer) const noexcept {}
307+
};
308+
309+
template <typename T>
310+
class my_null_deleter<T[]> {
311+
public:
312+
using pointer = T[];
313+
314+
void operator()(pointer) const noexcept {}
315+
};
316+
317+
318+
TYPED_TEST(Array, CustomDeleterCannotBeResized)
319+
{
320+
TypeParam data[] = {1, 2, 3};
321+
auto view_custom_deleter = gko::Array<TypeParam>(
322+
this->exec, 3, data, my_null_deleter<TypeParam[]>{});
323+
324+
EXPECT_THROW(view_custom_deleter.resize_and_reset(1), gko::NotSupported);
325+
EXPECT_EQ(view_custom_deleter.get_num_elems(), 3);
326+
ASSERT_EQ(view_custom_deleter.get_data()[0], TypeParam{1});
295327
}
296328

297329

@@ -342,6 +374,7 @@ TYPED_TEST(Array, CopyArrayToArray)
342374
EXPECT_EQ(array.get_data()[2], TypeParam{2});
343375
EXPECT_EQ(array.get_data()[3], TypeParam{1});
344376
EXPECT_EQ(array.get_num_elems(), 4);
377+
EXPECT_NE(array.get_data(), array2.get_data());
345378
ASSERT_EQ(array2.get_num_elems(), 4);
346379
}
347380

@@ -363,6 +396,7 @@ TYPED_TEST(Array, CopyViewToView)
363396
EXPECT_EQ(data[2], TypeParam{2});
364397
EXPECT_EQ(view.get_num_elems(), 3);
365398
EXPECT_EQ(view2.get_num_elems(), 3);
399+
EXPECT_EQ(view2.get_data()[0], TypeParam{2});
366400
ASSERT_THROW(view2 = view_size4, gko::OutOfBoundsError);
367401
}
368402

@@ -407,9 +441,11 @@ TYPED_TEST(Array, MoveArrayToArray)
407441
{
408442
gko::Array<TypeParam> array(this->exec, {1, 2, 3});
409443
gko::Array<TypeParam> array2(this->exec, {5, 4, 2, 1});
444+
auto data2 = array2.get_data();
410445

411446
array = std::move(array2);
412447

448+
EXPECT_EQ(array.get_data(), data2);
413449
EXPECT_EQ(array.get_data()[0], TypeParam{5});
414450
EXPECT_EQ(array.get_data()[1], TypeParam{4});
415451
EXPECT_EQ(array.get_data()[2], TypeParam{2});
@@ -435,7 +471,12 @@ TYPED_TEST(Array, MoveViewToView)
435471
EXPECT_EQ(view.get_data()[2], TypeParam{2});
436472
EXPECT_EQ(view.get_num_elems(), 3);
437473
EXPECT_EQ(view2.get_data(), nullptr);
438-
ASSERT_EQ(view2.get_num_elems(), 0);
474+
EXPECT_EQ(view2.get_num_elems(), 0);
475+
EXPECT_NE(data, nullptr);
476+
EXPECT_EQ(data[0], TypeParam{1});
477+
EXPECT_EQ(data[1], TypeParam{2});
478+
EXPECT_EQ(data[2], TypeParam{3});
479+
ASSERT_EQ(data[3], TypeParam{4});
439480
}
440481

441482

@@ -468,13 +509,18 @@ TYPED_TEST(Array, MoveArrayToView)
468509
auto view = gko::Array<TypeParam>::view(this->exec, 3, data);
469510
gko::Array<TypeParam> array_size2(this->exec, {5, 4});
470511
gko::Array<TypeParam> array_size4(this->exec, {5, 4, 2, 1});
512+
auto size2_ptr = array_size2.get_data();
513+
auto size4_ptr = array_size4.get_data();
471514

472515
view = std::move(array_size2);
473516

474517
EXPECT_EQ(view.get_data()[0], TypeParam{5});
475518
EXPECT_EQ(view.get_data()[1], TypeParam{4});
476519
EXPECT_EQ(view.get_num_elems(), 2);
477-
EXPECT_NO_THROW(view = array_size4);
520+
EXPECT_NE(view.get_data(), data);
521+
EXPECT_EQ(view.get_data(), size2_ptr);
522+
EXPECT_NO_THROW(view = std::move(array_size4));
523+
EXPECT_EQ(view.get_data(), size4_ptr);
478524
EXPECT_EQ(array_size2.get_data(), nullptr);
479525
ASSERT_EQ(array_size2.get_num_elems(), 0);
480526
}

include/ginkgo/core/base/array.hpp

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,8 @@ class Array {
255255
* Creates an Array from existing memory.
256256
*
257257
* The Array does not take ownership of the memory, and will not deallocate
258-
* it once it goes out of scope.
258+
* it once it goes out of scope. This array type cannot use the function
259+
* `resize_and_reset` since it does not own the data it should resize.
259260
*
260261
* @param exec executor where `data` is located
261262
* @param num_elems number of elements in `data`
@@ -295,13 +296,11 @@ class Array {
295296
data_ = data_manager{nullptr, other.data_.get_deleter()};
296297
}
297298
if (other.get_executor() == nullptr) {
298-
this->resize_and_reset(0);
299+
this->clear();
299300
return *this;
300301
}
301-
// We allow resizing only if we are an array, we cannot resize a view!
302-
// If we are a view, we do bound checking to ensure the data can be
303-
// copied over.
304-
if (data_.get_deleter().target_type() != typeid(view_deleter)) {
302+
303+
if (this->is_owning()) {
305304
this->resize_and_reset(other.get_num_elems());
306305
} else {
307306
GKO_ENSURE_COMPATIBLE_BOUNDS(other.get_num_elems(),
@@ -322,12 +321,14 @@ class Array {
322321
* b = std::move(a);
323322
* ```
324323
* Depending on whether `a` and `b` are array or view, this happens:
325-
* + `a` and `b` are view, `b` becomes the only valid view of `a`
326-
* + `a` and `b` are array, `b` becomes the only valid array of `a`
327-
* + `a` is `a` view and `b` is an array, `b` frees its data and becomes the
328-
* only valid view of `a`
329-
* + `a` is an array and `b` is `a` view, `b` becomes the only valid array
330-
* of `a`
324+
* + `a` and `b` are views, `b` becomes the only valid view of `a`;
325+
* + `a` and `b` are arrays, `b` becomes the only valid array of `a`;
326+
* + `a` is a view and `b` is an array, `b` frees its data and becomes the
327+
* only valid view of `a` ();
328+
* + `a` is an array and `b` is a view, `b` becomes the only valid array
329+
* of `a`.
330+
*
331+
* In all the previous cases, `a` becomes invalid (e.g., a `nullptr`).
331332
*
332333
* This does not invoke the constructors of the elements, instead they are
333334
* copied as POD types.
@@ -349,15 +350,15 @@ class Array {
349350
data_ = data_manager{nullptr, other.data_.get_deleter()};
350351
}
351352
if (other.get_executor() == nullptr) {
352-
this->resize_and_reset(0);
353+
this->clear();
353354
return *this;
354355
}
355356
if (exec_ == other.get_executor()) {
356357
// same device, only move the pointer
357358
using std::swap;
358359
swap(data_, other.data_);
359360
swap(num_elems_, other.num_elems_);
360-
other.resize_and_reset(0);
361+
other.clear();
361362
} else {
362363
// different device, copy the data
363364
*this = other;
@@ -380,8 +381,8 @@ class Array {
380381

381382
/**
382383
* Resizes the array so it is able to hold the specified number of elements.
383-
* To a view, this only resets the pointer. Afterwards, the only way to use
384-
* a view in such a state is to `std::move` another view.
384+
* For a view and other non-owning Array types, this throws an exception
385+
* since these types cannot be resized.
385386
*
386387
* All data stored in the array will be lost.
387388
*
@@ -399,15 +400,16 @@ class Array {
399400
throw gko::NotSupported(__FILE__, __LINE__, __func__,
400401
"gko::Executor (nullptr)");
401402
}
403+
if (!this->is_owning()) {
404+
throw gko::NotSupported(__FILE__, __LINE__, __func__,
405+
"Non owning gko::Array cannot be resized.");
406+
}
402407

403-
num_elems_ = num_elems;
404-
// For views, we should never allocate any data, all we can do is unset
405-
// the pointer.
406-
if (num_elems > 0 &&
407-
data_.get_deleter().target_type() != typeid(view_deleter)) {
408+
if (num_elems > 0 && this->is_owning()) {
409+
num_elems_ = num_elems;
408410
data_.reset(exec_->alloc<value_type>(num_elems));
409411
} else {
410-
data_.reset(nullptr);
412+
this->clear();
411413
}
412414
}
413415

@@ -464,6 +466,23 @@ class Array {
464466
data_ = std::move(tmp.data_);
465467
}
466468

469+
/**
470+
* Tells whether this Array owns its data or not.
471+
*
472+
* Views do not own their data and this has multiple implications. They
473+
* cannot be resized since the data is not owned by the Array which stores a
474+
* view. It is also unclear whether custom deleter types are owning types as
475+
* they could be a user-created view-type, therefore only proper Array which
476+
* use the `default_deleter` are considered owning types.
477+
*
478+
* @return whether this Array can be resized or not.
479+
*/
480+
bool is_owning()
481+
{
482+
return data_.get_deleter().target_type() == typeid(default_deleter);
483+
}
484+
485+
467486
private:
468487
using data_manager =
469488
std::unique_ptr<value_type[], std::function<void(value_type[])>>;

0 commit comments

Comments
 (0)