/Users/deen/code/yugabyte-db/src/yb/util/shared_mem-test.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include <signal.h> |
15 | | |
16 | | #include <thread> |
17 | | |
18 | | #include <gtest/gtest.h> |
19 | | |
20 | | #include "yb/util/shared_mem.h" |
21 | | #include "yb/util/test_util.h" |
22 | | |
23 | | using namespace std::literals; |
24 | | |
25 | | namespace yb { |
26 | | namespace util { |
27 | | |
28 | | struct SimpleObject { |
29 | | int value = 1; |
30 | | std::atomic<int> atomic_value; |
31 | | |
32 | 10 | SimpleObject(): atomic_value(2) { |
33 | 10 | } |
34 | | }; |
35 | | |
36 | | class SharedMemoryTest : public YBTest {}; |
37 | | |
38 | 1 | TEST_F(SharedMemoryTest, TestCreate) { |
39 | 1 | auto segment = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
40 | | |
41 | 1 | ASSERT_GE(segment.GetFd(), 0); |
42 | 1 | ASSERT_TRUE(segment.GetAddress()); |
43 | | |
44 | | // Can allocate in shared segment. |
45 | 1 | auto* obj = new(segment.GetAddress()) SimpleObject(); |
46 | 1 | ASSERT_EQ(1, obj->value); |
47 | 1 | ASSERT_EQ(2, obj->atomic_value.load(std::memory_order_acquire)); |
48 | | |
49 | | // Can update object in shared segment. |
50 | 1 | ++(obj->value); |
51 | 1 | obj->atomic_value.store(6, std::memory_order_release); |
52 | 1 | ASSERT_EQ(2, obj->value); |
53 | 1 | ASSERT_EQ(6, obj->atomic_value.load(std::memory_order_acquire)); |
54 | 1 | } |
55 | | |
56 | 1 | TEST_F(SharedMemoryTest, TestReadWriteSharedAccess) { |
57 | 1 | auto owner_handle = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
58 | 1 | auto rw_handle = ASSERT_RESULT(SharedMemorySegment::Open( |
59 | 1 | owner_handle.GetFd(), |
60 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
61 | 1 | sizeof(SimpleObject))); |
62 | | |
63 | 1 | ASSERT_EQ(rw_handle.GetFd(), owner_handle.GetFd()); |
64 | 1 | ASSERT_TRUE(rw_handle.GetAddress()); |
65 | | |
66 | 1 | auto* owner_object = new(owner_handle.GetAddress()) SimpleObject(); |
67 | 1 | auto* rw_object = static_cast<SimpleObject*>(rw_handle.GetAddress()); |
68 | | |
69 | | // Read-write handle can see default values. |
70 | 1 | ASSERT_EQ(1, rw_object->value); |
71 | 1 | ASSERT_EQ(2, rw_object->atomic_value.load(std::memory_order_acquire)); |
72 | | |
73 | 1 | owner_object->value = 3; |
74 | 1 | owner_object->atomic_value.store(8, std::memory_order_release); |
75 | | |
76 | | // Read-write handle can see updated values. |
77 | 1 | ASSERT_EQ(3, rw_object->value); |
78 | 1 | ASSERT_EQ(8, rw_object->atomic_value.load(std::memory_order_acquire)); |
79 | | |
80 | | // Read-write handle can update values. |
81 | 1 | rw_object->value = 13; |
82 | 1 | rw_object->atomic_value.store(1, std::memory_order_release); |
83 | 1 | ASSERT_EQ(13, owner_object->value); |
84 | 1 | ASSERT_EQ(1, owner_object->atomic_value.load(std::memory_order_acquire)); |
85 | 1 | } |
86 | | |
87 | 1 | TEST_F(SharedMemoryTest, TestReadOnlySharedAccess) { |
88 | 1 | auto owner_handle = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
89 | 1 | auto ro_handle1 = ASSERT_RESULT(SharedMemorySegment::Open( |
90 | 1 | owner_handle.GetFd(), |
91 | 1 | SharedMemorySegment::AccessMode::kReadOnly, |
92 | 1 | sizeof(SimpleObject))); |
93 | | |
94 | 1 | ASSERT_EQ(ro_handle1.GetFd(), owner_handle.GetFd()); |
95 | 1 | ASSERT_TRUE(ro_handle1.GetAddress()); |
96 | | |
97 | 1 | auto* owner_object = new(owner_handle.GetAddress()) SimpleObject(); |
98 | 1 | auto* ro_object1 = static_cast<SimpleObject*>(ro_handle1.GetAddress()); |
99 | | |
100 | | // Read-only handle can see default values. |
101 | 1 | ASSERT_EQ(1, ro_object1->value); |
102 | 1 | ASSERT_EQ(2, ro_object1->atomic_value.load(std::memory_order_acquire)); |
103 | | |
104 | 1 | owner_object->value = 3; |
105 | 1 | owner_object->atomic_value.store(8, std::memory_order_release); |
106 | | |
107 | | // Read-only handle can see updated values. |
108 | 1 | ASSERT_EQ(3, ro_object1->value); |
109 | 1 | ASSERT_EQ(8, ro_object1->atomic_value.load(std::memory_order_acquire)); |
110 | | |
111 | | // Writing to read-only memory fails. |
112 | 1 | pid_t child_pid = fork(); |
113 | 1 | if (child_pid == 0) { |
114 | 1 | auto ro_handle2 = ASSERT_RESULT(SharedMemorySegment::Open( |
115 | 1 | owner_handle.GetFd(), |
116 | 1 | SharedMemorySegment::AccessMode::kReadOnly, |
117 | 1 | sizeof(SimpleObject))); |
118 | | |
119 | 1 | auto* ro_object2 = static_cast<SimpleObject*>(ro_handle2.GetAddress()); |
120 | | |
121 | | // New handle can see the correct values. |
122 | 1 | ASSERT_EQ(3, ro_object2->value); |
123 | 1 | ASSERT_EQ(8, ro_object2->atomic_value.load(std::memory_order_acquire)); |
124 | | |
125 | 1 | auto open_result = SharedMemorySegment::Open( |
126 | 1 | owner_handle.GetFd(), |
127 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
128 | 1 | sizeof(SimpleObject)); |
129 | 1 | if (!open_result.ok()) { |
130 | 0 | LOG(WARNING) << "SharedMemorySegment::Open() failed: " << open_result.status(); |
131 | 0 | exit(1); |
132 | 0 | } |
133 | | |
134 | | // Update shared object, so we can verify we made it here. |
135 | 1 | static_cast<SimpleObject*>(open_result->GetAddress())->value = 6; |
136 | | |
137 | | // Unregister existing handlers for SIGBUS and SIGSEGV. |
138 | | // Since we are only forking, the test handler will catch these |
139 | | // and fail the test, even though we are in a different process. |
140 | 1 | signal(SIGBUS, SIG_DFL); |
141 | 1 | signal(SIGSEGV, SIG_DFL); |
142 | | |
143 | | // Attempt to write to the read-only segment. |
144 | 1 | ro_object2->value = 5; |
145 | | |
146 | | // We shouldn't get here. |
147 | 1 | ASSERT_TRUE(false); |
148 | 1 | } |
149 | | |
150 | | // Check that child exits with SIGBUS or SIGSEGV. |
151 | 0 | int status; |
152 | 0 | ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); |
153 | 1 | ASSERT_TRUE(WIFSIGNALED(status)); |
154 | 1 | int term_sig = WTERMSIG(status); |
155 | 1 | ASSERT_TRUE(term_sig == SIGBUS || term_sig == SIGSEGV); |
156 | | |
157 | | // Check that invalid write was not applied. |
158 | 1 | ASSERT_EQ(6, ro_object1->value); |
159 | 1 | ASSERT_EQ(8, ro_object1->atomic_value.load(std::memory_order_acquire)); |
160 | 1 | } |
161 | | |
162 | 1 | TEST_F(SharedMemoryTest, TestOpenInvalidSharedSegment) { |
163 | | // Create with invalid size. |
164 | 1 | auto segment_result = SharedMemorySegment::Create(-1 /* fd */); |
165 | | |
166 | 1 | ASSERT_NOK(segment_result); |
167 | | |
168 | | // Open with invalid file descriptor. |
169 | 1 | segment_result = SharedMemorySegment::Open( |
170 | 1 | -1 /* fd */, |
171 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
172 | 1 | sizeof(SimpleObject)); |
173 | | |
174 | 1 | ASSERT_NOK(segment_result); |
175 | | |
176 | 1 | auto valid_segment = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
177 | | |
178 | | // Open with invalid size. |
179 | 1 | segment_result = SharedMemorySegment::Open( |
180 | 1 | valid_segment.GetFd(), |
181 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
182 | 1 | -1 /* segment_size */); |
183 | | |
184 | 1 | ASSERT_NOK(segment_result); |
185 | 1 | } |
186 | | |
187 | 1 | TEST_F(SharedMemoryTest, TestMultipleDistinctSegments) { |
188 | 1 | auto owner1 = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
189 | 1 | auto follower1 = ASSERT_RESULT(SharedMemorySegment::Open( |
190 | 1 | owner1.GetFd(), |
191 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
192 | 1 | sizeof(SimpleObject))); |
193 | | |
194 | 1 | auto* owner1_object = new(owner1.GetAddress()) SimpleObject(); |
195 | 1 | auto* follower1_object = static_cast<SimpleObject*>(follower1.GetAddress()); |
196 | | |
197 | 1 | auto owner2 = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
198 | 1 | auto follower2 = ASSERT_RESULT(SharedMemorySegment::Open( |
199 | 1 | owner2.GetFd(), |
200 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
201 | 1 | sizeof(SimpleObject))); |
202 | | |
203 | 1 | auto* owner2_object = new(owner2.GetAddress()) SimpleObject(); |
204 | 1 | auto* follower2_object = static_cast<SimpleObject*>(follower2.GetAddress()); |
205 | | |
206 | 1 | ASSERT_EQ(1, owner1_object->value); |
207 | 1 | ASSERT_EQ(1, follower1_object->value); |
208 | 1 | ASSERT_EQ(1, owner2_object->value); |
209 | 1 | ASSERT_EQ(1, follower2_object->value); |
210 | | |
211 | 1 | owner1_object->value = 11; |
212 | 1 | owner2_object->value = 44; |
213 | | |
214 | 1 | ASSERT_EQ(11, owner1_object->value); |
215 | 1 | ASSERT_EQ(11, follower1_object->value); |
216 | 1 | ASSERT_EQ(44, owner2_object->value); |
217 | 1 | ASSERT_EQ(44, follower2_object->value); |
218 | | |
219 | 1 | follower1_object->value = 67; |
220 | 1 | follower2_object->value = 128; |
221 | | |
222 | 1 | ASSERT_EQ(67, owner1_object->value); |
223 | 1 | ASSERT_EQ(67, follower1_object->value); |
224 | 1 | ASSERT_EQ(128, owner2_object->value); |
225 | 1 | ASSERT_EQ(128, follower2_object->value); |
226 | 1 | } |
227 | | |
228 | 1 | TEST_F(SharedMemoryTest, TestAtomicSharedObjectUniprocess) { |
229 | 1 | auto handle1 = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
230 | 1 | auto handle2 = ASSERT_RESULT(SharedMemorySegment::Open( |
231 | 1 | handle1.GetFd(), |
232 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
233 | 1 | sizeof(SimpleObject))); |
234 | | |
235 | 1 | auto* handle1_object = new(handle1.GetAddress()) SimpleObject(); |
236 | 1 | auto* handle2_object = static_cast<SimpleObject*>(handle2.GetAddress()); |
237 | | |
238 | | // Start at 0. |
239 | 1 | handle1_object->atomic_value.store(0, std::memory_order_release); |
240 | | |
241 | 1 | std::thread thread1([&] { |
242 | | // Increment the value 1000 times. |
243 | 1.00k | for (int i = 0; i < 1000; i++) { |
244 | 1.00k | handle1_object->atomic_value.fetch_add(1); |
245 | 1.00k | } |
246 | 1 | }); |
247 | | |
248 | 1 | std::thread thread2([&] { |
249 | | // Increment the value 1000 times. |
250 | 1.00k | for (int i = 0; i < 1000; i++) { |
251 | 1.00k | handle2_object->atomic_value.fetch_add(1); |
252 | 1.00k | } |
253 | 1 | }); |
254 | | |
255 | 1 | thread1.join(); |
256 | 1 | thread2.join(); |
257 | | |
258 | | // Check that all increments were atomic. |
259 | 1 | ASSERT_EQ(2000, handle1_object->atomic_value.load(std::memory_order_acquire)); |
260 | 1 | ASSERT_EQ(2000, handle2_object->atomic_value.load(std::memory_order_acquire)); |
261 | 1 | } |
262 | | |
263 | 1 | TEST_F(SharedMemoryTest, TestAtomicSharedObjectMultiprocess) { |
264 | 1 | auto handle1 = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
265 | | |
266 | 1 | auto* handle1_object = new(handle1.GetAddress()) SimpleObject(); |
267 | | |
268 | | // Start at -1. |
269 | 1 | handle1_object->atomic_value.store(-1, std::memory_order_release); |
270 | | |
271 | 1 | pid_t child_pid = fork(); |
272 | 1 | if (child_pid == 0) { |
273 | 1 | auto handle2 = ASSERT_RESULT(SharedMemorySegment::Open( |
274 | 1 | handle1.GetFd(), |
275 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
276 | 1 | sizeof(SimpleObject))); |
277 | | |
278 | 1 | auto* handle2_object = static_cast<SimpleObject*>(handle2.GetAddress()); |
279 | | |
280 | | // Set value to 0, signalling parent to start. |
281 | 1 | handle1_object->atomic_value.store(0, std::memory_order_release); |
282 | | |
283 | | // Increment the value 10000 times. |
284 | 10.0k | for (int i = 0; i < 10000; i++) { |
285 | 10.0k | handle2_object->atomic_value.fetch_add(1); |
286 | 10.0k | } |
287 | | |
288 | 1 | exit(0); |
289 | 0 | } |
290 | | |
291 | | // Wait for child process to start and map shared memory. |
292 | 1 | while (handle1_object->atomic_value.load(std::memory_order_acquire) < 0) { |
293 | 1 | std::this_thread::sleep_for(1ms); |
294 | 1 | } |
295 | | |
296 | | // Increment the value 10000 times. |
297 | 10.0k | for (int i = 0; i < 10000; i++) { |
298 | 10.0k | handle1_object->atomic_value.fetch_add(1); |
299 | 10.0k | } |
300 | |
|
301 | 0 | int status; |
302 | 0 | ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); |
303 | 1 | ASSERT_FALSE(WIFSIGNALED(status)); |
304 | 1 | ASSERT_EQ(0, WEXITSTATUS(status)); |
305 | | |
306 | | // Check that all increments were atomic. |
307 | 1 | ASSERT_EQ(20000, handle1_object->atomic_value.load(std::memory_order_acquire)); |
308 | 1 | } |
309 | | |
310 | 1 | TEST_F(SharedMemoryTest, TestMultipleReadOnlyFollowers) { |
311 | 1 | auto owner_handle = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
312 | 1 | auto* owner_object = new(owner_handle.GetAddress()) SimpleObject(); |
313 | | |
314 | 1 | auto follower1_handle = ASSERT_RESULT(SharedMemorySegment::Open( |
315 | 1 | owner_handle.GetFd(), |
316 | 1 | SharedMemorySegment::AccessMode::kReadOnly, |
317 | 1 | sizeof(SimpleObject))); |
318 | 1 | auto* follower1_object = static_cast<SimpleObject*>(follower1_handle.GetAddress()); |
319 | | |
320 | 1 | auto follower2_handle = ASSERT_RESULT(SharedMemorySegment::Open( |
321 | 1 | owner_handle.GetFd(), |
322 | 1 | SharedMemorySegment::AccessMode::kReadOnly, |
323 | 1 | sizeof(SimpleObject))); |
324 | 1 | auto* follower2_object = static_cast<SimpleObject*>(follower2_handle.GetAddress()); |
325 | | |
326 | | // All handles see correct defaults. |
327 | 1 | ASSERT_EQ(1, owner_object->value); |
328 | 1 | ASSERT_EQ(1, follower1_object->value); |
329 | 1 | ASSERT_EQ(1, follower2_object->value); |
330 | | |
331 | | // All handles observe value being set. |
332 | 1 | owner_object->value = 20; |
333 | 1 | ASSERT_EQ(20, owner_object->value); |
334 | 1 | ASSERT_EQ(20, follower1_object->value); |
335 | 1 | ASSERT_EQ(20, follower2_object->value); |
336 | | |
337 | | // All handles observe value being updated. |
338 | 1 | owner_object->value = 13; |
339 | 1 | ASSERT_EQ(13, owner_object->value); |
340 | 1 | ASSERT_EQ(13, follower1_object->value); |
341 | 1 | ASSERT_EQ(13, follower2_object->value); |
342 | 1 | } |
343 | | |
344 | 1 | TEST_F(SharedMemoryTest, TestMultipleReadWriteFollowers) { |
345 | 1 | auto owner_handle = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
346 | 1 | auto* owner_object = new(owner_handle.GetAddress()) SimpleObject(); |
347 | | |
348 | 1 | auto follower1_handle = ASSERT_RESULT(SharedMemorySegment::Open( |
349 | 1 | owner_handle.GetFd(), |
350 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
351 | 1 | sizeof(SimpleObject))); |
352 | 1 | auto* follower1_object = static_cast<SimpleObject*>(follower1_handle.GetAddress()); |
353 | | |
354 | 1 | auto follower2_handle = ASSERT_RESULT(SharedMemorySegment::Open( |
355 | 1 | owner_handle.GetFd(), |
356 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
357 | 1 | sizeof(SimpleObject))); |
358 | 1 | auto* follower2_object = static_cast<SimpleObject*>(follower2_handle.GetAddress()); |
359 | | |
360 | | // All handles see correct defaults. |
361 | 1 | ASSERT_EQ(1, owner_object->value); |
362 | 1 | ASSERT_EQ(1, follower1_object->value); |
363 | 1 | ASSERT_EQ(1, follower2_object->value); |
364 | | |
365 | | // All handles observe changes from owner. |
366 | 1 | owner_object->value = 20; |
367 | 1 | ASSERT_EQ(20, owner_object->value); |
368 | 1 | ASSERT_EQ(20, follower1_object->value); |
369 | 1 | ASSERT_EQ(20, follower2_object->value); |
370 | | |
371 | | // All handles observe changes from followers. |
372 | 1 | follower1_object->value = 13; |
373 | 1 | ASSERT_EQ(13, owner_object->value); |
374 | 1 | ASSERT_EQ(13, follower1_object->value); |
375 | 1 | ASSERT_EQ(13, follower2_object->value); |
376 | 1 | follower2_object->value = 6; |
377 | 1 | ASSERT_EQ(6, owner_object->value); |
378 | 1 | ASSERT_EQ(6, follower1_object->value); |
379 | 1 | ASSERT_EQ(6, follower2_object->value); |
380 | 1 | } |
381 | | |
382 | 1 | TEST_F(SharedMemoryTest, TestSharedMemoryOwnership) { |
383 | 1 | int fd = -1; |
384 | | |
385 | 1 | { // Shared memory scope. |
386 | 1 | auto handle = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
387 | 1 | fd = handle.GetFd(); |
388 | 1 | } |
389 | | |
390 | | // File descriptor is no longer open. |
391 | 1 | ASSERT_NOK(SharedMemorySegment::Open( |
392 | 1 | fd, |
393 | 1 | SharedMemorySegment::AccessMode::kReadWrite, |
394 | 1 | sizeof(SimpleObject))); |
395 | 1 | } |
396 | | |
397 | 1 | TEST_F(SharedMemoryTest, TestAccessAfterClose) { |
398 | 1 | SimpleObject* object = nullptr; |
399 | | |
400 | 1 | { // Shared memory scope. |
401 | 1 | auto handle = ASSERT_RESULT(SharedMemorySegment::Create(sizeof(SimpleObject))); |
402 | 1 | object = new(handle.GetAddress()) SimpleObject(); |
403 | 1 | } |
404 | | |
405 | | // Memory region has been unmapped. |
406 | 1 | ASSERT_DEATH({ |
407 | 1 | object->value = 5; |
408 | 1 | }, "SIGSEGV"); |
409 | 1 | } |
410 | | |
411 | | class Data { |
412 | | public: |
413 | 8 | int Get() const { |
414 | 8 | return value_.load(); |
415 | 8 | } |
416 | | |
417 | 4 | void Set(int value) { |
418 | 4 | value_.store(value); |
419 | 4 | } |
420 | | |
421 | | private: |
422 | | std::atomic<int> value_{0}; |
423 | | }; |
424 | | |
425 | | typedef SharedMemoryObject<Data> SharedData; |
426 | | |
427 | 1 | TEST_F(SharedMemoryTest, SharedCatalogVersion) { |
428 | 1 | SharedData tserver = ASSERT_RESULT(SharedData::Create()); |
429 | 1 | SharedData postgres = ASSERT_RESULT(SharedData::OpenReadOnly(tserver.GetFd())); |
430 | | |
431 | | // Default value is zero. |
432 | 1 | ASSERT_EQ(0, tserver->Get()); |
433 | 1 | ASSERT_EQ(0, postgres->Get()); |
434 | | |
435 | | // TServer can set catalog version. |
436 | 1 | tserver->Set(2); |
437 | 1 | ASSERT_EQ(2, tserver->Get()); |
438 | 1 | ASSERT_EQ(2, postgres->Get()); |
439 | | |
440 | | // TServer can update catalog version. |
441 | 1 | tserver->Set(4); |
442 | 1 | ASSERT_EQ(4, tserver->Get()); |
443 | 1 | ASSERT_EQ(4, postgres->Get()); |
444 | 1 | } |
445 | | |
446 | 1 | TEST_F(SharedMemoryTest, MultipleTabletServers) { |
447 | 1 | SharedData tserver1 = ASSERT_RESULT(SharedData::Create()); |
448 | 1 | SharedData postgres1 = ASSERT_RESULT(SharedData::OpenReadOnly(tserver1.GetFd())); |
449 | | |
450 | 1 | SharedData tserver2 = ASSERT_RESULT(SharedData::Create()); |
451 | 1 | SharedData postgres2 = ASSERT_RESULT(SharedData::OpenReadOnly(tserver2.GetFd())); |
452 | | |
453 | 1 | tserver1->Set(22); |
454 | 1 | tserver2->Set(17); |
455 | | |
456 | 1 | ASSERT_EQ(22, postgres1->Get()); |
457 | 1 | ASSERT_EQ(17, postgres2->Get()); |
458 | 1 | } |
459 | | |
460 | 1 | TEST_F(SharedMemoryTest, BadSegment) { |
461 | 1 | ASSERT_NOK(SharedData::OpenReadOnly(-1)); |
462 | 1 | } |
463 | | |
464 | | } // namespace util |
465 | | } // namespace yb |