aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-05-14 14:09:24 +0000
committerGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-05-14 14:09:24 +0000
commit554875210043b34178f7ed6ac5bd682b1fad367b (patch)
treefdadeb167ef502f98784117a3c05378c72fceeec
parentf105b109264f71dfb0bfd9977e6a5dd0a5a12f57 (diff)
Add bench and test for SkRefCnt.
http://codereview.appspot.com/6195071/ This also adds a cross platform SkThread for testing purposes. git-svn-id: http://skia.googlecode.com/svn/trunk@3921 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--bench/RefCntBench.cpp77
-rw-r--r--gyp/bench.gypi1
-rw-r--r--gyp/tests.gyp1
-rw-r--r--gyp/utils.gyp23
-rw-r--r--src/ports/SkThread_none.cpp4
-rw-r--r--src/utils/SkThreadUtils.h46
-rw-r--r--src/utils/SkThreadUtils_pthread.cpp105
-rw-r--r--src/utils/SkThreadUtils_pthread.h29
-rw-r--r--src/utils/SkThreadUtils_pthread_linux.cpp46
-rw-r--r--src/utils/SkThreadUtils_pthread_mach.cpp30
-rw-r--r--src/utils/SkThreadUtils_pthread_other.cpp12
-rw-r--r--src/utils/SkThreadUtils_win.cpp136
-rw-r--r--src/utils/SkThreadUtils_win.h28
-rw-r--r--tests/RefCntTest.cpp44
14 files changed, 581 insertions, 1 deletions
diff --git a/bench/RefCntBench.cpp b/bench/RefCntBench.cpp
new file mode 100644
index 0000000000..44fb648f4e
--- /dev/null
+++ b/bench/RefCntBench.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkBenchmark.h"
+#include "SkThread.h"
+#include <memory>
+
+enum {
+ N = SkBENCHLOOP(1000000),
+ M = SkBENCHLOOP(2)
+};
+
+class RefCntBench_Stack : public SkBenchmark {
+public:
+ RefCntBench_Stack(void* param) : INHERITED(param) {
+ }
+protected:
+ virtual const char* onGetName() {
+ return "ref_cnt_stack";
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ for (int i = 0; i < N; ++i) {
+ SkRefCnt ref;
+ for (int j = 0; j < M; ++j) {
+ ref.ref();
+ ref.unref();
+ }
+ }
+ }
+
+private:
+ typedef SkBenchmark INHERITED;
+};
+
+class PlacedRefCnt : public SkRefCnt {
+public:
+ PlacedRefCnt() : SkRefCnt() { }
+ void operator delete(void *p) { }
+};
+
+class RefCntBench_Heap : public SkBenchmark {
+public:
+ RefCntBench_Heap(void* param) : INHERITED(param) {
+ }
+protected:
+ virtual const char* onGetName() {
+ return "ref_cnt_heap";
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ char memory[sizeof(PlacedRefCnt)];
+ for (int i = 0; i < N; ++i) {
+ PlacedRefCnt* ref = new (memory) PlacedRefCnt();
+ for (int j = 0; j < M; ++j) {
+ ref->ref();
+ ref->unref();
+ }
+ ref->unref();
+ }
+ }
+
+private:
+ typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact0(void* p) { return new RefCntBench_Stack(p); }
+static SkBenchmark* Fact1(void* p) { return new RefCntBench_Heap(p); }
+
+static BenchRegistry gReg01(Fact0);
+static BenchRegistry gReg02(Fact1);
+
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
index 8499ca137f..221faed9d8 100644
--- a/gyp/bench.gypi
+++ b/gyp/bench.gypi
@@ -33,6 +33,7 @@
'../bench/PathIterBench.cpp',
'../bench/PicturePlaybackBench.cpp',
'../bench/RectBench.cpp',
+ '../bench/RefCntBench.cpp',
'../bench/RegionBench.cpp',
'../bench/RepeatTileBench.cpp',
'../bench/ScalarBench.cpp',
diff --git a/gyp/tests.gyp b/gyp/tests.gyp
index e7d5724cda..ec850a4923 100644
--- a/gyp/tests.gyp
+++ b/gyp/tests.gyp
@@ -61,6 +61,7 @@
'../tests/Reader32Test.cpp',
'../tests/ReadPixelsTest.cpp',
'../tests/ReadWriteAlphaTest.cpp',
+ '../tests/RefCntTest.cpp',
'../tests/RefDictTest.cpp',
'../tests/RegionTest.cpp',
'../tests/ScalarTest.cpp',
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 2fca63fbfe..d2c413ecbc 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -56,6 +56,14 @@
'../src/utils/SkParsePath.cpp',
'../src/utils/SkProxyCanvas.cpp',
'../src/utils/SkSfntUtils.cpp',
+ '../src/utils/SkThreadUtils.h',
+ '../src/utils/SkThreadUtils_pthread.cpp',
+ '../src/utils/SkThreadUtils_pthread.h',
+ '../src/utils/SkThreadUtils_pthread_linux.cpp',
+ '../src/utils/SkThreadUtils_pthread_mach.cpp',
+ '../src/utils/SkThreadUtils_pthread_other.cpp',
+ '../src/utils/SkThreadUtils_win.cpp',
+ '../src/utils/SkThreadUtils_win.h',
'../src/utils/SkUnitMappers.cpp',
#mac
@@ -87,6 +95,9 @@
'../include/utils/mac',
],
},
+ 'sources!': [
+ '../src/utils/SkThreadUtils_pthread_other.cpp',
+ ],
},{ #else if 'skia_os != "mac"'
'include_dirs!': [
'../include/utils/mac',
@@ -94,6 +105,7 @@
'sources!': [
'../include/utils/mac/SkCGUtils.h',
'../src/utils/mac/SkCreateCGImageRef.cpp',
+ '../src/utils/SkThreadUtils_pthread_mach.cpp',
],
}],
[ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
@@ -103,10 +115,16 @@
'-lGLU',
],
},
+ 'sources!': [
+ '../src/utils/SkThreadUtils_pthread_other.cpp',
+ ],
},{ #else if 'skia_os not in ["linux", "freebsd", "openbsd", "solaris"]'
'include_dirs!': [
'../include/utils/unix',
],
+ 'sources!': [
+ '../src/utils/SkThreadUtils_pthread_linux.cpp',
+ ],
}],
[ 'skia_os == "win"', {
'direct_dependent_settings': {
@@ -114,6 +132,11 @@
'../include/utils/win',
],
},
+ 'sources!': [
+ '../src/utils/SkThreadUtils_pthread.cpp',
+ '../src/utils/SkThreadUtils_pthread.h',
+ '../src/utils/SkThreadUtils_pthread_other.cpp',
+ ],
},{ #else if 'skia_os != "win"'
'include_dirs!': [
'../include/utils/win',
diff --git a/src/ports/SkThread_none.cpp b/src/ports/SkThread_none.cpp
index 9e170cfb3d..a948a5410c 100644
--- a/src/ports/SkThread_none.cpp
+++ b/src/ports/SkThread_none.cpp
@@ -8,6 +8,7 @@
#include "SkThread.h"
+#include "SkTLS.h"
int32_t sk_atomic_inc(int32_t* addr) {
int32_t value = *addr;
@@ -25,9 +26,10 @@ SkMutex::SkMutex() {}
SkMutex::~SkMutex() {}
+#ifndef SK_USE_POSIX_THREADS
void SkMutex::acquire() {}
-
void SkMutex::release() {}
+#endif
//////////////////////////////////////////////////////////////////////////
diff --git a/src/utils/SkThreadUtils.h b/src/utils/SkThreadUtils.h
new file mode 100644
index 0000000000..b0c5044618
--- /dev/null
+++ b/src/utils/SkThreadUtils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkThreadUtils_DEFINED
+#define SkThreadUtils_DEFINED
+
+#include "SkTypes.h"
+
+class SkThread : SkNoncopyable {
+public:
+ typedef void (*entryPointProc)(void*);
+
+ SkThread(entryPointProc entryPoint, void* data = NULL);
+
+ /**
+ * Non-virtual, do not subclass.
+ */
+ ~SkThread();
+
+ /**
+ * Starts the thread. Returns false if the thread could not be started.
+ */
+ bool start();
+
+ /**
+ * Waits for the thread to finish.
+ * If the thread has not started, returns immediately.
+ */
+ void join();
+
+ /**
+ * SkThreads with an affinity for the same processor will attempt to run cache
+ * locally with each other. SkThreads with an affinity for different processors
+ * will attempt to run on different cores. Returns false if the request failed.
+ */
+ bool setProcessorAffinity(unsigned int processor);
+
+private:
+ void* fData;
+};
+
+#endif
diff --git a/src/utils/SkThreadUtils_pthread.cpp b/src/utils/SkThreadUtils_pthread.cpp
new file mode 100644
index 0000000000..17a2075ab0
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#include "SkThreadUtils.h"
+#include "SkThreadUtils_pthread.h"
+
+#include <pthread.h>
+#include <signal.h>
+
+SkThread_PThreadData::SkThread_PThreadData(SkThread::entryPointProc entryPoint, void* data)
+ : fPThread()
+ , fValidPThread(false)
+ , fParam(data)
+ , fEntryPoint(entryPoint)
+ , fStarted(false)
+{
+ pthread_mutex_init(&fStartMutex, NULL);
+
+ pthread_cond_init(&fStartCondition, NULL);
+
+ pthread_attr_init(&fAttr);
+ pthread_attr_setdetachstate(&fAttr, PTHREAD_CREATE_JOINABLE);
+}
+SkThread_PThreadData::~SkThread_PThreadData() {
+ pthread_attr_destroy(&fAttr);
+ pthread_cond_destroy(&fStartCondition);
+ pthread_mutex_destroy(&fStartMutex);
+}
+
+static void* thread_start(void* arg) {
+ SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(arg);
+ //Wait for start signal
+ pthread_mutex_lock(&(pthreadData->fStartMutex));
+ while (!pthreadData->fStarted) {
+ pthread_cond_wait(&(pthreadData->fStartCondition), &(pthreadData->fStartMutex));
+ }
+ pthread_mutex_unlock(&(pthreadData->fStartMutex));
+
+ //See if this thread was canceled before starting.
+ pthread_testcancel();
+
+ pthreadData->fEntryPoint(pthreadData->fParam);
+ return NULL;
+}
+
+SkThread::SkThread(entryPointProc entryPoint, void* data) {
+ SkThread_PThreadData* pthreadData = new SkThread_PThreadData(entryPoint, data);
+ fData = pthreadData;
+
+ int ret = pthread_create(&(pthreadData->fPThread),
+ &(pthreadData->fAttr),
+ thread_start,
+ pthreadData);
+
+ pthreadData->fValidPThread = (0 == ret);
+}
+
+SkThread::~SkThread() {
+ if (fData != NULL) {
+ SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+ // If created thread but start was never called, kill the thread.
+ if (pthreadData->fValidPThread && !pthreadData->fStarted) {
+ if (pthread_cancel(pthreadData->fPThread) == 0) {
+ if (this->start()) {
+ this->join();
+ }
+ } else {
+ //kill with prejudice
+ pthread_kill(pthreadData->fPThread, SIGKILL);
+ }
+ }
+ delete pthreadData;
+ }
+}
+
+bool SkThread::start() {
+ SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+ if (!pthreadData->fValidPThread) {
+ return false;
+ }
+
+ if (pthreadData->fStarted) {
+ return false;
+ }
+ pthreadData->fStarted = true;
+ pthread_mutex_lock(&(pthreadData->fStartMutex));
+ pthread_cond_signal(&(pthreadData->fStartCondition));
+ pthread_mutex_unlock(&(pthreadData->fStartMutex));
+ return true;
+}
+
+void SkThread::join() {
+ SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+ if (!pthreadData->fValidPThread || !pthreadData->fStarted) {
+ return;
+ }
+
+ pthread_join(pthreadData->fPThread, NULL);
+}
diff --git a/src/utils/SkThreadUtils_pthread.h b/src/utils/SkThreadUtils_pthread.h
new file mode 100644
index 0000000000..52b398c2e2
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkThreadUtils_PThreadData_DEFINED
+#define SkThreadUtils_PThreadData_DEFINED
+
+#include "SkThreadUtils.h"
+#include <pthread.h>
+
+class SkThread_PThreadData {
+public:
+ SkThread_PThreadData(SkThread::entryPointProc entryPoint, void* data);
+ ~SkThread_PThreadData();
+ pthread_t fPThread;
+ bool fValidPThread;
+ pthread_mutex_t fStartMutex;
+ pthread_cond_t fStartCondition;
+ pthread_attr_t fAttr;
+
+ void* fParam;
+ SkThread::entryPointProc fEntryPoint;
+ bool fStarted;
+};
+
+#endif
diff --git a/src/utils/SkThreadUtils_pthread_linux.cpp b/src/utils/SkThreadUtils_pthread_linux.cpp
new file mode 100644
index 0000000000..4a03cb8276
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread_linux.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE //for pthread_setaffinity_np
+#endif
+
+#include "SkThreadUtils.h"
+#include "SkThreadUtils_pthread.h"
+
+#include <pthread.h>
+
+static int nth_set_cpu(unsigned int n, cpu_set_t* cpuSet) {
+ n %= CPU_COUNT(cpuSet);
+ for (unsigned int setCpusSeen = 0, currentCpu = 0; true; ++currentCpu) {
+ if (CPU_ISSET(currentCpu, cpuSet)) {
+ ++setCpusSeen;
+ if (setCpusSeen > n) {
+ return currentCpu;
+ }
+ }
+ }
+}
+
+bool SkThread::setProcessorAffinity(unsigned int processor) {
+ SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+ if (!pthreadData->fValidPThread) {
+ return false;
+ }
+
+ cpu_set_t parentCpuset;
+ if (0 != pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &parentCpuset)) {
+ return false;
+ }
+
+ cpu_set_t cpuset;
+ CPU_ZERO(&cpuset);
+ CPU_SET(nth_set_cpu(processor, &parentCpuset), &cpuset);
+ return 0 == pthread_setaffinity_np(pthreadData->fPThread,
+ sizeof(cpu_set_t),
+ &cpuset);
+}
diff --git a/src/utils/SkThreadUtils_pthread_mach.cpp b/src/utils/SkThreadUtils_pthread_mach.cpp
new file mode 100644
index 0000000000..0f6e263906
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread_mach.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkThreadUtils.h"
+#include "SkThreadUtils_pthread.h"
+
+#include <mach/mach.h>
+#include <mach/thread_policy.h>
+#include <pthread.h>
+
+bool SkThread::setProcessorAffinity(unsigned int processor) {
+ SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+ if (!pthreadData->fValidPThread) {
+ return false;
+ }
+
+ mach_port_t tid = pthread_mach_thread_np(pthreadData->fPThread);
+
+ thread_affinity_policy_data_t policy;
+ policy.affinity_tag = processor;
+
+ return 0 == thread_policy_set(tid,
+ THREAD_AFFINITY_POLICY,
+ (thread_policy_t) &policy,
+ THREAD_AFFINITY_POLICY_COUNT);
+}
diff --git a/src/utils/SkThreadUtils_pthread_other.cpp b/src/utils/SkThreadUtils_pthread_other.cpp
new file mode 100644
index 0000000000..a3973f1d72
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread_other.cpp
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkThreadUtils.h"
+
+bool SkThread::setProcessorAffinity(unsigned int processor) {
+ return false;
+}
diff --git a/src/utils/SkThreadUtils_win.cpp b/src/utils/SkThreadUtils_win.cpp
new file mode 100644
index 0000000000..208ffded1b
--- /dev/null
+++ b/src/utils/SkThreadUtils_win.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#include "SkThreadUtils.h"
+#include "SkThreadUtils_win.h"
+
+SkThread_WinData::SkThread_WinData(SkThread::entryPointProc entryPoint, void* data)
+ : fHandle(NULL)
+ , fParam(data)
+ , fThreadId(0)
+ , fEntryPoint(entryPoint)
+ , fStarted(false)
+{
+ fCancelEvent = CreateEvent(
+ NULL, // default security attributes
+ false, //auto reset
+ false, //not signaled
+ NULL); //no name
+}
+
+SkThread_WinData::~SkThread_WinData() {
+ CloseHandle(fCancelEvent);
+}
+
+static DWORD WINAPI thread_start(LPVOID data) {
+ SkThread_WinData* winData = static_cast<SkThread_WinData*>(data);
+
+ //See if this thread was canceled before starting.
+ if (WaitForSingleObject(winData->fCancelEvent, 0) == WAIT_OBJECT_0) {
+ return 0;
+ }
+
+ winData->fEntryPoint(winData->fParam);
+ return 0;
+}
+
+SkThread::SkThread(entryPointProc entryPoint, void* data) {
+ SkThread_WinData* winData = new SkThread_WinData(entryPoint, data);
+ fData = winData;
+
+ if (NULL == winData->fCancelEvent) {
+ return;
+ }
+
+ winData->fHandle = CreateThread(
+ NULL, // default security attributes
+ 0, // use default stack size
+ thread_start, // thread function name (proxy)
+ winData, // argument to thread function (proxy args)
+ CREATE_SUSPENDED, // create suspended so affinity can be set
+ &winData->fThreadId); // returns the thread identifier
+}
+
+SkThread::~SkThread() {
+ if (fData != NULL) {
+ SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData);
+ // If created thread but start was never called, kill the thread.
+ if (winData->fHandle != NULL && !winData->fStarted) {
+ if (SetEvent(winData->fCancelEvent) != 0) {
+ if (this->start()) {
+ this->join();
+ }
+ } else {
+ //kill with prejudice
+ TerminateThread(winData->fHandle, -1);
+ }
+ }
+ delete winData;
+ }
+}
+
+bool SkThread::start() {
+ SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData);
+ if (NULL == winData->fHandle) {
+ return false;
+ }
+
+ if (winData->fStarted) {
+ return false;
+ }
+ winData->fStarted = -1 != ResumeThread(winData->fHandle);
+ return winData->fStarted;
+}
+
+void SkThread::join() {
+ SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData);
+ if (NULL == winData->fHandle || !winData->fStarted) {
+ return;
+ }
+
+ WaitForSingleObject(winData->fHandle, INFINITE);
+}
+
+static unsigned int num_bits_set(DWORD_PTR mask) {
+ unsigned int count;
+ for (count = 0; mask; ++count) {
+ mask &= mask - 1;
+ }
+ return count;
+}
+
+static unsigned int nth_set_bit(unsigned int n, DWORD_PTR mask) {
+ n %= num_bits_set(mask);
+ for (unsigned int setBitsSeen = 0, currentBit = 0; true; ++currentBit) {
+ if (mask & (1 << currentBit)) {
+ ++setBitsSeen;
+ if (setBitsSeen > n) {
+ return currentBit;
+ }
+ }
+ }
+}
+
+bool SkThread::setProcessorAffinity(unsigned int processor) {
+ SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData);
+ if (NULL == winData->fHandle) {
+ return false;
+ }
+
+ DWORD_PTR processAffinityMask;
+ DWORD_PTR systemAffinityMask;
+ if (0 == GetProcessAffinityMask(GetCurrentProcess(),
+ &processAffinityMask,
+ &systemAffinityMask)) {
+ return false;
+ }
+
+ DWORD_PTR threadAffinityMask = 1 << nth_set_bit(processor, processAffinityMask);
+ return 0 != SetThreadAffinityMask(winData->fHandle, threadAffinityMask);
+}
diff --git a/src/utils/SkThreadUtils_win.h b/src/utils/SkThreadUtils_win.h
new file mode 100644
index 0000000000..5861e5d0db
--- /dev/null
+++ b/src/utils/SkThreadUtils_win.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkThreadUtils_WinData_DEFINED
+#define SkThreadUtils_WinData_DEFINED
+
+#include "SkTypes.h"
+
+#include "SkThreadUtils.h"
+
+class SkThread_WinData {
+public:
+ SkThread_WinData(SkThread::entryPointProc entryPoint, void* data);
+ ~SkThread_WinData();
+ HANDLE fHandle;
+ HANDLE fCancelEvent;
+
+ LPVOID fParam;
+ DWORD fThreadId;
+ SkThread::entryPointProc fEntryPoint;
+ bool fStarted;
+};
+
+#endif
diff --git a/tests/RefCntTest.cpp b/tests/RefCntTest.cpp
new file mode 100644
index 0000000000..e48fd8a292
--- /dev/null
+++ b/tests/RefCntTest.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#include "Test.h"
+
+#include "SkRefCnt.h"
+#include "SkThreadUtils.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void bounce_ref(void* data) {
+ SkRefCnt* ref = static_cast<SkRefCnt*>(data);
+ for (int i = 0; i < 100000; ++i) {
+ ref->ref();
+ ref->unref();
+ }
+}
+
+static void test_refCnt(skiatest::Reporter* reporter) {
+ SkRefCnt* ref = new SkRefCnt();
+
+ SkThread thing1(bounce_ref, ref);
+ SkThread thing2(bounce_ref, ref);
+
+ thing1.setProcessorAffinity(0);
+ thing2.setProcessorAffinity(23);
+
+ SkASSERT(thing1.start());
+ SkASSERT(thing2.start());
+
+ thing1.join();
+ thing2.join();
+
+ REPORTER_ASSERT(reporter, ref->getRefCnt() == 1);
+ ref->unref();
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("ref_cnt", RefCntTestClass, test_refCnt)