1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #include "mongo/db/concurrency/lock_manager_defs.h"
32 #include "mongo/db/concurrency/lock_manager_test_help.h"
33 #include "mongo/unittest/unittest.h"
34
35 namespace mongo {
36
TEST(ResourceId,Semantics)37 TEST(ResourceId, Semantics) {
38 ResourceId resIdDb(RESOURCE_DATABASE, 324334234);
39 ASSERT(resIdDb.getType() == RESOURCE_DATABASE);
40 ASSERT(resIdDb.getHashId() == 324334234);
41
42 ResourceId resIdColl(RESOURCE_COLLECTION, std::string("TestDB.collection"));
43 ASSERT(resIdColl.getType() == RESOURCE_COLLECTION);
44
45 // Comparison functions
46
47 // Make sure the operator < is defined.
48 ASSERT(resIdDb < resIdColl || resIdColl < resIdDb);
49
50 ResourceId resId(RESOURCE_DATABASE, 324334234);
51 ASSERT_EQUALS(resIdDb, resId);
52
53 // Assignment functions
54 resId = resIdColl;
55 ASSERT_EQUALS(resId, resIdColl);
56 }
57
TEST(ResourceId,Constructors)58 TEST(ResourceId, Constructors) {
59 ResourceId resIdString(RESOURCE_COLLECTION, std::string("TestDB.collection"));
60 ResourceId resIdStringData(RESOURCE_COLLECTION, StringData("TestDB.collection"));
61
62 ASSERT_EQUALS(resIdString, resIdStringData);
63 }
64
TEST(ResourceId,Masking)65 TEST(ResourceId, Masking) {
66 const ResourceType maxRes = static_cast<ResourceType>(ResourceTypesCount - 1);
67 const uint64_t maxHash = (1ULL << 61) - 1; // Only 61 bits usable for hash
68 ResourceType resources[3] = {maxRes, RESOURCE_GLOBAL, RESOURCE_METADATA};
69 uint64_t hashes[3] = {maxHash, maxHash / 3, maxHash / 3 * 2};
70
71 // The test below verifies that types/hashes are stored/retrieved unchanged
72 for (int h = 0; h < 3; h++) {
73 for (int r = 0; r < 3; r++) {
74 ResourceId id(resources[r], hashes[h]);
75 ASSERT_EQUALS(id.getHashId(), hashes[h]);
76 ASSERT_EQUALS(id.getType(), resources[r]);
77 }
78 }
79 }
80
81 //
82 // LockManager
83 //
84
TEST(LockManager,Grant)85 TEST(LockManager, Grant) {
86 LockManager lockMgr;
87 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
88
89 MMAPV1LockerImpl locker;
90 TrackingLockGrantNotification notify;
91
92 LockRequest request;
93 request.initNew(&locker, ¬ify);
94
95 ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S));
96 ASSERT(request.mode == MODE_S);
97 ASSERT(request.recursiveCount == 1);
98 ASSERT(notify.numNotifies == 0);
99
100 lockMgr.unlock(&request);
101 ASSERT(request.recursiveCount == 0);
102 }
103
TEST(LockManager,GrantMultipleNoConflict)104 TEST(LockManager, GrantMultipleNoConflict) {
105 LockManager lockMgr;
106 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
107
108 MMAPV1LockerImpl locker;
109 TrackingLockGrantNotification notify;
110
111 LockRequest request[6];
112 for (int i = 0; i < 6; i++) {
113 request[i].initNew(&locker, ¬ify);
114 ASSERT(LOCK_OK == lockMgr.lock(resId, &request[i], MODE_S));
115
116 ASSERT(request[i].mode == MODE_S);
117 ASSERT(request[i].recursiveCount == 1);
118 }
119
120 ASSERT(notify.numNotifies == 0);
121
122 // Free the first
123 lockMgr.unlock(&request[0]);
124
125 // Free the last
126 lockMgr.unlock(&request[5]);
127
128 // Free one in the middle
129 lockMgr.unlock(&request[3]);
130
131 // Free the remaining so the LockMgr does not compain about leaked locks
132 lockMgr.unlock(&request[1]);
133 lockMgr.unlock(&request[2]);
134 lockMgr.unlock(&request[4]);
135 }
136
TEST(LockManager,GrantMultipleFIFOOrder)137 TEST(LockManager, GrantMultipleFIFOOrder) {
138 LockManager lockMgr;
139 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
140
141 std::unique_ptr<MMAPV1LockerImpl> locker[6];
142 for (int i = 0; i < 6; i++) {
143 locker[i].reset(new MMAPV1LockerImpl());
144 }
145
146 TrackingLockGrantNotification notify[6];
147
148 LockRequest request[6];
149 for (int i = 0; i < 6; i++) {
150 request[i].initNew(locker[i].get(), ¬ify[i]);
151 lockMgr.lock(resId, &request[i], MODE_X);
152
153 ASSERT(request[i].mode == MODE_X);
154 ASSERT(request[i].recursiveCount == 1);
155 }
156
157 // Release the last held lock and ensure the next one, based on time is granted
158 for (int i = 0; i < 5; i++) {
159 lockMgr.unlock(&request[i]);
160
161 ASSERT(notify[i + 1].numNotifies == 1);
162 ASSERT(notify[i + 1].lastResId == resId);
163 ASSERT(notify[i + 1].lastResult == LOCK_OK);
164 }
165
166 // Release the last one
167 lockMgr.unlock(&request[5]);
168 }
169
TEST(LockManager,GrantRecursive)170 TEST(LockManager, GrantRecursive) {
171 LockManager lockMgr;
172 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
173
174 MMAPV1LockerImpl locker;
175 LockRequestCombo request(&locker);
176
177 ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S));
178 ASSERT(request.mode == MODE_S);
179 ASSERT(request.recursiveCount == 1);
180 ASSERT(request.numNotifies == 0);
181
182 // Acquire again, in the same mode
183 ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_S));
184 ASSERT(request.mode == MODE_S);
185 ASSERT(request.recursiveCount == 2);
186 ASSERT(request.numNotifies == 0);
187
188 // Release first acquire
189 lockMgr.unlock(&request);
190 ASSERT(request.mode == MODE_S);
191 ASSERT(request.recursiveCount == 1);
192
193 // Release second acquire
194 lockMgr.unlock(&request);
195 ASSERT(request.recursiveCount == 0);
196 }
197
TEST(LockManager,GrantRecursiveCompatibleConvertUp)198 TEST(LockManager, GrantRecursiveCompatibleConvertUp) {
199 LockManager lockMgr;
200 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
201
202 MMAPV1LockerImpl locker;
203 LockRequestCombo request(&locker);
204
205 ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_IS));
206 ASSERT(request.mode == MODE_IS);
207 ASSERT(request.recursiveCount == 1);
208 ASSERT(request.numNotifies == 0);
209
210 // Acquire again, in *compatible*, but stricter mode
211 ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_S));
212 ASSERT(request.mode == MODE_S);
213 ASSERT(request.recursiveCount == 2);
214 ASSERT(request.numNotifies == 0);
215
216 // Release the first acquire
217 lockMgr.unlock(&request);
218 ASSERT(request.mode == MODE_S);
219 ASSERT(request.recursiveCount == 1);
220
221 // Release the second acquire
222 lockMgr.unlock(&request);
223 ASSERT(request.recursiveCount == 0);
224 }
225
TEST(LockManager,GrantRecursiveNonCompatibleConvertUp)226 TEST(LockManager, GrantRecursiveNonCompatibleConvertUp) {
227 LockManager lockMgr;
228 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
229
230 MMAPV1LockerImpl locker;
231 LockRequestCombo request(&locker);
232
233 ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_S));
234 ASSERT(request.mode == MODE_S);
235 ASSERT(request.recursiveCount == 1);
236 ASSERT(request.numNotifies == 0);
237
238 // Acquire again, in *non-compatible*, but stricter mode
239 ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_X));
240 ASSERT(request.mode == MODE_X);
241 ASSERT(request.recursiveCount == 2);
242 ASSERT(request.numNotifies == 0);
243
244 // Release first acquire
245 lockMgr.unlock(&request);
246 ASSERT(request.mode == MODE_X);
247 ASSERT(request.recursiveCount == 1);
248
249 // Release second acquire
250 lockMgr.unlock(&request);
251 ASSERT(request.recursiveCount == 0);
252 }
253
TEST(LockManager,GrantRecursiveNonCompatibleConvertDown)254 TEST(LockManager, GrantRecursiveNonCompatibleConvertDown) {
255 LockManager lockMgr;
256 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
257
258 MMAPV1LockerImpl locker;
259 LockRequestCombo request(&locker);
260
261 ASSERT(LOCK_OK == lockMgr.lock(resId, &request, MODE_X));
262 ASSERT(request.mode == MODE_X);
263 ASSERT(request.recursiveCount == 1);
264 ASSERT(request.numNotifies == 0);
265
266 // Acquire again, in *non-compatible*, but less strict mode
267 ASSERT(LOCK_OK == lockMgr.convert(resId, &request, MODE_S));
268 ASSERT(request.mode == MODE_X);
269 ASSERT(request.recursiveCount == 2);
270 ASSERT(request.numNotifies == 0);
271
272 // Release first acquire
273 lockMgr.unlock(&request);
274 ASSERT(request.mode == MODE_X);
275 ASSERT(request.recursiveCount == 1);
276
277 // Release second acquire
278 lockMgr.unlock(&request);
279 ASSERT(request.recursiveCount == 0);
280 }
281
TEST(LockManager,Conflict)282 TEST(LockManager, Conflict) {
283 LockManager lockMgr;
284 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
285
286 MMAPV1LockerImpl locker1;
287 MMAPV1LockerImpl locker2;
288
289 LockRequestCombo request1(&locker1);
290 LockRequestCombo request2(&locker2);
291
292 // First request granted right away
293 ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S));
294 ASSERT(request1.recursiveCount == 1);
295 ASSERT(request1.numNotifies == 0);
296
297 // Second request must block
298 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_X));
299 ASSERT(request2.mode == MODE_X);
300 ASSERT(request2.recursiveCount == 1);
301 ASSERT(request2.numNotifies == 0);
302
303 // Release first request
304 lockMgr.unlock(&request1);
305 ASSERT(request1.recursiveCount == 0);
306 ASSERT(request1.numNotifies == 0);
307
308 ASSERT(request2.mode == MODE_X);
309 ASSERT(request2.recursiveCount == 1);
310 ASSERT(request2.numNotifies == 1);
311 ASSERT(request2.lastResult == LOCK_OK);
312
313 // Release second acquire
314 lockMgr.unlock(&request2);
315 ASSERT(request2.recursiveCount == 0);
316
317 ASSERT(request1.numNotifies == 0);
318 ASSERT(request2.numNotifies == 1);
319 }
320
TEST(LockManager,MultipleConflict)321 TEST(LockManager, MultipleConflict) {
322 LockManager lockMgr;
323 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
324
325 MMAPV1LockerImpl locker;
326 TrackingLockGrantNotification notify;
327
328 LockRequest request[6];
329 for (int i = 0; i < 6; i++) {
330 request[i].initNew(&locker, ¬ify);
331
332 if (i == 0) {
333 ASSERT(LOCK_OK == lockMgr.lock(resId, &request[i], MODE_X));
334 } else {
335 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request[i], MODE_X));
336 }
337
338 ASSERT(request[i].mode == MODE_X);
339 ASSERT(request[i].recursiveCount == 1);
340 }
341
342 ASSERT(notify.numNotifies == 0);
343
344 // Free them one by one and make sure they get granted in the correct order
345 for (int i = 0; i < 6; i++) {
346 lockMgr.unlock(&request[i]);
347
348 if (i < 5) {
349 ASSERT(notify.numNotifies == i + 1);
350 }
351 }
352 }
353
TEST(LockManager,ConflictCancelWaiting)354 TEST(LockManager, ConflictCancelWaiting) {
355 LockManager lockMgr;
356 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
357
358 MMAPV1LockerImpl locker1;
359 TrackingLockGrantNotification notify1;
360
361 MMAPV1LockerImpl locker2;
362 TrackingLockGrantNotification notify2;
363
364 LockRequest request1;
365 request1.initNew(&locker1, ¬ify1);
366
367 LockRequest request2;
368 request2.initNew(&locker2, ¬ify2);
369
370 // First request granted right away
371 ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S));
372 ASSERT(notify1.numNotifies == 0);
373
374 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_X));
375
376 // Release second request (which is still in the WAITING mode)
377 lockMgr.unlock(&request2);
378 ASSERT(notify2.numNotifies == 0);
379
380 ASSERT(request1.mode == MODE_S);
381 ASSERT(request1.recursiveCount == 1);
382
383 // Release second acquire
384 lockMgr.unlock(&request1);
385 }
386
TEST(LockManager,ConflictCancelMultipleWaiting)387 TEST(LockManager, ConflictCancelMultipleWaiting) {
388 LockManager lockMgr;
389 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
390
391 MMAPV1LockerImpl locker;
392 TrackingLockGrantNotification notify;
393
394 LockRequest request[6];
395 for (int i = 0; i < 6; i++) {
396 request[i].initNew(&locker, ¬ify);
397 lockMgr.lock(resId, &request[i], MODE_X);
398
399 ASSERT(request[i].mode == MODE_X);
400 ASSERT(request[i].recursiveCount == 1);
401 }
402
403 ASSERT(notify.numNotifies == 0);
404
405 // Free the second (waiting)
406 lockMgr.unlock(&request[1]);
407
408 // Free the last
409 lockMgr.unlock(&request[5]);
410
411 // Free one in the middle
412 lockMgr.unlock(&request[3]);
413
414 // Free the remaining so the LockMgr does not compain about leaked locks
415 lockMgr.unlock(&request[2]);
416 lockMgr.unlock(&request[4]);
417 lockMgr.unlock(&request[0]);
418 }
419
TEST(LockManager,CancelWaitingConversionWeakModes)420 TEST(LockManager, CancelWaitingConversionWeakModes) {
421 LockManager lockMgr;
422 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
423
424 MMAPV1LockerImpl locker1;
425 MMAPV1LockerImpl locker2;
426
427 LockRequestCombo request1(&locker1);
428 LockRequestCombo request2(&locker2);
429
430 // First request granted right away
431 ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_IS));
432 ASSERT(request1.numNotifies == 0);
433
434 // Second request is granted right away
435 ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_IX));
436 ASSERT(request2.numNotifies == 0);
437
438 // Convert first request to conflicting
439 ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request1, MODE_S));
440 ASSERT(request1.mode == MODE_IS);
441 ASSERT(request1.convertMode == MODE_S);
442 ASSERT(request1.numNotifies == 0);
443
444 // Cancel the conflicting conversion
445 lockMgr.unlock(&request1);
446 ASSERT(request1.mode == MODE_IS);
447 ASSERT(request1.convertMode == MODE_NONE);
448 ASSERT(request1.numNotifies == 0);
449
450 // Free the remaining locks so the LockManager destructor does not complain
451 lockMgr.unlock(&request1);
452 lockMgr.unlock(&request2);
453 }
454
TEST(LockManager,CancelWaitingConversionStrongModes)455 TEST(LockManager, CancelWaitingConversionStrongModes) {
456 LockManager lockMgr;
457 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
458
459 MMAPV1LockerImpl locker1;
460 MMAPV1LockerImpl locker2;
461
462 LockRequestCombo request1(&locker1);
463 LockRequestCombo request2(&locker2);
464
465 // First request granted right away
466 ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S));
467 ASSERT(request1.numNotifies == 0);
468
469 // Second request is granted right away
470 ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S));
471 ASSERT(request2.numNotifies == 0);
472
473 // Convert second request to conflicting
474 ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request2, MODE_X));
475 ASSERT(request2.mode == MODE_S);
476 ASSERT(request2.convertMode == MODE_X);
477 ASSERT(request2.numNotifies == 0);
478
479 // Cancel the conflicting upgrade
480 lockMgr.unlock(&request2);
481 ASSERT(request2.mode == MODE_S);
482 ASSERT(request2.convertMode == MODE_NONE);
483 ASSERT(request2.numNotifies == 0);
484
485 // Free the remaining locks so the LockManager destructor does not complain
486 lockMgr.unlock(&request1);
487 lockMgr.unlock(&request2);
488 }
489
TEST(LockManager,ConflictingConversion)490 TEST(LockManager, ConflictingConversion) {
491 LockManager lockMgr;
492 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
493
494 MMAPV1LockerImpl locker1;
495 MMAPV1LockerImpl locker2;
496
497 LockRequestCombo request1(&locker1);
498 LockRequestCombo request2(&locker2);
499
500 // The S requests are granted right away
501 ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S));
502 ASSERT(request1.numNotifies == 0);
503
504 ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S));
505 ASSERT(request2.numNotifies == 0);
506
507 // Convert first request to conflicting
508 ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request1, MODE_X));
509 ASSERT(request1.numNotifies == 0);
510
511 // Free the second lock and make sure the first is granted
512 lockMgr.unlock(&request2);
513 ASSERT(request1.mode == MODE_X);
514 ASSERT(request1.numNotifies == 1);
515 ASSERT(request2.numNotifies == 0);
516
517 // Frees the first reference, mode remains X
518 lockMgr.unlock(&request1);
519 ASSERT(request1.mode == MODE_X);
520 ASSERT(request1.recursiveCount == 1);
521
522 lockMgr.unlock(&request1);
523 }
524
TEST(LockManager,ConflictingConversionInTheMiddle)525 TEST(LockManager, ConflictingConversionInTheMiddle) {
526 LockManager lockMgr;
527 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
528
529 MMAPV1LockerImpl locker;
530 TrackingLockGrantNotification notify;
531
532 LockRequest request[3];
533 for (int i = 0; i < 3; i++) {
534 request[i].initNew(&locker, ¬ify);
535 lockMgr.lock(resId, &request[i], MODE_S);
536 }
537
538 // Upgrade the one in the middle (not the first one)
539 ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request[1], MODE_X));
540
541 ASSERT(notify.numNotifies == 0);
542
543 // Release the two shared modes
544 lockMgr.unlock(&request[0]);
545 ASSERT(notify.numNotifies == 0);
546
547 lockMgr.unlock(&request[2]);
548 ASSERT(notify.numNotifies == 1);
549
550 ASSERT(request[1].mode == MODE_X);
551
552 // Request 1 should be unlocked twice
553 lockMgr.unlock(&request[1]);
554 lockMgr.unlock(&request[1]);
555 }
556
TEST(LockManager,ConvertUpgrade)557 TEST(LockManager, ConvertUpgrade) {
558 LockManager lockMgr;
559 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
560
561 MMAPV1LockerImpl locker1;
562 LockRequestCombo request1(&locker1);
563 ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_S));
564
565 MMAPV1LockerImpl locker2;
566 LockRequestCombo request2(&locker2);
567 ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_S));
568
569 // Upgrade the S lock to X
570 ASSERT(LOCK_WAITING == lockMgr.convert(resId, &request1, MODE_X));
571
572 ASSERT(!lockMgr.unlock(&request1));
573 ASSERT(lockMgr.unlock(&request1));
574
575 ASSERT(lockMgr.unlock(&request2));
576 }
577
TEST(LockManager,Downgrade)578 TEST(LockManager, Downgrade) {
579 LockManager lockMgr;
580 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
581
582 MMAPV1LockerImpl locker1;
583 LockRequestCombo request1(&locker1);
584 ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_X));
585
586 MMAPV1LockerImpl locker2;
587 LockRequestCombo request2(&locker2);
588 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_S));
589
590 // Downgrade the X request to S
591 lockMgr.downgrade(&request1, MODE_S);
592
593 ASSERT(request2.numNotifies == 1);
594 ASSERT(request2.lastResult == LOCK_OK);
595 ASSERT(request2.recursiveCount == 1);
596
597 ASSERT(lockMgr.unlock(&request1));
598 ASSERT(lockMgr.unlock(&request2));
599 }
600
601
602 // Lock conflict matrix tests
checkConflict(LockMode existingMode,LockMode newMode,bool hasConflict)603 static void checkConflict(LockMode existingMode, LockMode newMode, bool hasConflict) {
604 LockManager lockMgr;
605 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
606
607 MMAPV1LockerImpl lockerExisting;
608 TrackingLockGrantNotification notifyExisting;
609 LockRequest requestExisting;
610 requestExisting.initNew(&lockerExisting, ¬ifyExisting);
611
612 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestExisting, existingMode));
613
614 MMAPV1LockerImpl lockerNew;
615 TrackingLockGrantNotification notifyNew;
616 LockRequest requestNew;
617 requestNew.initNew(&lockerNew, ¬ifyNew);
618
619 LockResult result = lockMgr.lock(resId, &requestNew, newMode);
620 if (hasConflict) {
621 ASSERT_EQUALS(LOCK_WAITING, result);
622 } else {
623 ASSERT_EQUALS(LOCK_OK, result);
624 }
625
626 lockMgr.unlock(&requestNew);
627 lockMgr.unlock(&requestExisting);
628 }
629
TEST(LockManager,ValidateConflictMatrix)630 TEST(LockManager, ValidateConflictMatrix) {
631 checkConflict(MODE_IS, MODE_IS, false);
632 checkConflict(MODE_IS, MODE_IX, false);
633 checkConflict(MODE_IS, MODE_S, false);
634 checkConflict(MODE_IS, MODE_X, true);
635
636 checkConflict(MODE_IX, MODE_IS, false);
637 checkConflict(MODE_IX, MODE_IX, false);
638 checkConflict(MODE_IX, MODE_S, true);
639 checkConflict(MODE_IX, MODE_X, true);
640
641 checkConflict(MODE_S, MODE_IS, false);
642 checkConflict(MODE_S, MODE_IX, true);
643 checkConflict(MODE_S, MODE_S, false);
644 checkConflict(MODE_S, MODE_X, true);
645
646 checkConflict(MODE_X, MODE_IS, true);
647 checkConflict(MODE_X, MODE_IX, true);
648 checkConflict(MODE_X, MODE_S, true);
649 checkConflict(MODE_X, MODE_X, true);
650 }
651
TEST(LockManager,EnqueueAtFront)652 TEST(LockManager, EnqueueAtFront) {
653 LockManager lockMgr;
654 const ResourceId resId(RESOURCE_COLLECTION, std::string("TestDB.collection"));
655
656 MMAPV1LockerImpl lockerX;
657 LockRequestCombo requestX(&lockerX);
658
659 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestX, MODE_X));
660
661 // The subsequent request will block
662 MMAPV1LockerImpl lockerLow;
663 LockRequestCombo requestLow(&lockerLow);
664
665 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestLow, MODE_X));
666
667 // This is a "queue jumping request", which will go before locker 2 above
668 MMAPV1LockerImpl lockerHi;
669 LockRequestCombo requestHi(&lockerHi);
670 requestHi.enqueueAtFront = true;
671
672 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestHi, MODE_X));
673
674 // Once the X request is gone, lockerHi should be granted, because it's queue jumping
675 ASSERT(lockMgr.unlock(&requestX));
676
677 ASSERT(requestHi.lastResId == resId);
678 ASSERT(requestHi.lastResult == LOCK_OK);
679
680 // Finally lockerLow should be granted
681 ASSERT(lockMgr.unlock(&requestHi));
682
683 ASSERT(requestLow.lastResId == resId);
684 ASSERT(requestLow.lastResult == LOCK_OK);
685
686 // This avoids the lock manager asserting on leaked locks
687 ASSERT(lockMgr.unlock(&requestLow));
688 }
689
TEST(LockManager,CompatibleFirstImmediateGrant)690 TEST(LockManager, CompatibleFirstImmediateGrant) {
691 LockManager lockMgr;
692 const ResourceId resId(RESOURCE_GLOBAL, 0);
693
694 MMAPV1LockerImpl locker1;
695 LockRequestCombo request1(&locker1);
696
697 MMAPV1LockerImpl locker2;
698 LockRequestCombo request2(&locker2);
699 request2.compatibleFirst = true;
700
701 MMAPV1LockerImpl locker3;
702 LockRequestCombo request3(&locker3);
703
704 // Lock all in IS mode
705 ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_IS));
706 ASSERT(LOCK_OK == lockMgr.lock(resId, &request2, MODE_IS));
707 ASSERT(LOCK_OK == lockMgr.lock(resId, &request3, MODE_IS));
708
709 // Now an exclusive mode comes, which would block
710 MMAPV1LockerImpl lockerX;
711 LockRequestCombo requestX(&lockerX);
712
713 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X));
714
715 // If an S comes, it should be granted, because of request2
716 {
717 MMAPV1LockerImpl lockerS;
718 LockRequestCombo requestS(&lockerS);
719 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S));
720 ASSERT(lockMgr.unlock(&requestS));
721 }
722
723 // If request1 goes away, the policy should still be compatible-first, because of request2
724 ASSERT(lockMgr.unlock(&request1));
725
726 // If S comes again, it should be granted, because of request2 still there
727 {
728 MMAPV1LockerImpl lockerS;
729 LockRequestCombo requestS(&lockerS);
730 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S));
731 ASSERT(lockMgr.unlock(&requestS));
732 }
733
734 // With request2 gone the policy should go back to FIFO, even though request3 is active
735 ASSERT(lockMgr.unlock(&request2));
736
737 {
738 MMAPV1LockerImpl lockerS;
739 LockRequestCombo requestS(&lockerS);
740 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S));
741 ASSERT(lockMgr.unlock(&requestS));
742 }
743
744 // Unlock request3 to keep the lock mgr not assert for leaked locks
745 ASSERT(lockMgr.unlock(&request3));
746 ASSERT(lockMgr.unlock(&requestX));
747 }
748
TEST(LockManager,CompatibleFirstGrantAlreadyQueued)749 TEST(LockManager, CompatibleFirstGrantAlreadyQueued) {
750 LockManager lockMgr;
751 const ResourceId resId(RESOURCE_GLOBAL, 0);
752
753 // This tests the following behaviors (alternatives indicated with '|'):
754 // Lock held in X, queue: S X|IX IS, where S is compatibleFirst.
755 // Once X unlocks|downgrades both the S and IS requests should proceed.
756
757
758 enum UnblockMethod { kDowngrading, kUnlocking };
759 LockMode conflictingModes[2] = {MODE_IX, MODE_X};
760 UnblockMethod unblockMethods[2] = {kDowngrading, kUnlocking};
761
762 for (LockMode writerMode : conflictingModes) {
763 for (UnblockMethod unblockMethod : unblockMethods) {
764 MMAPV1LockerImpl locker1;
765 LockRequestCombo request1(&locker1);
766
767 MMAPV1LockerImpl locker2;
768 LockRequestCombo request2(&locker2);
769 request2.compatibleFirst = true;
770
771 MMAPV1LockerImpl locker3;
772 LockRequestCombo request3(&locker3);
773
774 MMAPV1LockerImpl locker4;
775 LockRequestCombo request4(&locker4);
776
777 // Hold the lock in X and establish the S IX|X IS queue.
778 ASSERT(LOCK_OK == lockMgr.lock(resId, &request1, MODE_X));
779 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_S));
780 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request3, writerMode));
781 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request4, MODE_IS));
782
783 // Now unlock the initial X, so all readers should be able to proceed, while the writer
784 // remains queued.
785 if (unblockMethod == kUnlocking) {
786 ASSERT(lockMgr.unlock(&request1));
787 } else {
788 invariant(unblockMethod == kDowngrading);
789 lockMgr.downgrade(&request1, MODE_S);
790 }
791 ASSERT(request2.lastResult == LOCK_OK);
792 ASSERT(request3.lastResult == LOCK_INVALID);
793 ASSERT(request4.lastResult == LOCK_OK);
794
795 // Now unlock the readers, and the writer succeeds as well.
796 ASSERT(lockMgr.unlock(&request2));
797 ASSERT(lockMgr.unlock(&request4));
798 if (unblockMethod == kDowngrading) {
799 ASSERT(lockMgr.unlock(&request1));
800 }
801 ASSERT(request3.lastResult == LOCK_OK);
802
803 // Unlock the writer
804 ASSERT(lockMgr.unlock(&request3));
805 }
806 }
807 }
808
TEST(LockManager,CompatibleFirstDelayedGrant)809 TEST(LockManager, CompatibleFirstDelayedGrant) {
810 LockManager lockMgr;
811 const ResourceId resId(RESOURCE_GLOBAL, 0);
812
813 MMAPV1LockerImpl lockerXInitial;
814 LockRequestCombo requestXInitial(&lockerXInitial);
815 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestXInitial, MODE_X));
816
817 MMAPV1LockerImpl locker1;
818 LockRequestCombo request1(&locker1);
819
820 MMAPV1LockerImpl locker2;
821 LockRequestCombo request2(&locker2);
822 request2.compatibleFirst = true;
823
824 MMAPV1LockerImpl locker3;
825 LockRequestCombo request3(&locker3);
826
827 // Lock all in IS mode (should block behind the global lock)
828 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request1, MODE_IS));
829 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request2, MODE_IS));
830 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &request3, MODE_IS));
831
832 // Now an exclusive mode comes, which would block behind the IS modes
833 MMAPV1LockerImpl lockerX;
834 LockRequestCombo requestX(&lockerX);
835 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X));
836
837 // Free the first X lock so all IS modes are granted
838 ASSERT(lockMgr.unlock(&requestXInitial));
839 ASSERT(request1.lastResult == LOCK_OK);
840 ASSERT(request2.lastResult == LOCK_OK);
841 ASSERT(request3.lastResult == LOCK_OK);
842
843 // If an S comes, it should be granted, because of request2
844 {
845 MMAPV1LockerImpl lockerS;
846 LockRequestCombo requestS(&lockerS);
847 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S));
848 ASSERT(lockMgr.unlock(&requestS));
849 }
850
851 // If request1 goes away, the policy should still be compatible-first, because of request2
852 ASSERT(lockMgr.unlock(&request1));
853
854 // If S comes again, it should be granted, because of request2 still there
855 {
856 MMAPV1LockerImpl lockerS;
857 LockRequestCombo requestS(&lockerS);
858 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestS, MODE_S));
859 ASSERT(lockMgr.unlock(&requestS));
860 }
861
862 // With request2 gone the policy should go back to FIFO, even though request3 is active
863 ASSERT(lockMgr.unlock(&request2));
864
865 {
866 MMAPV1LockerImpl lockerS;
867 LockRequestCombo requestS(&lockerS);
868 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S));
869 ASSERT(lockMgr.unlock(&requestS));
870 }
871
872 // Unlock request3 to keep the lock mgr not assert for leaked locks
873 ASSERT(lockMgr.unlock(&request3));
874 ASSERT(lockMgr.unlock(&requestX));
875 }
876
TEST(LockManager,CompatibleFirstCancelWaiting)877 TEST(LockManager, CompatibleFirstCancelWaiting) {
878 LockManager lockMgr;
879 const ResourceId resId(RESOURCE_GLOBAL, 0);
880
881 MMAPV1LockerImpl lockerSInitial;
882 LockRequestCombo requestSInitial(&lockerSInitial);
883 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestSInitial, MODE_S));
884
885 MMAPV1LockerImpl lockerX;
886 LockRequestCombo requestX(&lockerX);
887 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X));
888
889 MMAPV1LockerImpl lockerPending;
890 LockRequestCombo requestPending(&lockerPending);
891 requestPending.compatibleFirst = true;
892 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestPending, MODE_S));
893
894 // S1 is not granted yet, so the policy should still be FIFO
895 {
896 MMAPV1LockerImpl lockerS;
897 LockRequestCombo requestS(&lockerS);
898 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S));
899 ASSERT(lockMgr.unlock(&requestS));
900 }
901
902 // Unlock S1, the policy should still be FIFO
903 ASSERT(lockMgr.unlock(&requestPending));
904
905 {
906 MMAPV1LockerImpl lockerS;
907 LockRequestCombo requestS(&lockerS);
908 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestS, MODE_S));
909 ASSERT(lockMgr.unlock(&requestS));
910 }
911
912 // Unlock remaining locks to keep the leak detection logic happy
913 ASSERT(lockMgr.unlock(&requestSInitial));
914 ASSERT(lockMgr.unlock(&requestX));
915 }
916
TEST(LockManager,Fairness)917 TEST(LockManager, Fairness) {
918 LockManager lockMgr;
919 const ResourceId resId(RESOURCE_GLOBAL, 0);
920
921 // Start with some 'regular' intent locks
922 MMAPV1LockerImpl lockerIS;
923 LockRequestCombo requestIS(&lockerIS);
924 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestIS, MODE_IS));
925
926 MMAPV1LockerImpl lockerIX;
927 LockRequestCombo requestIX(&lockerIX);
928 ASSERT(LOCK_OK == lockMgr.lock(resId, &requestIX, MODE_IX));
929
930 // Now a conflicting lock comes
931 MMAPV1LockerImpl lockerX;
932 LockRequestCombo requestX(&lockerX);
933 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestX, MODE_X));
934
935 // Now, whoever comes next should be blocked
936 MMAPV1LockerImpl lockerIX1;
937 LockRequestCombo requestIX1(&lockerIX1);
938 ASSERT(LOCK_WAITING == lockMgr.lock(resId, &requestIX1, MODE_IX));
939
940 // Freeing the first two locks should grant the X lock
941 ASSERT(lockMgr.unlock(&requestIS));
942 ASSERT(lockMgr.unlock(&requestIX));
943 ASSERT_EQ(LOCK_OK, requestX.lastResult);
944 ASSERT_EQ(1, requestX.numNotifies);
945 ASSERT_EQ(LOCK_INVALID, requestIX1.lastResult);
946 ASSERT_EQ(0, requestIX1.numNotifies);
947
948 ASSERT(lockMgr.unlock(&requestX));
949 ASSERT_EQ(LOCK_OK, requestIX1.lastResult);
950 ASSERT_EQ(1, requestIX1.numNotifies);
951
952 // Unlock all locks so we don't assert for leaked locks
953 ASSERT(lockMgr.unlock(&requestIX1));
954 }
955
956 } // namespace mongo
957