1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <atomic>
6 #include <fbl/auto_lock.h>
7 #include <fbl/ref_counted_upgradeable.h>
8 #include <fbl/ref_ptr.h>
9 #include <lib/zx/event.h>
10 #include <pthread.h>
11 #include <unittest/unittest.h>
12
13 namespace {
14 template <bool EnableAdoptionValidator>
15 class RawUpgradeTester :
16 public fbl::RefCountedUpgradeable<RawUpgradeTester<EnableAdoptionValidator>,
17 EnableAdoptionValidator> {
18 public:
RawUpgradeTester(fbl::Mutex * mutex,std::atomic<bool> * destroying,zx::event * event)19 RawUpgradeTester(fbl::Mutex* mutex, std::atomic<bool>* destroying, zx::event* event)
20 : mutex_(mutex), destroying_(destroying), destroying_event_(event) {}
21
~RawUpgradeTester()22 ~RawUpgradeTester() {
23 atomic_store(destroying_, true);
24 if (destroying_event_)
25 destroying_event_->signal(0u, ZX_EVENT_SIGNALED);
26 fbl::AutoLock al(mutex_);
27 }
28
29 private:
30 fbl::Mutex* mutex_;
31 std::atomic<bool>* destroying_;
32 zx::event* destroying_event_;
33 };
34
35 template <bool EnableAdoptionValidator>
adopt_and_reset(void * arg)36 void* adopt_and_reset(void* arg) {
37 fbl::RefPtr<RawUpgradeTester<EnableAdoptionValidator>> rc_client =
38 fbl::AdoptRef(reinterpret_cast<RawUpgradeTester<EnableAdoptionValidator>*>(arg));
39 // The reset() which will call the dtor, which we expect to
40 // block because upgrade_fail_test() is holding the mutex.
41 rc_client.reset();
42 return NULL;
43 }
44 } // namespace
45
46 template <bool EnableAdoptionValidator>
upgrade_fail_test()47 static bool upgrade_fail_test() {
48 BEGIN_TEST;
49
50 fbl::Mutex mutex;
51 fbl::AllocChecker ac;
52 std::atomic<bool> destroying{false};
53 zx::event destroying_event;
54
55 zx_status_t status = zx::event::create(0u, &destroying_event);
56 ASSERT_EQ(status, ZX_OK);
57
58 auto raw = new (&ac) RawUpgradeTester<EnableAdoptionValidator>(&mutex,
59 &destroying,
60 &destroying_event);
61 EXPECT_TRUE(ac.check());
62
63 pthread_t thread;
64 {
65 fbl::AutoLock al(&mutex);
66 int res = pthread_create(&thread, NULL, &adopt_and_reset<EnableAdoptionValidator>, raw);
67 ASSERT_LE(0, res);
68 // Wait until the thread is in the destructor.
69 status = destroying_event.wait_one(ZX_EVENT_SIGNALED, zx::time::infinite(), nullptr);
70 EXPECT_EQ(status, ZX_OK);
71 EXPECT_TRUE(atomic_load(&destroying));
72 // The RawUpgradeTester must be blocked in the destructor, the upgrade will fail.
73 auto upgrade1 = fbl::MakeRefPtrUpgradeFromRaw(raw, mutex);
74 EXPECT_FALSE(upgrade1);
75 // Verify that the previous upgrade attempt did not change the refcount.
76 auto upgrade2 = fbl::MakeRefPtrUpgradeFromRaw(raw, mutex);
77 EXPECT_FALSE(upgrade2);
78 }
79
80 pthread_join(thread, NULL);
81 END_TEST;
82 }
83
84 template <bool EnableAdoptionValidator>
upgrade_success_test()85 static bool upgrade_success_test() {
86 BEGIN_TEST;
87
88 fbl::Mutex mutex;
89 fbl::AllocChecker ac;
90 std::atomic<bool> destroying{false};
91
92 auto ref = fbl::AdoptRef(new (&ac) RawUpgradeTester<EnableAdoptionValidator>(&mutex,
93 &destroying,
94 nullptr));
95 EXPECT_TRUE(ac.check());
96 auto raw = ref.get();
97
98 {
99 fbl::AutoLock al(&mutex);
100 // RawUpgradeTester is not in the destructor so the upgrade should
101 // succeed.
102 auto upgrade = fbl::MakeRefPtrUpgradeFromRaw(raw, mutex);
103 EXPECT_TRUE(upgrade);
104 }
105
106 ref.reset();
107 EXPECT_TRUE(atomic_load(&destroying));
108
109 END_TEST;
110 }
111
112 BEGIN_TEST_CASE(ref_counted_upgradeable_tests)
113 RUN_NAMED_TEST("Fail to upgrade raw pointer (adoption validation on)", upgrade_fail_test<true>)
114 RUN_NAMED_TEST("Fail to upgrade raw pointer (adoption validation off)", upgrade_fail_test<false>)
115 RUN_NAMED_TEST("Upgrade raw pointer (adoption validation on)", upgrade_success_test<true>)
116 RUN_NAMED_TEST("Upgrade raw pointer (adoption validation off)", upgrade_success_test<false>)
117 END_TEST_CASE(ref_counted_upgradeable_tests);
118