1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "space_test.h"
18 #include "large_object_space.h"
19 
20 namespace art {
21 namespace gc {
22 namespace space {
23 
24 class LargeObjectSpaceTest : public SpaceTest {
25  public:
26   void LargeObjectTest();
27 
28   static constexpr size_t kNumThreads = 10;
29   static constexpr size_t kNumIterations = 1000;
30   void RaceTest();
31 };
32 
33 
LargeObjectTest()34 void LargeObjectSpaceTest::LargeObjectTest() {
35   size_t rand_seed = 0;
36   for (size_t i = 0; i < 2; ++i) {
37     LargeObjectSpace* los = nullptr;
38     if (i == 0) {
39       los = space::LargeObjectMapSpace::Create("large object space");
40     } else {
41       los = space::FreeListSpace::Create("large object space", nullptr, 128 * MB);
42     }
43 
44     static const size_t num_allocations = 64;
45     static const size_t max_allocation_size = 0x100000;
46     std::vector<std::pair<mirror::Object*, size_t>> requests;
47 
48     for (size_t phase = 0; phase < 2; ++phase) {
49       while (requests.size() < num_allocations) {
50         size_t request_size = test_rand(&rand_seed) % max_allocation_size;
51         size_t allocation_size = 0;
52         mirror::Object* obj = los->Alloc(Thread::Current(), request_size, &allocation_size,
53                                          nullptr);
54         ASSERT_TRUE(obj != nullptr);
55         ASSERT_EQ(allocation_size, los->AllocationSize(obj, nullptr));
56         ASSERT_GE(allocation_size, request_size);
57         // Fill in our magic value.
58         byte magic = (request_size & 0xFF) | 1;
59         memset(obj, magic, request_size);
60         requests.push_back(std::make_pair(obj, request_size));
61       }
62 
63       // "Randomly" shuffle the requests.
64       for (size_t k = 0; k < 10; ++k) {
65         for (size_t j = 0; j < requests.size(); ++j) {
66           std::swap(requests[j], requests[test_rand(&rand_seed) % requests.size()]);
67         }
68       }
69 
70       // Free 1 / 2 the allocations the first phase, and all the second phase.
71       size_t limit = !phase ? requests.size() / 2 : 0;
72       while (requests.size() > limit) {
73         mirror::Object* obj = requests.back().first;
74         size_t request_size = requests.back().second;
75         requests.pop_back();
76         byte magic = (request_size & 0xFF) | 1;
77         for (size_t k = 0; k < request_size; ++k) {
78           ASSERT_EQ(reinterpret_cast<const byte*>(obj)[k], magic);
79         }
80         ASSERT_GE(los->Free(Thread::Current(), obj), request_size);
81       }
82     }
83     // Test that dump doesn't crash.
84     los->Dump(LOG(INFO));
85 
86     size_t bytes_allocated = 0;
87     // Checks that the coalescing works.
88     mirror::Object* obj = los->Alloc(Thread::Current(), 100 * MB, &bytes_allocated, nullptr);
89     EXPECT_TRUE(obj != nullptr);
90     los->Free(Thread::Current(), obj);
91 
92     EXPECT_EQ(0U, los->GetBytesAllocated());
93     EXPECT_EQ(0U, los->GetObjectsAllocated());
94     delete los;
95   }
96 }
97 
98 class AllocRaceTask : public Task {
99  public:
AllocRaceTask(size_t id,size_t iterations,size_t size,LargeObjectSpace * los)100   AllocRaceTask(size_t id, size_t iterations, size_t size, LargeObjectSpace* los) :
101     id_(id), iterations_(iterations), size_(size), los_(los) {}
102 
Run(Thread * self)103   void Run(Thread* self) {
104     for (size_t i = 0; i < iterations_ ; ++i) {
105       size_t alloc_size;
106       mirror::Object* ptr = los_->Alloc(self, size_, &alloc_size, nullptr);
107 
108       NanoSleep((id_ + 3) * 1000);  // (3+id) mu s
109 
110       los_->Free(self, ptr);
111     }
112   }
113 
Finalize()114   virtual void Finalize() {
115     delete this;
116   }
117 
118  private:
119   size_t id_;
120   size_t iterations_;
121   size_t size_;
122   LargeObjectSpace* los_;
123 };
124 
RaceTest()125 void LargeObjectSpaceTest::RaceTest() {
126   for (size_t los_type = 0; los_type < 2; ++los_type) {
127     LargeObjectSpace* los = nullptr;
128     if (los_type == 0) {
129       los = space::LargeObjectMapSpace::Create("large object space");
130     } else {
131       los = space::FreeListSpace::Create("large object space", nullptr, 128 * MB);
132     }
133 
134     Thread* self = Thread::Current();
135     ThreadPool thread_pool("Large object space test thread pool", kNumThreads);
136     for (size_t i = 0; i < kNumThreads; ++i) {
137       thread_pool.AddTask(self, new AllocRaceTask(i, kNumIterations, 16 * KB, los));
138     }
139 
140     thread_pool.StartWorkers(self);
141 
142     thread_pool.Wait(self, true, false);
143 
144     delete los;
145   }
146 }
147 
TEST_F(LargeObjectSpaceTest,LargeObjectTest)148 TEST_F(LargeObjectSpaceTest, LargeObjectTest) {
149   LargeObjectTest();
150 }
151 
TEST_F(LargeObjectSpaceTest,RaceTest)152 TEST_F(LargeObjectSpaceTest, RaceTest) {
153   RaceTest();
154 }
155 
156 }  // namespace space
157 }  // namespace gc
158 }  // namespace art
159