blob: 4ba94f403312fe607fd1a416555d4c1dc5c54119 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <atomic>
#include <fbl/auto_lock.h>
#include <fbl/ref_counted_upgradeable.h>
#include <fbl/ref_ptr.h>
#include <lib/zx/event.h>
#include <pthread.h>
#include <unittest/unittest.h>
namespace {
template <bool EnableAdoptionValidator>
class RawUpgradeTester :
public fbl::RefCountedUpgradeable<RawUpgradeTester<EnableAdoptionValidator>,
EnableAdoptionValidator> {
public:
RawUpgradeTester(fbl::Mutex* mutex, std::atomic<bool>* destroying, zx::event* event)
: mutex_(mutex), destroying_(destroying), destroying_event_(event) {}
~RawUpgradeTester() {
atomic_store(destroying_, true);
if (destroying_event_)
destroying_event_->signal(0u, ZX_EVENT_SIGNALED);
fbl::AutoLock al(mutex_);
}
private:
fbl::Mutex* mutex_;
std::atomic<bool>* destroying_;
zx::event* destroying_event_;
};
template <bool EnableAdoptionValidator>
void* adopt_and_reset(void* arg) {
fbl::RefPtr<RawUpgradeTester<EnableAdoptionValidator>> rc_client =
fbl::AdoptRef(reinterpret_cast<RawUpgradeTester<EnableAdoptionValidator>*>(arg));
// The reset() which will call the dtor, which we expect to
// block because upgrade_fail_test() is holding the mutex.
rc_client.reset();
return NULL;
}
} // namespace
template <bool EnableAdoptionValidator>
static bool upgrade_fail_test() {
BEGIN_TEST;
fbl::Mutex mutex;
fbl::AllocChecker ac;
std::atomic<bool> destroying{false};
zx::event destroying_event;
zx_status_t status = zx::event::create(0u, &destroying_event);
ASSERT_EQ(status, ZX_OK);
auto raw = new (&ac) RawUpgradeTester<EnableAdoptionValidator>(&mutex,
&destroying,
&destroying_event);
EXPECT_TRUE(ac.check());
pthread_t thread;
{
fbl::AutoLock al(&mutex);
int res = pthread_create(&thread, NULL, &adopt_and_reset<EnableAdoptionValidator>, raw);
ASSERT_LE(0, res);
// Wait until the thread is in the destructor.
status = destroying_event.wait_one(ZX_EVENT_SIGNALED, zx::time::infinite(), nullptr);
EXPECT_EQ(status, ZX_OK);
EXPECT_TRUE(atomic_load(&destroying));
// The RawUpgradeTester must be blocked in the destructor, the upgrade will fail.
auto upgrade1 = fbl::MakeRefPtrUpgradeFromRaw(raw, mutex);
EXPECT_FALSE(upgrade1);
// Verify that the previous upgrade attempt did not change the refcount.
auto upgrade2 = fbl::MakeRefPtrUpgradeFromRaw(raw, mutex);
EXPECT_FALSE(upgrade2);
}
pthread_join(thread, NULL);
END_TEST;
}
template <bool EnableAdoptionValidator>
static bool upgrade_success_test() {
BEGIN_TEST;
fbl::Mutex mutex;
fbl::AllocChecker ac;
std::atomic<bool> destroying{false};
auto ref = fbl::AdoptRef(new (&ac) RawUpgradeTester<EnableAdoptionValidator>(&mutex,
&destroying,
nullptr));
EXPECT_TRUE(ac.check());
auto raw = ref.get();
{
fbl::AutoLock al(&mutex);
// RawUpgradeTester is not in the destructor so the upgrade should
// succeed.
auto upgrade = fbl::MakeRefPtrUpgradeFromRaw(raw, mutex);
EXPECT_TRUE(upgrade);
}
ref.reset();
EXPECT_TRUE(atomic_load(&destroying));
END_TEST;
}
BEGIN_TEST_CASE(ref_counted_upgradeable_tests)
RUN_NAMED_TEST("Fail to upgrade raw pointer (adoption validation on)", upgrade_fail_test<true>)
RUN_NAMED_TEST("Fail to upgrade raw pointer (adoption validation off)", upgrade_fail_test<false>)
RUN_NAMED_TEST("Upgrade raw pointer (adoption validation on)", upgrade_success_test<true>)
RUN_NAMED_TEST("Upgrade raw pointer (adoption validation off)", upgrade_success_test<false>)
END_TEST_CASE(ref_counted_upgradeable_tests);