1 | /* |
2 | * Copyright 2013-present Facebook, Inc. |
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 <memory> |
18 | #include <mutex> |
19 | #include <thread> |
20 | |
21 | #include <folly/AtomicHashMap.h> |
22 | #include <folly/Memory.h> |
23 | #include <folly/ScopeGuard.h> |
24 | #include <folly/portability/GTest.h> |
25 | |
26 | namespace { |
27 | |
28 | struct MyObject { |
29 | explicit MyObject(int i_) : i(i_) {} |
30 | int i; |
31 | }; |
32 | |
33 | typedef folly::AtomicHashMap<int, std::shared_ptr<MyObject>> MyMap; |
34 | typedef std::lock_guard<std::mutex> Guard; |
35 | |
36 | std::unique_ptr<MyMap> newMap() { |
37 | return std::make_unique<MyMap>(100); |
38 | } |
39 | |
40 | struct MyObjectDirectory { |
41 | MyObjectDirectory() : cur_(newMap()), prev_(newMap()) {} |
42 | |
43 | std::shared_ptr<MyObject> get(int key) { |
44 | auto val = tryGet(key); |
45 | if (val) { |
46 | return val; |
47 | } |
48 | |
49 | std::shared_ptr<MyMap> cur; |
50 | { |
51 | Guard g(lock_); |
52 | cur = cur_; |
53 | } |
54 | |
55 | auto ret = cur->insert(key, std::make_shared<MyObject>(key)); |
56 | return ret.first->second; |
57 | } |
58 | |
59 | std::shared_ptr<MyObject> tryGet(int key) { |
60 | std::shared_ptr<MyMap> cur; |
61 | std::shared_ptr<MyMap> prev; |
62 | { |
63 | Guard g(lock_); |
64 | cur = cur_; |
65 | prev = prev_; |
66 | } |
67 | |
68 | auto it = cur->find(key); |
69 | if (it != cur->end()) { |
70 | return it->second; |
71 | } |
72 | |
73 | it = prev->find(key); |
74 | if (it != prev->end()) { |
75 | auto ret = cur->insert(key, it->second); |
76 | return ret.first->second; |
77 | } |
78 | |
79 | return nullptr; |
80 | } |
81 | |
82 | void archive() { |
83 | std::shared_ptr<MyMap> cur(newMap()); |
84 | |
85 | Guard g(lock_); |
86 | prev_ = cur_; |
87 | cur_ = cur; |
88 | } |
89 | |
90 | std::mutex lock_; |
91 | std::shared_ptr<MyMap> cur_; |
92 | std::shared_ptr<MyMap> prev_; |
93 | }; |
94 | |
95 | } // namespace |
96 | |
97 | ////////////////////////////////////////////////////////////////////// |
98 | |
99 | /* |
100 | * This test case stresses ThreadLocal allocation/deallocation heavily |
101 | * via ThreadCachedInt and AtomicHashMap, and a bunch of other |
102 | * mallocing. |
103 | */ |
104 | TEST(AHMIntStressTest, Test) { |
105 | auto const objs = new MyObjectDirectory(); |
106 | SCOPE_EXIT { |
107 | delete objs; |
108 | }; |
109 | |
110 | std::vector<std::thread> threads; |
111 | for (int threadId = 0; threadId < 64; ++threadId) { |
112 | threads.emplace_back([objs] { |
113 | for (int recycles = 0; recycles < 500; ++recycles) { |
114 | for (int i = 0; i < 10; i++) { |
115 | auto val = objs->get(i); |
116 | } |
117 | |
118 | objs->archive(); |
119 | } |
120 | }); |
121 | } |
122 | |
123 | for (auto& t : threads) { |
124 | t.join(); |
125 | } |
126 | } |
127 | |