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, &notify);
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, &notify);
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(), &notify[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, &notify);
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, &notify1);
366 
367     LockRequest request2;
368     request2.initNew(&locker2, &notify2);
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, &notify);
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, &notify);
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, &notifyExisting);
611 
612     ASSERT(LOCK_OK == lockMgr.lock(resId, &requestExisting, existingMode));
613 
614     MMAPV1LockerImpl lockerNew;
615     TrackingLockGrantNotification notifyNew;
616     LockRequest requestNew;
617     requestNew.initNew(&lockerNew, &notifyNew);
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