aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar reed <reed@google.com>2016-03-01 07:28:51 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2016-03-01 07:28:51 -0800
commitbb7b043b2db64197d2f6a1edaf3562a50c77afb1 (patch)
tree18d20cebb755cb2069732472529ac630d3641634
parent856cc17db434be717a29e1fa893a2108d1f9db36 (diff)
isolate sk_sp from larger cl
-rw-r--r--include/core/SkRefCnt.h99
-rw-r--r--tests/RefCntTest.cpp90
2 files changed, 189 insertions, 0 deletions
diff --git a/include/core/SkRefCnt.h b/include/core/SkRefCnt.h
index 9d1e5f1f02..43251d0788 100644
--- a/include/core/SkRefCnt.h
+++ b/include/core/SkRefCnt.h
@@ -227,4 +227,103 @@ private:
mutable int32_t fRefCnt;
};
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Shared pointer class to wrap classes that support a ref()/unref() interface.
+ *
+ * This can be used for classes inheriting from SkRefCnt, but it also works for other
+ * classes that match the interface, but have different internal choices: e.g. the hosted class
+ * may have its ref/unref be thread-safe, but that is not assumed/imposed by sk_sp.
+ */
+template <typename T> class sk_sp {
+public:
+ sk_sp() : fPtr(nullptr) {}
+ sk_sp(std::nullptr_t) : fPtr(nullptr) {}
+
+ /**
+ * Shares the underlying object by calling ref(), so that both the argument and the newly
+ * created sk_sp both have a reference to it.
+ */
+ sk_sp(const sk_sp<T>& that) : fPtr(SkSafeRef(that.get())) {}
+
+ /**
+ * Move the underlying object from the argument to the newly created sk_sp. Afterwards only
+ * the new sk_sp will have a reference to the object, and the argument will point to null.
+ * No call to ref() or unref() will be made.
+ */
+ sk_sp(sk_sp<T>&& that) : fPtr(that.release()) {}
+
+ /**
+ * Adopt the bare pointer into the newly created sk_sp.
+ * No call to ref() or unref() will be made.
+ */
+ explicit sk_sp(T* obj) : fPtr(obj) {}
+
+ /**
+ * Calls unref() on the underlying object pointer.
+ */
+ ~sk_sp() {
+ SkSafeUnref(fPtr);
+ }
+
+ sk_sp<T>& operator=(std::nullptr_t) { this->reset(); }
+
+ /**
+ * Shares the underlying object referenced by the argument by calling ref() on it. If this
+ * sk_sp previously had a reference to an object (i.e. not null) it will call unref() on that
+ * object.
+ */
+ sk_sp<T>& operator=(const sk_sp<T>& that) {
+ this->reset(SkSafeRef(that.get()));
+ return *this;
+ }
+
+ /**
+ * Move the underlying object from the argument to the sk_sp. If the sk_sp previously held
+ * a reference to another object, unref() will be called on that object. No call to ref()
+ * will be made.
+ */
+ sk_sp<T>& operator=(sk_sp<T>&& that) {
+ this->reset(that.release());
+ return *this;
+ }
+
+ bool operator==(std::nullptr_t) const { return this->get() == nullptr; }
+ bool operator!=(std::nullptr_t) const { return this->get() != nullptr; }
+
+ bool operator==(const sk_sp<T>& that) const { return this->get() == that.get(); }
+ bool operator!=(const sk_sp<T>& that) const { return this->get() != that.get(); }
+
+ explicit operator bool() const { return this->get() != nullptr; }
+
+ T* get() const { return fPtr; }
+ T* operator->() const { return fPtr; }
+
+ /**
+ * Adopt the new bare pointer, and call unref() on any previously held object (if not null).
+ * No call to ref() will be made.
+ */
+ void reset(T* ptr = nullptr) {
+ if (fPtr != ptr) {
+ SkSafeUnref(fPtr);
+ fPtr = ptr;
+ }
+ }
+
+ /**
+ * Return the bare pointer, and set the internal object pointer to nullptr.
+ * The caller must assume ownership of the object, and manage its reference count directly.
+ * No call to unref() will be made.
+ */
+ T* SK_WARN_UNUSED_RESULT release() {
+ T* ptr = fPtr;
+ fPtr = nullptr;
+ return ptr;
+ }
+
+private:
+ T* fPtr;
+};
+
#endif
diff --git a/tests/RefCntTest.cpp b/tests/RefCntTest.cpp
index 6a1e0dc2fe..e3f41bdd5d 100644
--- a/tests/RefCntTest.cpp
+++ b/tests/RefCntTest.cpp
@@ -79,3 +79,93 @@ DEF_TEST(RefCnt, reporter) {
test_refCnt(reporter);
test_weakRefCnt(reporter);
}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static int gRefCounter;
+static int gUnrefCounter;
+static int gNewCounter;
+static int gDeleteCounter;
+
+#define check(reporter, ref, unref, make, kill) \
+ REPORTER_ASSERT(reporter, gRefCounter == ref); \
+ REPORTER_ASSERT(reporter, gUnrefCounter == unref); \
+ REPORTER_ASSERT(reporter, gNewCounter == make); \
+ REPORTER_ASSERT(reporter, gDeleteCounter == kill);
+
+
+class Effect {
+public:
+ Effect() : fRefCnt(1) {
+ gNewCounter += 1;
+ }
+
+ int fRefCnt;
+
+ void ref() {
+ gRefCounter += 1;
+ fRefCnt += 1;
+ }
+ void unref() {
+ gUnrefCounter += 1;
+
+ SkASSERT(fRefCnt > 0);
+ if (0 == --fRefCnt) {
+ gDeleteCounter += 1;
+ delete this;
+ }
+ }
+
+ int* method() const { return new int; }
+};
+
+static sk_sp<Effect> Create() {
+ return sk_sp<Effect>(new Effect);
+}
+
+class Paint {
+public:
+ sk_sp<Effect> fEffect;
+
+ const sk_sp<Effect>& get() const { return fEffect; }
+
+ void set(sk_sp<Effect> value) {
+ fEffect = std::move(value);
+ }
+};
+
+DEF_TEST(sk_sp, reporter) {
+ gRefCounter = 0;
+ gUnrefCounter = 0;
+ gNewCounter = 0;
+ gDeleteCounter = 0;
+
+ Paint paint;
+ REPORTER_ASSERT(reporter, paint.fEffect.get() == nullptr);
+ REPORTER_ASSERT(reporter, !paint.get());
+ check(reporter, 0, 0, 0, 0);
+
+ paint.set(Create());
+ check(reporter, 0, 0, 1, 0);
+ REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 1);
+
+ paint.set(nullptr);
+ check(reporter, 0, 1, 1, 1);
+
+ auto e = Create();
+ REPORTER_ASSERT(reporter, sizeof(e) == sizeof(void*));
+
+ check(reporter, 0, 1, 2, 1);
+ paint.set(e);
+ check(reporter, 1, 1, 2, 1);
+ REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 2);
+
+ Paint paint2;
+ paint2.set(paint.get());
+ check(reporter, 2, 1, 2, 1);
+ REPORTER_ASSERT(reporter, paint.fEffect.get()->fRefCnt == 3);
+
+ delete paint.get()->method();
+ check(reporter, 2, 1, 2, 1);
+}
+