Commit 8447ac76 authored by Kenton Varda's avatar Kenton Varda

Add Array::attach() and ArrayPtr::attach().

Array::attach() is like Own::attach().

ArrayPtr::attach() promotes an ArrayPtr to an Array by attaching other objects to it. (Hopefully one of those objects actually owns the underlying array data.)
parent 68a9815f
...@@ -385,5 +385,120 @@ KJ_TEST("kj::arr()") { ...@@ -385,5 +385,120 @@ KJ_TEST("kj::arr()") {
} }
#endif #endif
struct DestructionOrderRecorder {
DestructionOrderRecorder(uint& counter, uint& recordTo)
: counter(counter), recordTo(recordTo) {}
~DestructionOrderRecorder() {
recordTo = ++counter;
}
uint& counter;
uint& recordTo;
};
TEST(Array, Attach) {
uint counter = 0;
uint destroyed1 = 0;
uint destroyed2 = 0;
uint destroyed3 = 0;
auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1);
auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2);
auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3);
auto builder = kj::heapArrayBuilder<Own<DestructionOrderRecorder>>(1);
builder.add(kj::mv(obj1));
auto arr = builder.finish();
auto ptr = arr.begin();
Array<Own<DestructionOrderRecorder>> combined = arr.attach(kj::mv(obj2), kj::mv(obj3));
KJ_EXPECT(combined.begin() == ptr);
KJ_EXPECT(obj1.get() == nullptr);
KJ_EXPECT(obj2.get() == nullptr);
KJ_EXPECT(obj3.get() == nullptr);
KJ_EXPECT(destroyed1 == 0);
KJ_EXPECT(destroyed2 == 0);
KJ_EXPECT(destroyed3 == 0);
combined = nullptr;
KJ_EXPECT(destroyed1 == 1, destroyed1);
KJ_EXPECT(destroyed2 == 2, destroyed2);
KJ_EXPECT(destroyed3 == 3, destroyed3);
}
TEST(Array, AttachNested) {
uint counter = 0;
uint destroyed1 = 0;
uint destroyed2 = 0;
uint destroyed3 = 0;
auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1);
auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2);
auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3);
auto builder = kj::heapArrayBuilder<Own<DestructionOrderRecorder>>(1);
builder.add(kj::mv(obj1));
auto arr = builder.finish();
auto ptr = arr.begin();
Array<Own<DestructionOrderRecorder>> combined = arr.attach(kj::mv(obj2)).attach(kj::mv(obj3));
KJ_EXPECT(combined.begin() == ptr);
KJ_EXPECT(obj1.get() == nullptr);
KJ_EXPECT(obj2.get() == nullptr);
KJ_EXPECT(obj3.get() == nullptr);
KJ_EXPECT(destroyed1 == 0);
KJ_EXPECT(destroyed2 == 0);
KJ_EXPECT(destroyed3 == 0);
combined = nullptr;
KJ_EXPECT(destroyed1 == 1, destroyed1);
KJ_EXPECT(destroyed2 == 2, destroyed2);
KJ_EXPECT(destroyed3 == 3, destroyed3);
}
TEST(Array, AttachFromArrayPtr) {
uint counter = 0;
uint destroyed1 = 0;
uint destroyed2 = 0;
uint destroyed3 = 0;
auto obj1 = kj::heap<DestructionOrderRecorder>(counter, destroyed1);
auto obj2 = kj::heap<DestructionOrderRecorder>(counter, destroyed2);
auto obj3 = kj::heap<DestructionOrderRecorder>(counter, destroyed3);
auto builder = kj::heapArrayBuilder<Own<DestructionOrderRecorder>>(1);
builder.add(kj::mv(obj1));
auto arr = builder.finish();
auto ptr = arr.begin();
Array<Own<DestructionOrderRecorder>> combined =
arr.asPtr().attach(kj::mv(obj2)).attach(kj::mv(obj3));
KJ_EXPECT(arr != nullptr);
KJ_EXPECT(combined.begin() == ptr);
KJ_EXPECT(obj1.get() == nullptr);
KJ_EXPECT(obj2.get() == nullptr);
KJ_EXPECT(obj3.get() == nullptr);
KJ_EXPECT(destroyed1 == 0);
KJ_EXPECT(destroyed2 == 0);
KJ_EXPECT(destroyed3 == 0);
combined = nullptr;
KJ_EXPECT(destroyed2 == 1, destroyed2);
KJ_EXPECT(destroyed3 == 2, destroyed3);
arr = nullptr;
KJ_EXPECT(destroyed1 == 3, destroyed1);
}
} // namespace } // namespace
} // namespace kj } // namespace kj
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
#pragma GCC system_header #pragma GCC system_header
#endif #endif
#include "common.h" #include "memory.h"
#include <string.h> #include <string.h>
#include <initializer_list> #include <initializer_list>
...@@ -234,6 +234,10 @@ public: ...@@ -234,6 +234,10 @@ public:
return *this; return *this;
} }
template <typename... Attachments>
Array<T> attach(Attachments&&... attachments) KJ_WARN_UNUSED_RESULT;
// Like Own<T>::attach(), but attaches to an Array.
private: private:
T* ptr; T* ptr;
size_t size_; size_t size_;
...@@ -834,4 +838,48 @@ inline Array<Decay<T>> arr(T&& param1, Params&&... params) { ...@@ -834,4 +838,48 @@ inline Array<Decay<T>> arr(T&& param1, Params&&... params) {
} }
#endif #endif
namespace _ { // private
template <typename... T>
struct ArrayDisposableOwnedBundle final: public ArrayDisposer, public OwnedBundle<T...> {
ArrayDisposableOwnedBundle(T&&... values): OwnedBundle<T...>(kj::fwd<T>(values)...) {}
void disposeImpl(void*, size_t, size_t, size_t, void (*)(void*)) const override { delete this; }
};
} // namespace _ (private)
template <typename T>
template <typename... Attachments>
Array<T> Array<T>::attach(Attachments&&... attachments) {
T* ptrCopy = ptr;
KJ_IREQUIRE(ptrCopy != nullptr, "cannot attach to null pointer");
// HACK: If someone accidentally calls .attach() on a null pointer in opt mode, try our best to
// accomplish reasonable behavior: We turn the pointer non-null but still invalid, so that the
// disposer will still be called when the pointer goes out of scope.
if (ptrCopy == nullptr) ptrCopy = reinterpret_cast<T*>(1);
auto bundle = new _::ArrayDisposableOwnedBundle<Array<T>, Attachments...>(
kj::mv(*this), kj::fwd<Attachments>(attachments)...);
return Array<T>(ptrCopy, size_, *bundle);
}
template <typename T>
template <typename... Attachments>
Array<T> ArrayPtr<T>::attach(Attachments&&... attachments) const {
T* ptrCopy = ptr;
KJ_IREQUIRE(ptr != nullptr, "cannot attach to null pointer");
// HACK: If someone accidentally calls .attach() on a null pointer in opt mode, try our best to
// accomplish reasonable behavior: We turn the pointer non-null but still invalid, so that the
// disposer will still be called when the pointer goes out of scope.
if (ptrCopy == nullptr) ptrCopy = reinterpret_cast<T*>(1);
auto bundle = new _::ArrayDisposableOwnedBundle<Attachments...>(
kj::fwd<Attachments>(attachments)...);
return Array<T>(ptr, size_, *bundle);
}
} // namespace kj } // namespace kj
...@@ -1247,6 +1247,9 @@ private: ...@@ -1247,6 +1247,9 @@ private:
// //
// So common that we put it in common.h rather than array.h. // So common that we put it in common.h rather than array.h.
template <typename T>
class Array;
template <typename T> template <typename T>
class ArrayPtr: public DisallowConstCopyIfNotConst<T> { class ArrayPtr: public DisallowConstCopyIfNotConst<T> {
// A pointer to an array. Includes a size. Like any pointer, it doesn't own the target data, // A pointer to an array. Includes a size. Like any pointer, it doesn't own the target data,
...@@ -1356,6 +1359,13 @@ public: ...@@ -1356,6 +1359,13 @@ public:
template <typename U> template <typename U>
inline bool operator!=(const ArrayPtr<U>& other) const { return !(*this == other); } inline bool operator!=(const ArrayPtr<U>& other) const { return !(*this == other); }
template <typename... Attachments>
Array<T> attach(Attachments&&... attachments) const KJ_WARN_UNUSED_RESULT;
// Like Array<T>::attach(), but also promotes an ArrayPtr to an Array. Generally the attachment
// should be an object that actually owns the array that the ArrayPtr is pointing at.
//
// You must include kj/array.h to call this.
private: private:
T* ptr; T* ptr;
size_t size_; size_t size_;
......
...@@ -197,7 +197,7 @@ public: ...@@ -197,7 +197,7 @@ public:
} }
template <typename... Attachments> template <typename... Attachments>
Own<T> attach(Attachments&&... attachments); Own<T> attach(Attachments&&... attachments) KJ_WARN_UNUSED_RESULT;
// Returns an Own<T> which points to the same object but which also ensures that all values // Returns an Own<T> which points to the same object but which also ensures that all values
// passed to `attachments` remain alive until after this object is destroyed. Normally // passed to `attachments` remain alive until after this object is destroyed. Normally
// `attachments` are other Own<?>s pointing to objects that this one depends on. // `attachments` are other Own<?>s pointing to objects that this one depends on.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment