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