YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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