Reverted commit D3270439
[folly.git] / folly / test / SharedMutexTest.cpp
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/SharedMutex.h>
18
19 #include <stdlib.h>
20 #include <thread>
21 #include <vector>
22
23 #include <gtest/gtest.h>
24 #include <boost/optional.hpp>
25 #include <boost/thread/shared_mutex.hpp>
26
27 #include <folly/Benchmark.h>
28 #include <folly/MPMCQueue.h>
29 #include <folly/RWSpinLock.h>
30 #include <folly/Random.h>
31 #include <folly/portability/GFlags.h>
32 #include <folly/test/DeterministicSchedule.h>
33
34 using namespace folly;
35 using namespace folly::test;
36 using namespace std;
37 using namespace chrono;
38
39 typedef DeterministicSchedule DSched;
40 typedef SharedMutexImpl<true, void, DeterministicAtomic, true>
41     DSharedMutexReadPriority;
42 typedef SharedMutexImpl<false, void, DeterministicAtomic, true>
43     DSharedMutexWritePriority;
44
45 COMMON_CONCURRENCY_SHARED_MUTEX_DECLARE_STATIC_STORAGE(
46     DSharedMutexReadPriority);
47 COMMON_CONCURRENCY_SHARED_MUTEX_DECLARE_STATIC_STORAGE(
48     DSharedMutexWritePriority);
49
50 template <typename Lock>
51 void runBasicTest() {
52   Lock lock;
53   SharedMutexToken token1;
54   SharedMutexToken token2;
55   SharedMutexToken token3;
56
57   EXPECT_TRUE(lock.try_lock());
58   EXPECT_FALSE(lock.try_lock());
59   EXPECT_FALSE(lock.try_lock_shared(token1));
60   lock.unlock();
61
62   EXPECT_TRUE(lock.try_lock_shared(token1));
63   EXPECT_FALSE(lock.try_lock());
64   EXPECT_TRUE(lock.try_lock_shared(token2));
65   lock.lock_shared(token3);
66   lock.unlock_shared(token3);
67   lock.unlock_shared(token2);
68   lock.unlock_shared(token1);
69
70   lock.lock();
71   lock.unlock();
72
73   lock.lock_shared(token1);
74   lock.lock_shared(token2);
75   lock.unlock_shared(token1);
76   lock.unlock_shared(token2);
77
78   lock.lock();
79   lock.unlock_and_lock_shared(token1);
80   lock.lock_shared(token2);
81   lock.unlock_shared(token2);
82   lock.unlock_shared(token1);
83 }
84
85 TEST(SharedMutex, basic) {
86   runBasicTest<SharedMutexReadPriority>();
87   runBasicTest<SharedMutexWritePriority>();
88 }
89
90 template <typename Lock>
91 void runBasicHoldersTest() {
92   Lock lock;
93   SharedMutexToken token;
94
95   {
96     // create an exclusive write lock via holder
97     typename Lock::WriteHolder holder(lock);
98     EXPECT_FALSE(lock.try_lock());
99     EXPECT_FALSE(lock.try_lock_shared(token));
100
101     // move ownership to another write holder via move constructor
102     typename Lock::WriteHolder holder2(std::move(holder));
103     EXPECT_FALSE(lock.try_lock());
104     EXPECT_FALSE(lock.try_lock_shared(token));
105
106     // move ownership to another write holder via assign operator
107     typename Lock::WriteHolder holder3;
108     holder3 = std::move(holder2);
109     EXPECT_FALSE(lock.try_lock());
110     EXPECT_FALSE(lock.try_lock_shared(token));
111
112     // downgrade from exclusive to upgrade lock via move constructor
113     typename Lock::UpgradeHolder holder4(std::move(holder3));
114
115     // ensure we can lock from a shared source
116     EXPECT_FALSE(lock.try_lock());
117     EXPECT_TRUE(lock.try_lock_shared(token));
118     lock.unlock_shared(token);
119
120     // promote from upgrade to exclusive lock via move constructor
121     typename Lock::WriteHolder holder5(std::move(holder4));
122     EXPECT_FALSE(lock.try_lock());
123     EXPECT_FALSE(lock.try_lock_shared(token));
124
125     // downgrade exclusive to shared lock via move constructor
126     typename Lock::ReadHolder holder6(std::move(holder5));
127
128     // ensure we can lock from another shared source
129     EXPECT_FALSE(lock.try_lock());
130     EXPECT_TRUE(lock.try_lock_shared(token));
131     lock.unlock_shared(token);
132   }
133
134   {
135     typename Lock::WriteHolder holder(lock);
136     EXPECT_FALSE(lock.try_lock());
137   }
138
139   {
140     typename Lock::ReadHolder holder(lock);
141     typename Lock::ReadHolder holder2(lock);
142     typename Lock::UpgradeHolder holder3(lock);
143   }
144
145   {
146     typename Lock::UpgradeHolder holder(lock);
147     typename Lock::ReadHolder holder2(lock);
148     typename Lock::ReadHolder holder3(std::move(holder));
149   }
150 }
151
152 TEST(SharedMutex, basic_holders) {
153   runBasicHoldersTest<SharedMutexReadPriority>();
154   runBasicHoldersTest<SharedMutexWritePriority>();
155 }
156
157 template <typename Lock>
158 void runManyReadLocksTestWithTokens() {
159   Lock lock;
160
161   vector<SharedMutexToken> tokens;
162   for (int i = 0; i < 1000; ++i) {
163     tokens.emplace_back();
164     EXPECT_TRUE(lock.try_lock_shared(tokens.back()));
165   }
166   for (auto& token : tokens) {
167     lock.unlock_shared(token);
168   }
169   EXPECT_TRUE(lock.try_lock());
170   lock.unlock();
171 }
172
173 TEST(SharedMutex, many_read_locks_with_tokens) {
174   runManyReadLocksTestWithTokens<SharedMutexReadPriority>();
175   runManyReadLocksTestWithTokens<SharedMutexWritePriority>();
176 }
177
178 template <typename Lock>
179 void runManyReadLocksTestWithoutTokens() {
180   Lock lock;
181
182   for (int i = 0; i < 1000; ++i) {
183     EXPECT_TRUE(lock.try_lock_shared());
184   }
185   for (int i = 0; i < 1000; ++i) {
186     lock.unlock_shared();
187   }
188   EXPECT_TRUE(lock.try_lock());
189   lock.unlock();
190 }
191
192 TEST(SharedMutex, many_read_locks_without_tokens) {
193   runManyReadLocksTestWithoutTokens<SharedMutexReadPriority>();
194   runManyReadLocksTestWithoutTokens<SharedMutexWritePriority>();
195 }
196
197 template <typename Lock>
198 void runTimeoutInPastTest() {
199   Lock lock;
200
201   EXPECT_TRUE(lock.try_lock_for(milliseconds(0)));
202   lock.unlock();
203   EXPECT_TRUE(lock.try_lock_for(milliseconds(-1)));
204   lock.unlock();
205   EXPECT_TRUE(lock.try_lock_shared_for(milliseconds(0)));
206   lock.unlock_shared();
207   EXPECT_TRUE(lock.try_lock_shared_for(milliseconds(-1)));
208   lock.unlock_shared();
209   EXPECT_TRUE(lock.try_lock_until(system_clock::now() - milliseconds(1)));
210   lock.unlock();
211   EXPECT_TRUE(
212       lock.try_lock_shared_until(system_clock::now() - milliseconds(1)));
213   lock.unlock_shared();
214   EXPECT_TRUE(lock.try_lock_until(steady_clock::now() - milliseconds(1)));
215   lock.unlock();
216   EXPECT_TRUE(
217       lock.try_lock_shared_until(steady_clock::now() - milliseconds(1)));
218   lock.unlock_shared();
219 }
220
221 TEST(SharedMutex, timeout_in_past) {
222   runTimeoutInPastTest<SharedMutexReadPriority>();
223   runTimeoutInPastTest<SharedMutexWritePriority>();
224 }
225
226 template <class Func>
227 bool funcHasDuration(milliseconds expectedDuration, Func func) {
228   // elapsed time should eventually fall within expectedDuration +- 25%
229   for (int tries = 0; tries < 100; ++tries) {
230     auto start = steady_clock::now();
231     func();
232     auto elapsed = steady_clock::now() - start;
233     if (elapsed > expectedDuration - expectedDuration / 4 &&
234         elapsed < expectedDuration + expectedDuration / 4) {
235       return true;
236     }
237   }
238   return false;
239 }
240
241 template <typename Lock>
242 void runFailingTryTimeoutTest() {
243   Lock lock;
244   lock.lock();
245   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
246     EXPECT_FALSE(lock.try_lock_for(milliseconds(10)));
247   }));
248   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
249     typename Lock::Token token;
250     EXPECT_FALSE(lock.try_lock_shared_for(milliseconds(10), token));
251   }));
252   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
253     EXPECT_FALSE(lock.try_lock_upgrade_for(milliseconds(10)));
254   }));
255   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
256     EXPECT_FALSE(lock.try_lock_until(steady_clock::now() + milliseconds(10)));
257   }));
258   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
259     typename Lock::Token token;
260     EXPECT_FALSE(lock.try_lock_shared_until(
261         steady_clock::now() + milliseconds(10), token));
262   }));
263   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
264     EXPECT_FALSE(
265         lock.try_lock_upgrade_until(steady_clock::now() + milliseconds(10)));
266   }));
267   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
268     EXPECT_FALSE(lock.try_lock_until(system_clock::now() + milliseconds(10)));
269   }));
270   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
271     typename Lock::Token token;
272     EXPECT_FALSE(lock.try_lock_shared_until(
273         system_clock::now() + milliseconds(10), token));
274   }));
275   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
276     EXPECT_FALSE(
277         lock.try_lock_upgrade_until(system_clock::now() + milliseconds(10)));
278   }));
279   lock.unlock();
280
281   lock.lock_shared();
282   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
283     EXPECT_FALSE(lock.try_lock_for(milliseconds(10)));
284   }));
285   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
286     EXPECT_FALSE(lock.try_lock_until(steady_clock::now() + milliseconds(10)));
287   }));
288   EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
289     EXPECT_FALSE(lock.try_lock_until(system_clock::now() + milliseconds(10)));
290   }));
291   lock.unlock_shared();
292
293   lock.lock();
294   for (int p = 0; p < 8; ++p) {
295     EXPECT_FALSE(lock.try_lock_for(nanoseconds(1 << p)));
296   }
297   lock.unlock();
298
299   for (int p = 0; p < 8; ++p) {
300     typename Lock::ReadHolder holder1(lock);
301     typename Lock::ReadHolder holder2(lock);
302     typename Lock::ReadHolder holder3(lock);
303     EXPECT_FALSE(lock.try_lock_for(nanoseconds(1 << p)));
304   }
305 }
306
307 TEST(SharedMutex, failing_try_timeout) {
308   runFailingTryTimeoutTest<SharedMutexReadPriority>();
309   runFailingTryTimeoutTest<SharedMutexWritePriority>();
310 }
311
312 template <typename Lock>
313 void runBasicUpgradeTest() {
314   Lock lock;
315   typename Lock::Token token1;
316   typename Lock::Token token2;
317
318   lock.lock_upgrade();
319   EXPECT_FALSE(lock.try_lock());
320   EXPECT_TRUE(lock.try_lock_shared(token1));
321   lock.unlock_shared(token1);
322   lock.unlock_upgrade();
323
324   lock.lock_upgrade();
325   lock.unlock_upgrade_and_lock();
326   EXPECT_FALSE(lock.try_lock_shared(token1));
327   lock.unlock();
328
329   lock.lock_upgrade();
330   lock.unlock_upgrade_and_lock_shared(token1);
331   lock.lock_upgrade();
332   lock.unlock_upgrade_and_lock_shared(token2);
333   lock.unlock_shared(token1);
334   lock.unlock_shared(token2);
335
336   lock.lock();
337   lock.unlock_and_lock_upgrade();
338   EXPECT_TRUE(lock.try_lock_shared(token1));
339   lock.unlock_upgrade();
340   lock.unlock_shared(token1);
341 }
342
343 TEST(SharedMutex, basic_upgrade_tests) {
344   runBasicUpgradeTest<SharedMutexReadPriority>();
345   runBasicUpgradeTest<SharedMutexWritePriority>();
346 }
347
348 TEST(SharedMutex, read_has_prio) {
349   SharedMutexReadPriority lock;
350   SharedMutexToken token1;
351   SharedMutexToken token2;
352   lock.lock_shared(token1);
353   bool exclusiveAcquired = false;
354   auto writer = thread([&] {
355     lock.lock();
356     exclusiveAcquired = true;
357     lock.unlock();
358   });
359
360   // lock() can't complete until we unlock token1, but it should stake
361   // its claim with regards to other exclusive or upgrade locks.  We can
362   // use try_lock_upgrade to poll for that eventuality.
363   while (lock.try_lock_upgrade()) {
364     lock.unlock_upgrade();
365     this_thread::yield();
366   }
367   EXPECT_FALSE(exclusiveAcquired);
368
369   // Even though lock() is stuck we should be able to get token2
370   EXPECT_TRUE(lock.try_lock_shared(token2));
371   lock.unlock_shared(token1);
372   lock.unlock_shared(token2);
373   writer.join();
374   EXPECT_TRUE(exclusiveAcquired);
375 }
376
377 TEST(SharedMutex, write_has_prio) {
378   SharedMutexWritePriority lock;
379   SharedMutexToken token1;
380   SharedMutexToken token2;
381   lock.lock_shared(token1);
382   auto writer = thread([&] {
383     lock.lock();
384     lock.unlock();
385   });
386
387   // eventually lock() should block readers
388   while (lock.try_lock_shared(token2)) {
389     lock.unlock_shared(token2);
390     this_thread::yield();
391   }
392
393   lock.unlock_shared(token1);
394   writer.join();
395 }
396
397 struct TokenLocker {
398   SharedMutexToken token;
399
400   template <typename T>
401   void lock(T* lock) {
402     lock->lock();
403   }
404
405   template <typename T>
406   void unlock(T* lock) {
407     lock->unlock();
408   }
409
410   template <typename T>
411   void lock_shared(T* lock) {
412     lock->lock_shared(token);
413   }
414
415   template <typename T>
416   void unlock_shared(T* lock) {
417     lock->unlock_shared(token);
418   }
419 };
420
421 struct Locker {
422   template <typename T>
423   void lock(T* lock) {
424     lock->lock();
425   }
426
427   template <typename T>
428   void unlock(T* lock) {
429     lock->unlock();
430   }
431
432   template <typename T>
433   void lock_shared(T* lock) {
434     lock->lock_shared();
435   }
436
437   template <typename T>
438   void unlock_shared(T* lock) {
439     lock->unlock_shared();
440   }
441 };
442
443 struct EnterLocker {
444   template <typename T>
445   void lock(T* lock) {
446     lock->lock(0);
447   }
448
449   template <typename T>
450   void unlock(T* lock) {
451     lock->unlock();
452   }
453
454   template <typename T>
455   void lock_shared(T* lock) {
456     lock->enter(0);
457   }
458
459   template <typename T>
460   void unlock_shared(T* lock) {
461     lock->leave();
462   }
463 };
464
465 struct PosixRWLock {
466   pthread_rwlock_t lock_;
467
468   PosixRWLock() { pthread_rwlock_init(&lock_, nullptr); }
469
470   ~PosixRWLock() { pthread_rwlock_destroy(&lock_); }
471
472   void lock() { pthread_rwlock_wrlock(&lock_); }
473
474   void unlock() { pthread_rwlock_unlock(&lock_); }
475
476   void lock_shared() { pthread_rwlock_rdlock(&lock_); }
477
478   void unlock_shared() { pthread_rwlock_unlock(&lock_); }
479 };
480
481 struct PosixMutex {
482   pthread_mutex_t lock_;
483
484   PosixMutex() { pthread_mutex_init(&lock_, nullptr); }
485
486   ~PosixMutex() { pthread_mutex_destroy(&lock_); }
487
488   void lock() { pthread_mutex_lock(&lock_); }
489
490   void unlock() { pthread_mutex_unlock(&lock_); }
491
492   void lock_shared() { pthread_mutex_lock(&lock_); }
493
494   void unlock_shared() { pthread_mutex_unlock(&lock_); }
495 };
496
497 template <template <typename> class Atom, typename Lock, typename Locker>
498 static void runContendedReaders(size_t numOps,
499                                 size_t numThreads,
500                                 bool useSeparateLocks) {
501   char padding1[64];
502   (void)padding1;
503   Lock globalLock;
504   int valueProtectedByLock = 10;
505   char padding2[64];
506   (void)padding2;
507   Atom<bool> go(false);
508   Atom<bool>* goPtr = &go; // workaround for clang bug
509   vector<thread> threads(numThreads);
510
511   BENCHMARK_SUSPEND {
512     for (size_t t = 0; t < numThreads; ++t) {
513       threads[t] = DSched::thread([&, t, numThreads] {
514         Lock privateLock;
515         Lock* lock = useSeparateLocks ? &privateLock : &globalLock;
516         Locker locker;
517         while (!goPtr->load()) {
518           this_thread::yield();
519         }
520         for (size_t op = t; op < numOps; op += numThreads) {
521           locker.lock_shared(lock);
522           // note: folly::doNotOptimizeAway reads and writes to its arg,
523           // so the following two lines are very different than a call
524           // to folly::doNotOptimizeAway(valueProtectedByLock);
525           auto copy = valueProtectedByLock;
526           folly::doNotOptimizeAway(copy);
527           locker.unlock_shared(lock);
528         }
529       });
530     }
531   }
532
533   go.store(true);
534   for (auto& thr : threads) {
535     DSched::join(thr);
536   }
537 }
538
539 static void folly_rwspin_reads(uint numOps,
540                                size_t numThreads,
541                                bool useSeparateLocks) {
542   runContendedReaders<atomic, RWSpinLock, Locker>(
543       numOps, numThreads, useSeparateLocks);
544 }
545
546 static void shmtx_wr_pri_reads(uint numOps,
547                                size_t numThreads,
548                                bool useSeparateLocks) {
549   runContendedReaders<atomic, SharedMutexWritePriority, TokenLocker>(
550       numOps, numThreads, useSeparateLocks);
551 }
552
553 static void shmtx_w_bare_reads(uint numOps,
554                                size_t numThreads,
555                                bool useSeparateLocks) {
556   runContendedReaders<atomic, SharedMutexWritePriority, Locker>(
557       numOps, numThreads, useSeparateLocks);
558 }
559
560 static void shmtx_rd_pri_reads(uint numOps,
561                                size_t numThreads,
562                                bool useSeparateLocks) {
563   runContendedReaders<atomic, SharedMutexReadPriority, TokenLocker>(
564       numOps, numThreads, useSeparateLocks);
565 }
566
567 static void shmtx_r_bare_reads(uint numOps,
568                                size_t numThreads,
569                                bool useSeparateLocks) {
570   runContendedReaders<atomic, SharedMutexReadPriority, Locker>(
571       numOps, numThreads, useSeparateLocks);
572 }
573
574 static void folly_ticket_reads(uint numOps,
575                                size_t numThreads,
576                                bool useSeparateLocks) {
577   runContendedReaders<atomic, RWTicketSpinLock64, Locker>(
578       numOps, numThreads, useSeparateLocks);
579 }
580
581 static void boost_shared_reads(uint numOps,
582                                size_t numThreads,
583                                bool useSeparateLocks) {
584   runContendedReaders<atomic, boost::shared_mutex, Locker>(
585       numOps, numThreads, useSeparateLocks);
586 }
587
588 static void pthrd_rwlock_reads(uint numOps,
589                                size_t numThreads,
590                                bool useSeparateLocks) {
591   runContendedReaders<atomic, PosixRWLock, Locker>(
592       numOps, numThreads, useSeparateLocks);
593 }
594
595 template <template <typename> class Atom, typename Lock, typename Locker>
596 static void runMixed(size_t numOps,
597                      size_t numThreads,
598                      double writeFraction,
599                      bool useSeparateLocks) {
600   char padding1[64];
601   (void)padding1;
602   Lock globalLock;
603   int valueProtectedByLock = 0;
604   char padding2[64];
605   (void)padding2;
606   Atom<bool> go(false);
607   Atom<bool>* goPtr = &go; // workaround for clang bug
608   vector<thread> threads(numThreads);
609
610   BENCHMARK_SUSPEND {
611     for (size_t t = 0; t < numThreads; ++t) {
612       threads[t] = DSched::thread([&, t, numThreads] {
613         struct drand48_data buffer;
614         srand48_r(t, &buffer);
615         long writeThreshold = writeFraction * 0x7fffffff;
616         Lock privateLock;
617         Lock* lock = useSeparateLocks ? &privateLock : &globalLock;
618         Locker locker;
619         while (!goPtr->load()) {
620           this_thread::yield();
621         }
622         for (size_t op = t; op < numOps; op += numThreads) {
623           long randVal;
624           lrand48_r(&buffer, &randVal);
625           bool writeOp = randVal < writeThreshold;
626           if (writeOp) {
627             locker.lock(lock);
628             if (!useSeparateLocks) {
629               ++valueProtectedByLock;
630             }
631             locker.unlock(lock);
632           } else {
633             locker.lock_shared(lock);
634             auto v = valueProtectedByLock;
635             folly::doNotOptimizeAway(v);
636             locker.unlock_shared(lock);
637           }
638         }
639       });
640     }
641   }
642
643   go.store(true);
644   for (auto& thr : threads) {
645     DSched::join(thr);
646   }
647 }
648
649 static void folly_rwspin(size_t numOps,
650                          size_t numThreads,
651                          double writeFraction,
652                          bool useSeparateLocks) {
653   runMixed<atomic, RWSpinLock, Locker>(
654       numOps, numThreads, writeFraction, useSeparateLocks);
655 }
656
657 static void shmtx_wr_pri(uint numOps,
658                          size_t numThreads,
659                          double writeFraction,
660                          bool useSeparateLocks) {
661   runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
662       numOps, numThreads, writeFraction, useSeparateLocks);
663 }
664
665 static void shmtx_w_bare(uint numOps,
666                          size_t numThreads,
667                          double writeFraction,
668                          bool useSeparateLocks) {
669   runMixed<atomic, SharedMutexWritePriority, Locker>(
670       numOps, numThreads, writeFraction, useSeparateLocks);
671 }
672
673 static void shmtx_rd_pri(uint numOps,
674                          size_t numThreads,
675                          double writeFraction,
676                          bool useSeparateLocks) {
677   runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
678       numOps, numThreads, writeFraction, useSeparateLocks);
679 }
680
681 static void shmtx_r_bare(uint numOps,
682                          size_t numThreads,
683                          double writeFraction,
684                          bool useSeparateLocks) {
685   runMixed<atomic, SharedMutexReadPriority, Locker>(
686       numOps, numThreads, writeFraction, useSeparateLocks);
687 }
688
689 static void folly_ticket(size_t numOps,
690                          size_t numThreads,
691                          double writeFraction,
692                          bool useSeparateLocks) {
693   runMixed<atomic, RWTicketSpinLock64, Locker>(
694       numOps, numThreads, writeFraction, useSeparateLocks);
695 }
696
697 static void boost_shared(size_t numOps,
698                          size_t numThreads,
699                          double writeFraction,
700                          bool useSeparateLocks) {
701   runMixed<atomic, boost::shared_mutex, Locker>(
702       numOps, numThreads, writeFraction, useSeparateLocks);
703 }
704
705 static void pthrd_rwlock(size_t numOps,
706                          size_t numThreads,
707                          double writeFraction,
708                          bool useSeparateLocks) {
709   runMixed<atomic, PosixRWLock, Locker>(
710       numOps, numThreads, writeFraction, useSeparateLocks);
711 }
712
713 static void pthrd_mutex_(size_t numOps,
714                          size_t numThreads,
715                          double writeFraction,
716                          bool useSeparateLocks) {
717   runMixed<atomic, PosixMutex, Locker>(
718       numOps, numThreads, writeFraction, useSeparateLocks);
719 }
720
721 template <typename Lock, template <typename> class Atom>
722 static void runAllAndValidate(size_t numOps, size_t numThreads) {
723   Lock globalLock;
724   Atom<int> globalExclusiveCount(0);
725   Atom<int> globalUpgradeCount(0);
726   Atom<int> globalSharedCount(0);
727
728   Atom<bool> go(false);
729
730   // clang crashes on access to Atom<> captured by ref in closure
731   Atom<int>* globalExclusiveCountPtr = &globalExclusiveCount;
732   Atom<int>* globalUpgradeCountPtr = &globalUpgradeCount;
733   Atom<int>* globalSharedCountPtr = &globalSharedCount;
734   Atom<bool>* goPtr = &go;
735
736   vector<thread> threads(numThreads);
737
738   BENCHMARK_SUSPEND {
739     for (size_t t = 0; t < numThreads; ++t) {
740       threads[t] = DSched::thread([&, t, numThreads] {
741         struct drand48_data buffer;
742         srand48_r(t, &buffer);
743
744         bool exclusive = false;
745         bool upgrade = false;
746         bool shared = false;
747         bool ourGlobalTokenUsed = false;
748         SharedMutexToken ourGlobalToken;
749
750         Lock privateLock;
751         vector<SharedMutexToken> privateTokens;
752
753         while (!goPtr->load()) {
754           this_thread::yield();
755         }
756         for (size_t op = t; op < numOps; op += numThreads) {
757           // randVal in [0,1000)
758           long randVal;
759           lrand48_r(&buffer, &randVal);
760           randVal = (long)((randVal * (uint64_t)1000) / 0x7fffffff);
761
762           // make as many assertions as possible about the global state
763           if (exclusive) {
764             EXPECT_EQ(1, globalExclusiveCountPtr->load(memory_order_acquire));
765             EXPECT_EQ(0, globalUpgradeCountPtr->load(memory_order_acquire));
766             EXPECT_EQ(0, globalSharedCountPtr->load(memory_order_acquire));
767           }
768           if (upgrade) {
769             EXPECT_EQ(0, globalExclusiveCountPtr->load(memory_order_acquire));
770             EXPECT_EQ(1, globalUpgradeCountPtr->load(memory_order_acquire));
771           }
772           if (shared) {
773             EXPECT_EQ(0, globalExclusiveCountPtr->load(memory_order_acquire));
774             EXPECT_TRUE(globalSharedCountPtr->load(memory_order_acquire) > 0);
775           } else {
776             EXPECT_FALSE(ourGlobalTokenUsed);
777           }
778
779           // independent 20% chance we do something to the private lock
780           if (randVal < 200) {
781             // it's okay to take multiple private shared locks because
782             // we never take an exclusive lock, so reader versus writer
783             // priority doesn't cause deadlocks
784             if (randVal < 100 && privateTokens.size() > 0) {
785               auto i = randVal % privateTokens.size();
786               privateLock.unlock_shared(privateTokens[i]);
787               privateTokens.erase(privateTokens.begin() + i);
788             } else {
789               SharedMutexToken token;
790               privateLock.lock_shared(token);
791               privateTokens.push_back(token);
792             }
793             continue;
794           }
795
796           // if we've got a lock, the only thing we can do is release it
797           // or transform it into a different kind of lock
798           if (exclusive) {
799             exclusive = false;
800             --*globalExclusiveCountPtr;
801             if (randVal < 500) {
802               globalLock.unlock();
803             } else if (randVal < 700) {
804               globalLock.unlock_and_lock_shared();
805               ++*globalSharedCountPtr;
806               shared = true;
807             } else if (randVal < 900) {
808               globalLock.unlock_and_lock_shared(ourGlobalToken);
809               ++*globalSharedCountPtr;
810               shared = true;
811               ourGlobalTokenUsed = true;
812             } else {
813               globalLock.unlock_and_lock_upgrade();
814               ++*globalUpgradeCountPtr;
815               upgrade = true;
816             }
817           } else if (upgrade) {
818             upgrade = false;
819             --*globalUpgradeCountPtr;
820             if (randVal < 500) {
821               globalLock.unlock_upgrade();
822             } else if (randVal < 700) {
823               globalLock.unlock_upgrade_and_lock_shared();
824               ++*globalSharedCountPtr;
825               shared = true;
826             } else if (randVal < 900) {
827               globalLock.unlock_upgrade_and_lock_shared(ourGlobalToken);
828               ++*globalSharedCountPtr;
829               shared = true;
830               ourGlobalTokenUsed = true;
831             } else {
832               globalLock.unlock_upgrade_and_lock();
833               ++*globalExclusiveCountPtr;
834               exclusive = true;
835             }
836           } else if (shared) {
837             shared = false;
838             --*globalSharedCountPtr;
839             if (ourGlobalTokenUsed) {
840               globalLock.unlock_shared(ourGlobalToken);
841               ourGlobalTokenUsed = false;
842             } else {
843               globalLock.unlock_shared();
844             }
845           } else if (randVal < 400) {
846             // 40% chance of shared lock with token, 5 ways to get it
847
848             // delta t goes from -1 millis to 7 millis
849             auto dt = microseconds(10 * (randVal - 100));
850
851             if (randVal < 400) {
852               globalLock.lock_shared(ourGlobalToken);
853               shared = true;
854             } else if (randVal < 500) {
855               shared = globalLock.try_lock_shared(ourGlobalToken);
856             } else if (randVal < 600) {
857               shared = globalLock.try_lock_shared_for(dt, ourGlobalToken);
858             } else if (randVal < 800) {
859               shared = globalLock.try_lock_shared_until(
860                   system_clock::now() + dt, ourGlobalToken);
861             }
862             if (shared) {
863               ourGlobalTokenUsed = true;
864               ++*globalSharedCountPtr;
865             }
866           } else if (randVal < 800) {
867             // 40% chance of shared lock without token
868             auto dt = microseconds(10 * (randVal - 100));
869             if (randVal < 400) {
870               globalLock.lock_shared();
871               shared = true;
872             } else if (randVal < 500) {
873               shared = globalLock.try_lock_shared();
874             } else if (randVal < 600) {
875               shared = globalLock.try_lock_shared_for(dt);
876             } else if (randVal < 800) {
877               shared = globalLock.try_lock_shared_until(
878                   system_clock::now() + dt);
879             }
880             if (shared) {
881               ++*globalSharedCountPtr;
882             }
883           } else if (randVal < 900) {
884             // 10% change of upgrade lock
885             globalLock.lock_upgrade();
886             upgrade = true;
887             ++*globalUpgradeCountPtr;
888           } else {
889             // 10% chance of exclusive lock, 5 ways to get it
890
891             // delta t goes from -1 millis to 9 millis
892             auto dt = microseconds(100 * (randVal - 910));
893
894             if (randVal < 400) {
895               globalLock.lock();
896               exclusive = true;
897             } else if (randVal < 500) {
898               exclusive = globalLock.try_lock();
899             } else if (randVal < 600) {
900               exclusive = globalLock.try_lock_for(dt);
901             } else if (randVal < 700) {
902               exclusive = globalLock.try_lock_until(steady_clock::now() + dt);
903             } else {
904               exclusive = globalLock.try_lock_until(system_clock::now() + dt);
905             }
906             if (exclusive) {
907               ++*globalExclusiveCountPtr;
908             }
909           }
910         }
911
912         if (exclusive) {
913           --*globalExclusiveCountPtr;
914           globalLock.unlock();
915         }
916         if (upgrade) {
917           --*globalUpgradeCountPtr;
918           globalLock.unlock_upgrade();
919         }
920         if (shared) {
921           --*globalSharedCountPtr;
922           if (ourGlobalTokenUsed) {
923             globalLock.unlock_shared(ourGlobalToken);
924             ourGlobalTokenUsed = false;
925           } else {
926             globalLock.unlock_shared();
927           }
928         }
929         for (auto& token : privateTokens) {
930           privateLock.unlock_shared(token);
931         }
932       });
933     }
934   }
935
936   go.store(true);
937   for (auto& thr : threads) {
938     DSched::join(thr);
939   }
940 }
941
942 TEST(SharedMutex, deterministic_concurrent_readers_of_one_lock_read_prio) {
943   for (int pass = 0; pass < 3; ++pass) {
944     DSched sched(DSched::uniform(pass));
945     runContendedReaders<DeterministicAtomic,
946                         DSharedMutexReadPriority,
947                         Locker>(1000, 3, false);
948   }
949 }
950
951 TEST(SharedMutex, deterministic_concurrent_readers_of_one_lock_write_prio) {
952   for (int pass = 0; pass < 3; ++pass) {
953     DSched sched(DSched::uniform(pass));
954     runContendedReaders<DeterministicAtomic,
955                         DSharedMutexWritePriority,
956                         Locker>(1000, 3, false);
957   }
958 }
959
960 TEST(SharedMutex, concurrent_readers_of_one_lock_read_prio) {
961   for (int pass = 0; pass < 10; ++pass) {
962     runContendedReaders<atomic, SharedMutexReadPriority, Locker>(
963         100000, 32, false);
964   }
965 }
966
967 TEST(SharedMutex, concurrent_readers_of_one_lock_write_prio) {
968   for (int pass = 0; pass < 10; ++pass) {
969     runContendedReaders<atomic, SharedMutexWritePriority, Locker>(
970         100000, 32, false);
971   }
972 }
973
974 TEST(SharedMutex, deterministic_readers_of_concurrent_locks_read_prio) {
975   for (int pass = 0; pass < 3; ++pass) {
976     DSched sched(DSched::uniform(pass));
977     runContendedReaders<DeterministicAtomic,
978                         DSharedMutexReadPriority,
979                         Locker>(1000, 3, true);
980   }
981 }
982
983 TEST(SharedMutex, deterministic_readers_of_concurrent_locks_write_prio) {
984   for (int pass = 0; pass < 3; ++pass) {
985     DSched sched(DSched::uniform(pass));
986     runContendedReaders<DeterministicAtomic,
987                         DSharedMutexWritePriority,
988                         Locker>(1000, 3, true);
989   }
990 }
991
992 TEST(SharedMutex, readers_of_concurrent_locks_read_prio) {
993   for (int pass = 0; pass < 10; ++pass) {
994     runContendedReaders<atomic, SharedMutexReadPriority, TokenLocker>(
995         100000, 32, true);
996   }
997 }
998
999 TEST(SharedMutex, readers_of_concurrent_locks_write_prio) {
1000   for (int pass = 0; pass < 10; ++pass) {
1001     runContendedReaders<atomic, SharedMutexWritePriority, TokenLocker>(
1002         100000, 32, true);
1003   }
1004 }
1005
1006 TEST(SharedMutex, deterministic_mixed_mostly_read_read_prio) {
1007   for (int pass = 0; pass < 3; ++pass) {
1008     DSched sched(DSched::uniform(pass));
1009     runMixed<DeterministicAtomic, DSharedMutexReadPriority, Locker>(
1010         1000, 3, 0.1, false);
1011   }
1012 }
1013
1014 TEST(SharedMutex, deterministic_mixed_mostly_read_write_prio) {
1015   for (int pass = 0; pass < 3; ++pass) {
1016     DSched sched(DSched::uniform(pass));
1017     runMixed<DeterministicAtomic, DSharedMutexWritePriority, Locker>(
1018         1000, 3, 0.1, false);
1019   }
1020 }
1021
1022 TEST(SharedMutex, mixed_mostly_read_read_prio) {
1023   for (int pass = 0; pass < 5; ++pass) {
1024     runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
1025         10000, 32, 0.1, false);
1026   }
1027 }
1028
1029 TEST(SharedMutex, mixed_mostly_read_write_prio) {
1030   for (int pass = 0; pass < 5; ++pass) {
1031     runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
1032         10000, 32, 0.1, false);
1033   }
1034 }
1035
1036 TEST(SharedMutex, deterministic_mixed_mostly_write_read_prio) {
1037   for (int pass = 0; pass < 1; ++pass) {
1038     DSched sched(DSched::uniform(pass));
1039     runMixed<DeterministicAtomic, DSharedMutexReadPriority, TokenLocker>(
1040         1000, 10, 0.9, false);
1041   }
1042 }
1043
1044 TEST(SharedMutex, deterministic_mixed_mostly_write_write_prio) {
1045   for (int pass = 0; pass < 1; ++pass) {
1046     DSched sched(DSched::uniform(pass));
1047     runMixed<DeterministicAtomic, DSharedMutexWritePriority, TokenLocker>(
1048         1000, 10, 0.9, false);
1049   }
1050 }
1051
1052 TEST(SharedMutex, deterministic_lost_wakeup_write_prio) {
1053   for (int pass = 0; pass < 10; ++pass) {
1054     DSched sched(DSched::uniformSubset(pass, 2, 200));
1055     runMixed<DeterministicAtomic, DSharedMutexWritePriority, TokenLocker>(
1056         1000, 3, 1.0, false);
1057   }
1058 }
1059
1060 TEST(SharedMutex, mixed_mostly_write_read_prio) {
1061   for (int pass = 0; pass < 5; ++pass) {
1062     runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
1063         50000, 300, 0.9, false);
1064   }
1065 }
1066
1067 TEST(SharedMutex, mixed_mostly_write_write_prio) {
1068   for (int pass = 0; pass < 5; ++pass) {
1069     runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
1070         50000, 300, 0.9, false);
1071   }
1072 }
1073
1074 TEST(SharedMutex, deterministic_all_ops_read_prio) {
1075   for (int pass = 0; pass < 5; ++pass) {
1076     DSched sched(DSched::uniform(pass));
1077     runAllAndValidate<DSharedMutexReadPriority, DeterministicAtomic>(1000, 8);
1078   }
1079 }
1080
1081 TEST(SharedMutex, deterministic_all_ops_write_prio) {
1082   for (int pass = 0; pass < 5; ++pass) {
1083     DSched sched(DSched::uniform(pass));
1084     runAllAndValidate<DSharedMutexWritePriority, DeterministicAtomic>(1000, 8);
1085   }
1086 }
1087
1088 TEST(SharedMutex, all_ops_read_prio) {
1089   for (int pass = 0; pass < 5; ++pass) {
1090     runAllAndValidate<SharedMutexReadPriority, atomic>(100000, 32);
1091   }
1092 }
1093
1094 TEST(SharedMutex, all_ops_write_prio) {
1095   for (int pass = 0; pass < 5; ++pass) {
1096     runAllAndValidate<SharedMutexWritePriority, atomic>(100000, 32);
1097   }
1098 }
1099
1100 FOLLY_ASSUME_FBVECTOR_COMPATIBLE(
1101     boost::optional<boost::optional<SharedMutexToken>>)
1102
1103 // Setup is a set of threads that either grab a shared lock, or exclusive
1104 // and then downgrade it, or upgrade then upgrade and downgrade, then
1105 // enqueue the shared lock to a second set of threads that just performs
1106 // unlocks.  Half of the shared locks use tokens, the others don't.
1107 template <typename Lock, template <typename> class Atom>
1108 static void runRemoteUnlock(size_t numOps,
1109                             double preWriteFraction,
1110                             double preUpgradeFraction,
1111                             size_t numSendingThreads,
1112                             size_t numReceivingThreads) {
1113   Lock globalLock;
1114   MPMCQueue<boost::optional<boost::optional<SharedMutexToken>>, Atom>
1115     queue(10);
1116   auto queuePtr = &queue; // workaround for clang crash
1117
1118   Atom<bool> go(false);
1119   auto goPtr = &go; // workaround for clang crash
1120   Atom<int> pendingSenders(numSendingThreads);
1121   auto pendingSendersPtr = &pendingSenders; // workaround for clang crash
1122   vector<thread> threads(numSendingThreads + numReceivingThreads);
1123
1124   BENCHMARK_SUSPEND {
1125     for (size_t t = 0; t < threads.size(); ++t) {
1126       threads[t] = DSched::thread([&, t, numSendingThreads] {
1127         if (t >= numSendingThreads) {
1128           // we're a receiver
1129           typename decltype(queue)::value_type elem;
1130           while (true) {
1131             queuePtr->blockingRead(elem);
1132             if (!elem) {
1133               // EOF, pass the EOF token
1134               queuePtr->blockingWrite(std::move(elem));
1135               break;
1136             }
1137             if (*elem) {
1138               globalLock.unlock_shared(**elem);
1139             } else {
1140               globalLock.unlock_shared();
1141             }
1142           }
1143           return;
1144         }
1145         // else we're a sender
1146
1147         struct drand48_data buffer;
1148         srand48_r(t, &buffer);
1149
1150         while (!goPtr->load()) {
1151           this_thread::yield();
1152         }
1153         for (size_t op = t; op < numOps; op += numSendingThreads) {
1154           long unscaledRandVal;
1155           lrand48_r(&buffer, &unscaledRandVal);
1156
1157           // randVal in [0,1]
1158           double randVal = ((double)unscaledRandVal) / 0x7fffffff;
1159
1160           // extract a bit and rescale
1161           bool useToken = randVal >= 0.5;
1162           randVal = (randVal - (useToken ? 0.5 : 0.0)) * 2;
1163
1164           boost::optional<SharedMutexToken> maybeToken;
1165
1166           if (useToken) {
1167             SharedMutexToken token;
1168             if (randVal < preWriteFraction) {
1169               globalLock.lock();
1170               globalLock.unlock_and_lock_shared(token);
1171             } else if (randVal < preWriteFraction + preUpgradeFraction / 2) {
1172               globalLock.lock_upgrade();
1173               globalLock.unlock_upgrade_and_lock_shared(token);
1174             } else if (randVal < preWriteFraction + preUpgradeFraction) {
1175               globalLock.lock_upgrade();
1176               globalLock.unlock_upgrade_and_lock();
1177               globalLock.unlock_and_lock_shared(token);
1178             } else {
1179               globalLock.lock_shared(token);
1180             }
1181             maybeToken = token;
1182           } else {
1183             if (randVal < preWriteFraction) {
1184               globalLock.lock();
1185               globalLock.unlock_and_lock_shared();
1186             } else if (randVal < preWriteFraction + preUpgradeFraction / 2) {
1187               globalLock.lock_upgrade();
1188               globalLock.unlock_upgrade_and_lock_shared();
1189             } else if (randVal < preWriteFraction + preUpgradeFraction) {
1190               globalLock.lock_upgrade();
1191               globalLock.unlock_upgrade_and_lock();
1192               globalLock.unlock_and_lock_shared();
1193             } else {
1194               globalLock.lock_shared();
1195             }
1196           }
1197
1198           // blockingWrite is emplace-like, so this automatically adds
1199           // another level of wrapping
1200           queuePtr->blockingWrite(maybeToken);
1201         }
1202         if (--*pendingSendersPtr == 0) {
1203           queuePtr->blockingWrite(boost::none);
1204         }
1205       });
1206     }
1207   }
1208
1209   go.store(true);
1210   for (auto& thr : threads) {
1211     DSched::join(thr);
1212   }
1213 }
1214
1215 TEST(SharedMutex, deterministic_remote_write_prio) {
1216   for (int pass = 0; pass < 1; ++pass) {
1217     DSched sched(DSched::uniform(pass));
1218     runRemoteUnlock<DSharedMutexWritePriority, DeterministicAtomic>(
1219         500, 0.1, 0.1, 5, 5);
1220   }
1221 }
1222
1223 TEST(SharedMutex, deterministic_remote_read_prio) {
1224   for (int pass = 0; pass < 1; ++pass) {
1225     DSched sched(DSched::uniform(pass));
1226     runRemoteUnlock<DSharedMutexReadPriority, DeterministicAtomic>(
1227         500, 0.1, 0.1, 5, 5);
1228   }
1229 }
1230
1231 TEST(SharedMutex, remote_write_prio) {
1232   for (int pass = 0; pass < 10; ++pass) {
1233     runRemoteUnlock<SharedMutexWritePriority, atomic>(100000, 0.1, 0.1, 5, 5);
1234   }
1235 }
1236
1237 TEST(SharedMutex, remote_read_prio) {
1238   for (int pass = 0; pass < 100; ++pass) {
1239     runRemoteUnlock<SharedMutexReadPriority, atomic>(100000, 0.1, 0.1, 5, 5);
1240   }
1241 }
1242
1243 static void burn(size_t n) {
1244   for (size_t i = 0; i < n; ++i) {
1245     folly::doNotOptimizeAway(i);
1246   }
1247 }
1248
1249 // Two threads and three locks, arranged so that they have to proceed
1250 // in turn with reader/writer conflict
1251 template <typename Lock, template <typename> class Atom = atomic>
1252 static void runPingPong(size_t numRounds, size_t burnCount) {
1253   char padding1[56];
1254   (void)padding1;
1255   pair<Lock, char[56]> locks[3];
1256   char padding2[56];
1257   (void)padding2;
1258
1259   Atom<int> avail(0);
1260   auto availPtr = &avail; // workaround for clang crash
1261   Atom<bool> go(false);
1262   auto goPtr = &go; // workaround for clang crash
1263   vector<thread> threads(2);
1264
1265   locks[0].first.lock();
1266   locks[1].first.lock();
1267   locks[2].first.lock_shared();
1268
1269   BENCHMARK_SUSPEND {
1270     threads[0] = DSched::thread([&] {
1271       ++*availPtr;
1272       while (!goPtr->load()) {
1273         this_thread::yield();
1274       }
1275       for (size_t i = 0; i < numRounds; ++i) {
1276         locks[i % 3].first.unlock();
1277         locks[(i + 2) % 3].first.lock();
1278         burn(burnCount);
1279       }
1280     });
1281     threads[1] = DSched::thread([&] {
1282       ++*availPtr;
1283       while (!goPtr->load()) {
1284         this_thread::yield();
1285       }
1286       for (size_t i = 0; i < numRounds; ++i) {
1287         locks[i % 3].first.lock_shared();
1288         burn(burnCount);
1289         locks[(i + 2) % 3].first.unlock_shared();
1290       }
1291     });
1292
1293     while (avail.load() < 2) {
1294       this_thread::yield();
1295     }
1296   }
1297
1298   go.store(true);
1299   for (auto& thr : threads) {
1300     DSched::join(thr);
1301   }
1302   locks[numRounds % 3].first.unlock();
1303   locks[(numRounds + 1) % 3].first.unlock();
1304   locks[(numRounds + 2) % 3].first.unlock_shared();
1305 }
1306
1307 static void folly_rwspin_ping_pong(size_t n, size_t scale, size_t burnCount) {
1308   runPingPong<RWSpinLock>(n / scale, burnCount);
1309 }
1310
1311 static void shmtx_w_bare_ping_pong(size_t n, size_t scale, size_t burnCount) {
1312   runPingPong<SharedMutexWritePriority>(n / scale, burnCount);
1313 }
1314
1315 static void shmtx_r_bare_ping_pong(size_t n, size_t scale, size_t burnCount) {
1316   runPingPong<SharedMutexReadPriority>(n / scale, burnCount);
1317 }
1318
1319 static void folly_ticket_ping_pong(size_t n, size_t scale, size_t burnCount) {
1320   runPingPong<RWTicketSpinLock64>(n / scale, burnCount);
1321 }
1322
1323 static void boost_shared_ping_pong(size_t n, size_t scale, size_t burnCount) {
1324   runPingPong<boost::shared_mutex>(n / scale, burnCount);
1325 }
1326
1327 static void pthrd_rwlock_ping_pong(size_t n, size_t scale, size_t burnCount) {
1328   runPingPong<PosixRWLock>(n / scale, burnCount);
1329 }
1330
1331 TEST(SharedMutex, deterministic_ping_pong_write_prio) {
1332   for (int pass = 0; pass < 1; ++pass) {
1333     DSched sched(DSched::uniform(pass));
1334     runPingPong<DSharedMutexWritePriority, DeterministicAtomic>(500, 0);
1335   }
1336 }
1337
1338 TEST(SharedMutex, deterministic_ping_pong_read_prio) {
1339   for (int pass = 0; pass < 1; ++pass) {
1340     DSched sched(DSched::uniform(pass));
1341     runPingPong<DSharedMutexReadPriority, DeterministicAtomic>(500, 0);
1342   }
1343 }
1344
1345 TEST(SharedMutex, ping_pong_write_prio) {
1346   for (int pass = 0; pass < 1; ++pass) {
1347     runPingPong<SharedMutexWritePriority, atomic>(50000, 0);
1348   }
1349 }
1350
1351 TEST(SharedMutex, ping_pong_read_prio) {
1352   for (int pass = 0; pass < 1; ++pass) {
1353     runPingPong<SharedMutexReadPriority, atomic>(50000, 0);
1354   }
1355 }
1356
1357 // This is here so you can tell how much of the runtime reported by the
1358 // more complex harnesses is due to the harness, although due to the
1359 // magic of compiler optimization it may also be slower
1360 BENCHMARK(single_thread_lock_shared_unlock_shared, iters) {
1361   SharedMutex lock;
1362   for (size_t n = 0; n < iters; ++n) {
1363     SharedMutex::Token token;
1364     lock.lock_shared(token);
1365     folly::doNotOptimizeAway(0);
1366     lock.unlock_shared(token);
1367   }
1368 }
1369
1370 BENCHMARK(single_thread_lock_unlock, iters) {
1371   SharedMutex lock;
1372   for (size_t n = 0; n < iters; ++n) {
1373     lock.lock();
1374     folly::doNotOptimizeAway(0);
1375     lock.unlock();
1376   }
1377 }
1378
1379 #define BENCH_BASE(args...) BENCHMARK_NAMED_PARAM(args)
1380 #define BENCH_REL(args...) BENCHMARK_RELATIVE_NAMED_PARAM(args)
1381
1382 // 100% reads.  Best-case scenario for deferred locks.  Lock is colocated
1383 // with read data, so inline lock takes cache miss every time but deferred
1384 // lock has only cache hits and local access.
1385 BENCHMARK_DRAW_LINE()
1386 BENCHMARK_DRAW_LINE()
1387 BENCH_BASE(folly_rwspin_reads, 1thread, 1, false)
1388 BENCH_REL (shmtx_wr_pri_reads, 1thread, 1, false)
1389 BENCH_REL (shmtx_w_bare_reads, 1thread, 1, false)
1390 BENCH_REL (shmtx_rd_pri_reads, 1thread, 1, false)
1391 BENCH_REL (shmtx_r_bare_reads, 1thread, 1, false)
1392 BENCH_REL (folly_ticket_reads, 1thread, 1, false)
1393 BENCH_REL (boost_shared_reads, 1thread, 1, false)
1394 BENCH_REL (pthrd_rwlock_reads, 1thread, 1, false)
1395 BENCHMARK_DRAW_LINE()
1396 BENCH_BASE(folly_rwspin_reads, 2thread, 2, false)
1397 BENCH_REL (shmtx_wr_pri_reads, 2thread, 2, false)
1398 BENCH_REL (shmtx_w_bare_reads, 2thread, 2, false)
1399 BENCH_REL (shmtx_rd_pri_reads, 2thread, 2, false)
1400 BENCH_REL (shmtx_r_bare_reads, 2thread, 2, false)
1401 BENCH_REL (folly_ticket_reads, 2thread, 2, false)
1402 BENCH_REL (boost_shared_reads, 2thread, 2, false)
1403 BENCH_REL (pthrd_rwlock_reads, 2thread, 2, false)
1404 BENCHMARK_DRAW_LINE()
1405 BENCH_BASE(folly_rwspin_reads, 4thread, 4, false)
1406 BENCH_REL (shmtx_wr_pri_reads, 4thread, 4, false)
1407 BENCH_REL (shmtx_w_bare_reads, 4thread, 4, false)
1408 BENCH_REL (shmtx_rd_pri_reads, 4thread, 4, false)
1409 BENCH_REL (shmtx_r_bare_reads, 4thread, 4, false)
1410 BENCH_REL (folly_ticket_reads, 4thread, 4, false)
1411 BENCH_REL (boost_shared_reads, 4thread, 4, false)
1412 BENCH_REL (pthrd_rwlock_reads, 4thread, 4, false)
1413 BENCHMARK_DRAW_LINE()
1414 BENCH_BASE(folly_rwspin_reads, 8thread, 8, false)
1415 BENCH_REL (shmtx_wr_pri_reads, 8thread, 8, false)
1416 BENCH_REL (shmtx_w_bare_reads, 8thread, 8, false)
1417 BENCH_REL (shmtx_rd_pri_reads, 8thread, 8, false)
1418 BENCH_REL (shmtx_r_bare_reads, 8thread, 8, false)
1419 BENCH_REL (folly_ticket_reads, 8thread, 8, false)
1420 BENCH_REL (boost_shared_reads, 8thread, 8, false)
1421 BENCH_REL (pthrd_rwlock_reads, 8thread, 8, false)
1422 BENCHMARK_DRAW_LINE()
1423 BENCH_BASE(folly_rwspin_reads, 16thread, 16, false)
1424 BENCH_REL (shmtx_wr_pri_reads, 16thread, 16, false)
1425 BENCH_REL (shmtx_w_bare_reads, 16thread, 16, false)
1426 BENCH_REL (shmtx_rd_pri_reads, 16thread, 16, false)
1427 BENCH_REL (shmtx_r_bare_reads, 16thread, 16, false)
1428 BENCH_REL (folly_ticket_reads, 16thread, 16, false)
1429 BENCH_REL (boost_shared_reads, 16thread, 16, false)
1430 BENCH_REL (pthrd_rwlock_reads, 16thread, 16, false)
1431 BENCHMARK_DRAW_LINE()
1432 BENCH_BASE(folly_rwspin_reads, 32thread, 32, false)
1433 BENCH_REL (shmtx_wr_pri_reads, 32thread, 32, false)
1434 BENCH_REL (shmtx_w_bare_reads, 32thread, 32, false)
1435 BENCH_REL (shmtx_rd_pri_reads, 32thread, 32, false)
1436 BENCH_REL (shmtx_r_bare_reads, 32thread, 32, false)
1437 BENCH_REL (folly_ticket_reads, 32thread, 32, false)
1438 BENCH_REL (boost_shared_reads, 32thread, 32, false)
1439 BENCH_REL (pthrd_rwlock_reads, 32thread, 32, false)
1440 BENCHMARK_DRAW_LINE()
1441 BENCH_BASE(folly_rwspin_reads, 64thread, 64, false)
1442 BENCH_REL (shmtx_wr_pri_reads, 64thread, 64, false)
1443 BENCH_REL (shmtx_w_bare_reads, 64thread, 64, false)
1444 BENCH_REL (shmtx_rd_pri_reads, 64thread, 64, false)
1445 BENCH_REL (shmtx_r_bare_reads, 64thread, 64, false)
1446 BENCH_REL (folly_ticket_reads, 64thread, 64, false)
1447 BENCH_REL (boost_shared_reads, 64thread, 64, false)
1448 BENCH_REL (pthrd_rwlock_reads, 64thread, 64, false)
1449
1450 // 1 lock used by everybody, 100% writes.  Threads only hurt, but it is
1451 // good to not fail catastrophically.  Compare to single_thread_lock_unlock
1452 // to see the overhead of the generic driver (and its pseudo-random number
1453 // generator).  pthrd_mutex_ is a pthread_mutex_t (default, not adaptive),
1454 // which is better than any of the reader-writer locks for this scenario.
1455 BENCHMARK_DRAW_LINE()
1456 BENCHMARK_DRAW_LINE()
1457 BENCH_BASE(folly_rwspin, 1thread_all_write, 1, 1.0, false)
1458 BENCH_REL (shmtx_wr_pri, 1thread_all_write, 1, 1.0, false)
1459 BENCH_REL (shmtx_rd_pri, 1thread_all_write, 1, 1.0, false)
1460 BENCH_REL (folly_ticket, 1thread_all_write, 1, 1.0, false)
1461 BENCH_REL (boost_shared, 1thread_all_write, 1, 1.0, false)
1462 BENCH_REL (pthrd_rwlock, 1thread_all_write, 1, 1.0, false)
1463 BENCH_REL (pthrd_mutex_, 1thread_all_write, 1, 1.0, false)
1464 BENCHMARK_DRAW_LINE()
1465 BENCH_BASE(folly_rwspin, 2thread_all_write, 2, 1.0, false)
1466 BENCH_REL (shmtx_wr_pri, 2thread_all_write, 2, 1.0, false)
1467 BENCH_REL (shmtx_rd_pri, 2thread_all_write, 2, 1.0, false)
1468 BENCH_REL (folly_ticket, 2thread_all_write, 2, 1.0, false)
1469 BENCH_REL (boost_shared, 2thread_all_write, 2, 1.0, false)
1470 BENCH_REL (pthrd_rwlock, 2thread_all_write, 2, 1.0, false)
1471 BENCH_REL (pthrd_mutex_, 2thread_all_write, 2, 1.0, false)
1472 BENCHMARK_DRAW_LINE()
1473 BENCH_BASE(folly_rwspin, 4thread_all_write, 4, 1.0, false)
1474 BENCH_REL (shmtx_wr_pri, 4thread_all_write, 4, 1.0, false)
1475 BENCH_REL (shmtx_rd_pri, 4thread_all_write, 4, 1.0, false)
1476 BENCH_REL (folly_ticket, 4thread_all_write, 4, 1.0, false)
1477 BENCH_REL (boost_shared, 4thread_all_write, 4, 1.0, false)
1478 BENCH_REL (pthrd_rwlock, 4thread_all_write, 4, 1.0, false)
1479 BENCH_REL (pthrd_mutex_, 4thread_all_write, 4, 1.0, false)
1480 BENCHMARK_DRAW_LINE()
1481 BENCH_BASE(folly_rwspin, 8thread_all_write, 8, 1.0, false)
1482 BENCH_REL (shmtx_wr_pri, 8thread_all_write, 8, 1.0, false)
1483 BENCH_REL (shmtx_rd_pri, 8thread_all_write, 8, 1.0, false)
1484 BENCH_REL (folly_ticket, 8thread_all_write, 8, 1.0, false)
1485 BENCH_REL (boost_shared, 8thread_all_write, 8, 1.0, false)
1486 BENCH_REL (pthrd_rwlock, 8thread_all_write, 8, 1.0, false)
1487 BENCH_REL (pthrd_mutex_, 8thread_all_write, 8, 1.0, false)
1488 BENCHMARK_DRAW_LINE()
1489 BENCH_BASE(folly_rwspin, 16thread_all_write, 16, 1.0, false)
1490 BENCH_REL (shmtx_wr_pri, 16thread_all_write, 16, 1.0, false)
1491 BENCH_REL (shmtx_rd_pri, 16thread_all_write, 16, 1.0, false)
1492 BENCH_REL (folly_ticket, 16thread_all_write, 16, 1.0, false)
1493 BENCH_REL (boost_shared, 16thread_all_write, 16, 1.0, false)
1494 BENCH_REL (pthrd_rwlock, 16thread_all_write, 16, 1.0, false)
1495 BENCH_REL (pthrd_mutex_, 16thread_all_write, 16, 1.0, false)
1496 BENCHMARK_DRAW_LINE()
1497 BENCH_BASE(folly_rwspin, 32thread_all_write, 32, 1.0, false)
1498 BENCH_REL (shmtx_wr_pri, 32thread_all_write, 32, 1.0, false)
1499 BENCH_REL (shmtx_rd_pri, 32thread_all_write, 32, 1.0, false)
1500 BENCH_REL (folly_ticket, 32thread_all_write, 32, 1.0, false)
1501 BENCH_REL (boost_shared, 32thread_all_write, 32, 1.0, false)
1502 BENCH_REL (pthrd_rwlock, 32thread_all_write, 32, 1.0, false)
1503 BENCH_REL (pthrd_mutex_, 32thread_all_write, 32, 1.0, false)
1504 BENCHMARK_DRAW_LINE()
1505 BENCH_BASE(folly_rwspin, 64thread_all_write, 64, 1.0, false)
1506 BENCH_REL (shmtx_wr_pri, 64thread_all_write, 64, 1.0, false)
1507 BENCH_REL (shmtx_rd_pri, 64thread_all_write, 64, 1.0, false)
1508 BENCH_REL (folly_ticket, 64thread_all_write, 64, 1.0, false)
1509 BENCH_REL (boost_shared, 64thread_all_write, 64, 1.0, false)
1510 BENCH_REL (pthrd_rwlock, 64thread_all_write, 64, 1.0, false)
1511 BENCH_REL (pthrd_mutex_, 64thread_all_write, 64, 1.0, false)
1512
1513 // 1 lock used by everybody, 10% writes.  Not much scaling to be had.  Perf
1514 // is best at 1 thread, once you've got multiple threads > 8 threads hurts.
1515 BENCHMARK_DRAW_LINE()
1516 BENCHMARK_DRAW_LINE()
1517 BENCH_BASE(folly_rwspin, 1thread_10pct_write, 1, 0.10, false)
1518 BENCH_REL (shmtx_wr_pri, 1thread_10pct_write, 1, 0.10, false)
1519 BENCH_REL (shmtx_rd_pri, 1thread_10pct_write, 1, 0.10, false)
1520 BENCH_REL (folly_ticket, 1thread_10pct_write, 1, 0.10, false)
1521 BENCH_REL (boost_shared, 1thread_10pct_write, 1, 0.10, false)
1522 BENCH_REL (pthrd_rwlock, 1thread_10pct_write, 1, 0.10, false)
1523 BENCHMARK_DRAW_LINE()
1524 BENCH_BASE(folly_rwspin, 2thread_10pct_write, 2, 0.10, false)
1525 BENCH_REL (shmtx_wr_pri, 2thread_10pct_write, 2, 0.10, false)
1526 BENCH_REL (shmtx_rd_pri, 2thread_10pct_write, 2, 0.10, false)
1527 BENCH_REL (folly_ticket, 2thread_10pct_write, 2, 0.10, false)
1528 BENCH_REL (boost_shared, 2thread_10pct_write, 2, 0.10, false)
1529 BENCH_REL (pthrd_rwlock, 2thread_10pct_write, 2, 0.10, false)
1530 BENCHMARK_DRAW_LINE()
1531 BENCH_BASE(folly_rwspin, 4thread_10pct_write, 4, 0.10, false)
1532 BENCH_REL (shmtx_wr_pri, 4thread_10pct_write, 4, 0.10, false)
1533 BENCH_REL (shmtx_rd_pri, 4thread_10pct_write, 4, 0.10, false)
1534 BENCH_REL (folly_ticket, 4thread_10pct_write, 4, 0.10, false)
1535 BENCH_REL (boost_shared, 4thread_10pct_write, 4, 0.10, false)
1536 BENCH_REL (pthrd_rwlock, 4thread_10pct_write, 4, 0.10, false)
1537 BENCHMARK_DRAW_LINE()
1538 BENCH_BASE(folly_rwspin, 8thread_10pct_write, 8, 0.10, false)
1539 BENCH_REL (shmtx_wr_pri, 8thread_10pct_write, 8, 0.10, false)
1540 BENCH_REL (shmtx_rd_pri, 8thread_10pct_write, 8, 0.10, false)
1541 BENCH_REL (folly_ticket, 8thread_10pct_write, 8, 0.10, false)
1542 BENCH_REL (boost_shared, 8thread_10pct_write, 8, 0.10, false)
1543 BENCH_REL (pthrd_rwlock, 8thread_10pct_write, 8, 0.10, false)
1544 BENCHMARK_DRAW_LINE()
1545 BENCH_BASE(folly_rwspin, 16thread_10pct_write, 16, 0.10, false)
1546 BENCH_REL (shmtx_wr_pri, 16thread_10pct_write, 16, 0.10, false)
1547 BENCH_REL (shmtx_rd_pri, 16thread_10pct_write, 16, 0.10, false)
1548 BENCH_REL (folly_ticket, 16thread_10pct_write, 16, 0.10, false)
1549 BENCH_REL (boost_shared, 16thread_10pct_write, 16, 0.10, false)
1550 BENCH_REL (pthrd_rwlock, 16thread_10pct_write, 16, 0.10, false)
1551 BENCHMARK_DRAW_LINE()
1552 BENCH_BASE(folly_rwspin, 32thread_10pct_write, 32, 0.10, false)
1553 BENCH_REL (shmtx_wr_pri, 32thread_10pct_write, 32, 0.10, false)
1554 BENCH_REL (shmtx_rd_pri, 32thread_10pct_write, 32, 0.10, false)
1555 BENCH_REL (folly_ticket, 32thread_10pct_write, 32, 0.10, false)
1556 BENCH_REL (boost_shared, 32thread_10pct_write, 32, 0.10, false)
1557 BENCH_REL (pthrd_rwlock, 32thread_10pct_write, 32, 0.10, false)
1558 BENCHMARK_DRAW_LINE()
1559 BENCH_BASE(folly_rwspin, 64thread_10pct_write, 64, 0.10, false)
1560 BENCH_REL (shmtx_wr_pri, 64thread_10pct_write, 64, 0.10, false)
1561 BENCH_REL (shmtx_rd_pri, 64thread_10pct_write, 64, 0.10, false)
1562 BENCH_REL (folly_ticket, 64thread_10pct_write, 64, 0.10, false)
1563 BENCH_REL (boost_shared, 64thread_10pct_write, 64, 0.10, false)
1564 BENCH_REL (pthrd_rwlock, 64thread_10pct_write, 64, 0.10, false)
1565
1566 // 1 lock used by everybody, 1% writes.  This is a more realistic example
1567 // than the concurrent_*_reads benchmark, but still shows SharedMutex locks
1568 // winning over all of the others
1569 BENCHMARK_DRAW_LINE()
1570 BENCHMARK_DRAW_LINE()
1571 BENCH_BASE(folly_rwspin, 1thread_1pct_write, 1, 0.01, false)
1572 BENCH_REL (shmtx_wr_pri, 1thread_1pct_write, 1, 0.01, false)
1573 BENCH_REL (shmtx_w_bare, 1thread_1pct_write, 1, 0.01, false)
1574 BENCH_REL (shmtx_rd_pri, 1thread_1pct_write, 1, 0.01, false)
1575 BENCH_REL (shmtx_r_bare, 1thread_1pct_write, 1, 0.01, false)
1576 BENCH_REL (folly_ticket, 1thread_1pct_write, 1, 0.01, false)
1577 BENCH_REL (boost_shared, 1thread_1pct_write, 1, 0.01, false)
1578 BENCH_REL (pthrd_rwlock, 1thread_1pct_write, 1, 0.01, false)
1579 BENCHMARK_DRAW_LINE()
1580 BENCH_BASE(folly_rwspin, 2thread_1pct_write, 2, 0.01, false)
1581 BENCH_REL (shmtx_wr_pri, 2thread_1pct_write, 2, 0.01, false)
1582 BENCH_REL (shmtx_w_bare, 2thread_1pct_write, 2, 0.01, false)
1583 BENCH_REL (shmtx_rd_pri, 2thread_1pct_write, 2, 0.01, false)
1584 BENCH_REL (shmtx_r_bare, 2thread_1pct_write, 2, 0.01, false)
1585 BENCH_REL (folly_ticket, 2thread_1pct_write, 2, 0.01, false)
1586 BENCH_REL (boost_shared, 2thread_1pct_write, 2, 0.01, false)
1587 BENCH_REL (pthrd_rwlock, 2thread_1pct_write, 2, 0.01, false)
1588 BENCHMARK_DRAW_LINE()
1589 BENCH_BASE(folly_rwspin, 4thread_1pct_write, 4, 0.01, false)
1590 BENCH_REL (shmtx_wr_pri, 4thread_1pct_write, 4, 0.01, false)
1591 BENCH_REL (shmtx_w_bare, 4thread_1pct_write, 4, 0.01, false)
1592 BENCH_REL (shmtx_rd_pri, 4thread_1pct_write, 4, 0.01, false)
1593 BENCH_REL (shmtx_r_bare, 4thread_1pct_write, 4, 0.01, false)
1594 BENCH_REL (folly_ticket, 4thread_1pct_write, 4, 0.01, false)
1595 BENCH_REL (boost_shared, 4thread_1pct_write, 4, 0.01, false)
1596 BENCH_REL (pthrd_rwlock, 4thread_1pct_write, 4, 0.01, false)
1597 BENCHMARK_DRAW_LINE()
1598 BENCH_BASE(folly_rwspin, 8thread_1pct_write, 8, 0.01, false)
1599 BENCH_REL (shmtx_wr_pri, 8thread_1pct_write, 8, 0.01, false)
1600 BENCH_REL (shmtx_w_bare, 8thread_1pct_write, 8, 0.01, false)
1601 BENCH_REL (shmtx_rd_pri, 8thread_1pct_write, 8, 0.01, false)
1602 BENCH_REL (shmtx_r_bare, 8thread_1pct_write, 8, 0.01, false)
1603 BENCH_REL (folly_ticket, 8thread_1pct_write, 8, 0.01, false)
1604 BENCH_REL (boost_shared, 8thread_1pct_write, 8, 0.01, false)
1605 BENCH_REL (pthrd_rwlock, 8thread_1pct_write, 8, 0.01, false)
1606 BENCHMARK_DRAW_LINE()
1607 BENCH_BASE(folly_rwspin, 16thread_1pct_write, 16, 0.01, false)
1608 BENCH_REL (shmtx_wr_pri, 16thread_1pct_write, 16, 0.01, false)
1609 BENCH_REL (shmtx_w_bare, 16thread_1pct_write, 16, 0.01, false)
1610 BENCH_REL (shmtx_rd_pri, 16thread_1pct_write, 16, 0.01, false)
1611 BENCH_REL (shmtx_r_bare, 16thread_1pct_write, 16, 0.01, false)
1612 BENCH_REL (folly_ticket, 16thread_1pct_write, 16, 0.01, false)
1613 BENCH_REL (boost_shared, 16thread_1pct_write, 16, 0.01, false)
1614 BENCH_REL (pthrd_rwlock, 16thread_1pct_write, 16, 0.01, false)
1615 BENCHMARK_DRAW_LINE()
1616 BENCH_BASE(folly_rwspin, 32thread_1pct_write, 32, 0.01, false)
1617 BENCH_REL (shmtx_wr_pri, 32thread_1pct_write, 32, 0.01, false)
1618 BENCH_REL (shmtx_w_bare, 32thread_1pct_write, 32, 0.01, false)
1619 BENCH_REL (shmtx_rd_pri, 32thread_1pct_write, 32, 0.01, false)
1620 BENCH_REL (shmtx_r_bare, 32thread_1pct_write, 32, 0.01, false)
1621 BENCH_REL (folly_ticket, 32thread_1pct_write, 32, 0.01, false)
1622 BENCH_REL (boost_shared, 32thread_1pct_write, 32, 0.01, false)
1623 BENCH_REL (pthrd_rwlock, 32thread_1pct_write, 32, 0.01, false)
1624 BENCHMARK_DRAW_LINE()
1625 BENCH_BASE(folly_rwspin, 64thread_1pct_write, 64, 0.01, false)
1626 BENCH_REL (shmtx_wr_pri, 64thread_1pct_write, 64, 0.01, false)
1627 BENCH_REL (shmtx_w_bare, 64thread_1pct_write, 64, 0.01, false)
1628 BENCH_REL (shmtx_rd_pri, 64thread_1pct_write, 64, 0.01, false)
1629 BENCH_REL (shmtx_r_bare, 64thread_1pct_write, 64, 0.01, false)
1630 BENCH_REL (folly_ticket, 64thread_1pct_write, 64, 0.01, false)
1631 BENCH_REL (boost_shared, 64thread_1pct_write, 64, 0.01, false)
1632 BENCH_REL (pthrd_rwlock, 64thread_1pct_write, 64, 0.01, false)
1633
1634 // Worst case scenario for deferred locks. No actual sharing, likely that
1635 // read operations will have to first set the kDeferredReadersPossibleBit,
1636 // and likely that writers will have to scan deferredReaders[].
1637 BENCHMARK_DRAW_LINE()
1638 BENCH_BASE(folly_rwspin, 2thr_2lock_50pct_write, 2, 0.50, true)
1639 BENCH_REL (shmtx_wr_pri, 2thr_2lock_50pct_write, 2, 0.50, true)
1640 BENCH_REL (shmtx_rd_pri, 2thr_2lock_50pct_write, 2, 0.50, true)
1641 BENCH_BASE(folly_rwspin, 4thr_4lock_50pct_write, 4, 0.50, true)
1642 BENCH_REL (shmtx_wr_pri, 4thr_4lock_50pct_write, 4, 0.50, true)
1643 BENCH_REL (shmtx_rd_pri, 4thr_4lock_50pct_write, 4, 0.50, true)
1644 BENCH_BASE(folly_rwspin, 8thr_8lock_50pct_write, 8, 0.50, true)
1645 BENCH_REL (shmtx_wr_pri, 8thr_8lock_50pct_write, 8, 0.50, true)
1646 BENCH_REL (shmtx_rd_pri, 8thr_8lock_50pct_write, 8, 0.50, true)
1647 BENCH_BASE(folly_rwspin, 16thr_16lock_50pct_write, 16, 0.50, true)
1648 BENCH_REL (shmtx_wr_pri, 16thr_16lock_50pct_write, 16, 0.50, true)
1649 BENCH_REL (shmtx_rd_pri, 16thr_16lock_50pct_write, 16, 0.50, true)
1650 BENCH_BASE(folly_rwspin, 32thr_32lock_50pct_write, 32, 0.50, true)
1651 BENCH_REL (shmtx_wr_pri, 32thr_32lock_50pct_write, 32, 0.50, true)
1652 BENCH_REL (shmtx_rd_pri, 32thr_32lock_50pct_write, 32, 0.50, true)
1653 BENCH_BASE(folly_rwspin, 64thr_64lock_50pct_write, 64, 0.50, true)
1654 BENCH_REL (shmtx_wr_pri, 64thr_64lock_50pct_write, 64, 0.50, true)
1655 BENCH_REL (shmtx_rd_pri, 64thr_64lock_50pct_write, 64, 0.50, true)
1656 BENCHMARK_DRAW_LINE()
1657 BENCH_BASE(folly_rwspin, 2thr_2lock_10pct_write, 2, 0.10, true)
1658 BENCH_REL (shmtx_wr_pri, 2thr_2lock_10pct_write, 2, 0.10, true)
1659 BENCH_REL (shmtx_rd_pri, 2thr_2lock_10pct_write, 2, 0.10, true)
1660 BENCH_BASE(folly_rwspin, 4thr_4lock_10pct_write, 4, 0.10, true)
1661 BENCH_REL (shmtx_wr_pri, 4thr_4lock_10pct_write, 4, 0.10, true)
1662 BENCH_REL (shmtx_rd_pri, 4thr_4lock_10pct_write, 4, 0.10, true)
1663 BENCH_BASE(folly_rwspin, 8thr_8lock_10pct_write, 8, 0.10, true)
1664 BENCH_REL (shmtx_wr_pri, 8thr_8lock_10pct_write, 8, 0.10, true)
1665 BENCH_REL (shmtx_rd_pri, 8thr_8lock_10pct_write, 8, 0.10, true)
1666 BENCH_BASE(folly_rwspin, 16thr_16lock_10pct_write, 16, 0.10, true)
1667 BENCH_REL (shmtx_wr_pri, 16thr_16lock_10pct_write, 16, 0.10, true)
1668 BENCH_REL (shmtx_rd_pri, 16thr_16lock_10pct_write, 16, 0.10, true)
1669 BENCH_BASE(folly_rwspin, 32thr_32lock_10pct_write, 32, 0.10, true)
1670 BENCH_REL (shmtx_wr_pri, 32thr_32lock_10pct_write, 32, 0.10, true)
1671 BENCH_REL (shmtx_rd_pri, 32thr_32lock_10pct_write, 32, 0.10, true)
1672 BENCH_BASE(folly_rwspin, 64thr_64lock_10pct_write, 64, 0.10, true)
1673 BENCH_REL (shmtx_wr_pri, 64thr_64lock_10pct_write, 64, 0.10, true)
1674 BENCH_REL (shmtx_rd_pri, 64thr_64lock_10pct_write, 64, 0.10, true)
1675 BENCHMARK_DRAW_LINE()
1676 BENCH_BASE(folly_rwspin, 2thr_2lock_1pct_write, 2, 0.01, true)
1677 BENCH_REL (shmtx_wr_pri, 2thr_2lock_1pct_write, 2, 0.01, true)
1678 BENCH_REL (shmtx_rd_pri, 2thr_2lock_1pct_write, 2, 0.01, true)
1679 BENCH_BASE(folly_rwspin, 4thr_4lock_1pct_write, 4, 0.01, true)
1680 BENCH_REL (shmtx_wr_pri, 4thr_4lock_1pct_write, 4, 0.01, true)
1681 BENCH_REL (shmtx_rd_pri, 4thr_4lock_1pct_write, 4, 0.01, true)
1682 BENCH_BASE(folly_rwspin, 8thr_8lock_1pct_write, 8, 0.01, true)
1683 BENCH_REL (shmtx_wr_pri, 8thr_8lock_1pct_write, 8, 0.01, true)
1684 BENCH_REL (shmtx_rd_pri, 8thr_8lock_1pct_write, 8, 0.01, true)
1685 BENCH_BASE(folly_rwspin, 16thr_16lock_1pct_write, 16, 0.01, true)
1686 BENCH_REL (shmtx_wr_pri, 16thr_16lock_1pct_write, 16, 0.01, true)
1687 BENCH_REL (shmtx_rd_pri, 16thr_16lock_1pct_write, 16, 0.01, true)
1688 BENCH_BASE(folly_rwspin, 32thr_32lock_1pct_write, 32, 0.01, true)
1689 BENCH_REL (shmtx_wr_pri, 32thr_32lock_1pct_write, 32, 0.01, true)
1690 BENCH_REL (shmtx_rd_pri, 32thr_32lock_1pct_write, 32, 0.01, true)
1691 BENCH_BASE(folly_rwspin, 64thr_64lock_1pct_write, 64, 0.01, true)
1692 BENCH_REL (shmtx_wr_pri, 64thr_64lock_1pct_write, 64, 0.01, true)
1693 BENCH_REL (shmtx_rd_pri, 64thr_64lock_1pct_write, 64, 0.01, true)
1694
1695 // Ping-pong tests have a scaled number of iterations, because their burn
1696 // loop would make them too slow otherwise.  Ping-pong with burn count of
1697 // 100k or 300k shows the advantage of soft-spin, reducing the cost of
1698 // each wakeup by about 20 usec.  (Take benchmark reported difference,
1699 // ~400 nanos, multiply by the scale of 100, then divide by 2 because
1700 // each round has two wakeups.)
1701 BENCHMARK_DRAW_LINE()
1702 BENCHMARK_DRAW_LINE()
1703 BENCH_BASE(folly_rwspin_ping_pong, burn0, 1, 0)
1704 BENCH_REL (shmtx_w_bare_ping_pong, burn0, 1, 0)
1705 BENCH_REL (shmtx_r_bare_ping_pong, burn0, 1, 0)
1706 BENCH_REL (folly_ticket_ping_pong, burn0, 1, 0)
1707 BENCH_REL (boost_shared_ping_pong, burn0, 1, 0)
1708 BENCH_REL (pthrd_rwlock_ping_pong, burn0, 1, 0)
1709 BENCHMARK_DRAW_LINE()
1710 BENCH_BASE(folly_rwspin_ping_pong, burn100k, 100, 100000)
1711 BENCH_REL (shmtx_w_bare_ping_pong, burn100k, 100, 100000)
1712 BENCH_REL (shmtx_r_bare_ping_pong, burn100k, 100, 100000)
1713 BENCH_REL (folly_ticket_ping_pong, burn100k, 100, 100000)
1714 BENCH_REL (boost_shared_ping_pong, burn100k, 100, 100000)
1715 BENCH_REL (pthrd_rwlock_ping_pong, burn100k, 100, 100000)
1716 BENCHMARK_DRAW_LINE()
1717 BENCH_BASE(folly_rwspin_ping_pong, burn300k, 100, 300000)
1718 BENCH_REL (shmtx_w_bare_ping_pong, burn300k, 100, 300000)
1719 BENCH_REL (shmtx_r_bare_ping_pong, burn300k, 100, 300000)
1720 BENCH_REL (folly_ticket_ping_pong, burn300k, 100, 300000)
1721 BENCH_REL (boost_shared_ping_pong, burn300k, 100, 300000)
1722 BENCH_REL (pthrd_rwlock_ping_pong, burn300k, 100, 300000)
1723 BENCHMARK_DRAW_LINE()
1724 BENCH_BASE(folly_rwspin_ping_pong, burn1M, 1000, 1000000)
1725 BENCH_REL (shmtx_w_bare_ping_pong, burn1M, 1000, 1000000)
1726 BENCH_REL (shmtx_r_bare_ping_pong, burn1M, 1000, 1000000)
1727 BENCH_REL (folly_ticket_ping_pong, burn1M, 1000, 1000000)
1728 BENCH_REL (boost_shared_ping_pong, burn1M, 1000, 1000000)
1729 BENCH_REL (pthrd_rwlock_ping_pong, burn1M, 1000, 1000000)
1730
1731 // Reproduce with 10 minutes and
1732 //   sudo nice -n -20
1733 //     shared_mutex_test --benchmark --bm_min_iters=1000000
1734 //
1735 // Comparison use folly::RWSpinLock as the baseline, with the
1736 // following row being the default SharedMutex (using *Holder or
1737 // Token-ful methods).
1738 // ============================================================================
1739 // folly/experimental/test/SharedMutexTest.cpp     relative  time/iter  iters/s
1740 // ============================================================================
1741 // single_thread_lock_shared_unlock_shared                     22.78ns   43.89M
1742 // single_thread_lock_unlock                                   26.01ns   38.45M
1743 // ----------------------------------------------------------------------------
1744 // ----------------------------------------------------------------------------
1745 // folly_rwspin_reads(1thread)                                 15.09ns   66.25M
1746 // shmtx_wr_pri_reads(1thread)                       69.89%    21.60ns   46.30M
1747 // shmtx_w_bare_reads(1thread)                       58.25%    25.91ns   38.59M
1748 // shmtx_rd_pri_reads(1thread)                       72.50%    20.82ns   48.03M
1749 // shmtx_r_bare_reads(1thread)                       58.27%    25.91ns   38.60M
1750 // folly_ticket_reads(1thread)                       54.80%    27.55ns   36.30M
1751 // boost_shared_reads(1thread)                       10.88%   138.80ns    7.20M
1752 // pthrd_rwlock_reads(1thread)                       40.68%    37.11ns   26.95M
1753 // ----------------------------------------------------------------------------
1754 // folly_rwspin_reads(2thread)                                 92.63ns   10.80M
1755 // shmtx_wr_pri_reads(2thread)                      462.86%    20.01ns   49.97M
1756 // shmtx_w_bare_reads(2thread)                      430.53%    21.51ns   46.48M
1757 // shmtx_rd_pri_reads(2thread)                      487.13%    19.01ns   52.59M
1758 // shmtx_r_bare_reads(2thread)                      433.35%    21.37ns   46.79M
1759 // folly_ticket_reads(2thread)                       69.82%   132.67ns    7.54M
1760 // boost_shared_reads(2thread)                       36.66%   252.63ns    3.96M
1761 // pthrd_rwlock_reads(2thread)                      127.76%    72.50ns   13.79M
1762 // ----------------------------------------------------------------------------
1763 // folly_rwspin_reads(4thread)                                 97.45ns   10.26M
1764 // shmtx_wr_pri_reads(4thread)                      978.22%     9.96ns  100.38M
1765 // shmtx_w_bare_reads(4thread)                      908.35%    10.73ns   93.21M
1766 // shmtx_rd_pri_reads(4thread)                     1032.29%     9.44ns  105.93M
1767 // shmtx_r_bare_reads(4thread)                      912.38%    10.68ns   93.63M
1768 // folly_ticket_reads(4thread)                       46.08%   211.46ns    4.73M
1769 // boost_shared_reads(4thread)                       25.00%   389.74ns    2.57M
1770 // pthrd_rwlock_reads(4thread)                       47.53%   205.01ns    4.88M
1771 // ----------------------------------------------------------------------------
1772 // folly_rwspin_reads(8thread)                                147.24ns    6.79M
1773 // shmtx_wr_pri_reads(8thread)                     2915.66%     5.05ns  198.02M
1774 // shmtx_w_bare_reads(8thread)                     2699.32%     5.45ns  183.32M
1775 // shmtx_rd_pri_reads(8thread)                     3092.58%     4.76ns  210.03M
1776 // shmtx_r_bare_reads(8thread)                     2744.63%     5.36ns  186.40M
1777 // folly_ticket_reads(8thread)                       54.84%   268.47ns    3.72M
1778 // boost_shared_reads(8thread)                       42.40%   347.30ns    2.88M
1779 // pthrd_rwlock_reads(8thread)                       78.90%   186.63ns    5.36M
1780 // ----------------------------------------------------------------------------
1781 // folly_rwspin_reads(16thread)                               166.25ns    6.02M
1782 // shmtx_wr_pri_reads(16thread)                    6133.03%     2.71ns  368.91M
1783 // shmtx_w_bare_reads(16thread)                    5936.05%     2.80ns  357.06M
1784 // shmtx_rd_pri_reads(16thread)                    6786.57%     2.45ns  408.22M
1785 // shmtx_r_bare_reads(16thread)                    5995.54%     2.77ns  360.64M
1786 // folly_ticket_reads(16thread)                      56.35%   295.01ns    3.39M
1787 // boost_shared_reads(16thread)                      51.62%   322.08ns    3.10M
1788 // pthrd_rwlock_reads(16thread)                      92.47%   179.79ns    5.56M
1789 // ----------------------------------------------------------------------------
1790 // folly_rwspin_reads(32thread)                               107.72ns    9.28M
1791 // shmtx_wr_pri_reads(32thread)                    6772.80%     1.59ns  628.77M
1792 // shmtx_w_bare_reads(32thread)                    6236.13%     1.73ns  578.94M
1793 // shmtx_rd_pri_reads(32thread)                    8143.32%     1.32ns  756.00M
1794 // shmtx_r_bare_reads(32thread)                    6485.18%     1.66ns  602.06M
1795 // folly_ticket_reads(32thread)                      35.12%   306.73ns    3.26M
1796 // boost_shared_reads(32thread)                      28.19%   382.17ns    2.62M
1797 // pthrd_rwlock_reads(32thread)                      65.29%   164.99ns    6.06M
1798 // ----------------------------------------------------------------------------
1799 // folly_rwspin_reads(64thread)                               119.46ns    8.37M
1800 // shmtx_wr_pri_reads(64thread)                    6744.92%     1.77ns  564.60M
1801 // shmtx_w_bare_reads(64thread)                    6268.50%     1.91ns  524.72M
1802 // shmtx_rd_pri_reads(64thread)                    7508.56%     1.59ns  628.52M
1803 // shmtx_r_bare_reads(64thread)                    6299.53%     1.90ns  527.32M
1804 // folly_ticket_reads(64thread)                      37.42%   319.26ns    3.13M
1805 // boost_shared_reads(64thread)                      32.58%   366.70ns    2.73M
1806 // pthrd_rwlock_reads(64thread)                      73.64%   162.24ns    6.16M
1807 // ----------------------------------------------------------------------------
1808 // ----------------------------------------------------------------------------
1809 // folly_rwspin(1thread_all_write)                             25.51ns   39.19M
1810 // shmtx_wr_pri(1thread_all_write)                   97.38%    26.20ns   38.17M
1811 // shmtx_rd_pri(1thread_all_write)                   97.55%    26.16ns   38.23M
1812 // folly_ticket(1thread_all_write)                   90.98%    28.04ns   35.66M
1813 // boost_shared(1thread_all_write)                   16.80%   151.89ns    6.58M
1814 // pthrd_rwlock(1thread_all_write)                   63.86%    39.96ns   25.03M
1815 // pthrd_mutex_(1thread_all_write)                   82.05%    31.09ns   32.16M
1816 // ----------------------------------------------------------------------------
1817 // folly_rwspin(2thread_all_write)                            100.70ns    9.93M
1818 // shmtx_wr_pri(2thread_all_write)                   40.83%   246.61ns    4.05M
1819 // shmtx_rd_pri(2thread_all_write)                   40.53%   248.44ns    4.03M
1820 // folly_ticket(2thread_all_write)                   58.49%   172.17ns    5.81M
1821 // boost_shared(2thread_all_write)                   24.26%   415.00ns    2.41M
1822 // pthrd_rwlock(2thread_all_write)                   41.35%   243.49ns    4.11M
1823 // pthrd_mutex_(2thread_all_write)                  146.91%    68.55ns   14.59M
1824 // ----------------------------------------------------------------------------
1825 // folly_rwspin(4thread_all_write)                            199.52ns    5.01M
1826 // shmtx_wr_pri(4thread_all_write)                   51.71%   385.86ns    2.59M
1827 // shmtx_rd_pri(4thread_all_write)                   49.43%   403.62ns    2.48M
1828 // folly_ticket(4thread_all_write)                  117.88%   169.26ns    5.91M
1829 // boost_shared(4thread_all_write)                    9.81%     2.03us  491.48K
1830 // pthrd_rwlock(4thread_all_write)                   28.23%   706.69ns    1.42M
1831 // pthrd_mutex_(4thread_all_write)                  111.54%   178.88ns    5.59M
1832 // ----------------------------------------------------------------------------
1833 // folly_rwspin(8thread_all_write)                            304.61ns    3.28M
1834 // shmtx_wr_pri(8thread_all_write)                   69.77%   436.59ns    2.29M
1835 // shmtx_rd_pri(8thread_all_write)                   66.58%   457.51ns    2.19M
1836 // folly_ticket(8thread_all_write)                  141.00%   216.03ns    4.63M
1837 // boost_shared(8thread_all_write)                    6.11%     4.99us  200.59K
1838 // pthrd_rwlock(8thread_all_write)                   38.03%   800.88ns    1.25M
1839 // pthrd_mutex_(8thread_all_write)                  177.66%   171.45ns    5.83M
1840 // ----------------------------------------------------------------------------
1841 // folly_rwspin(16thread_all_write)                           576.97ns    1.73M
1842 // shmtx_wr_pri(16thread_all_write)                 105.72%   545.77ns    1.83M
1843 // shmtx_rd_pri(16thread_all_write)                 105.13%   548.83ns    1.82M
1844 // folly_ticket(16thread_all_write)                 161.70%   356.82ns    2.80M
1845 // boost_shared(16thread_all_write)                   7.73%     7.46us  134.03K
1846 // pthrd_rwlock(16thread_all_write)                  96.88%   595.54ns    1.68M
1847 // pthrd_mutex_(16thread_all_write)                 330.44%   174.61ns    5.73M
1848 // ----------------------------------------------------------------------------
1849 // folly_rwspin(32thread_all_write)                             1.41us  707.76K
1850 // shmtx_wr_pri(32thread_all_write)                 240.46%   587.58ns    1.70M
1851 // shmtx_rd_pri(32thread_all_write)                 393.71%   358.87ns    2.79M
1852 // folly_ticket(32thread_all_write)                 325.07%   434.65ns    2.30M
1853 // boost_shared(32thread_all_write)                  18.57%     7.61us  131.43K
1854 // pthrd_rwlock(32thread_all_write)                 266.78%   529.62ns    1.89M
1855 // pthrd_mutex_(32thread_all_write)                 877.89%   160.94ns    6.21M
1856 // ----------------------------------------------------------------------------
1857 // folly_rwspin(64thread_all_write)                             1.76us  566.94K
1858 // shmtx_wr_pri(64thread_all_write)                 255.67%   689.91ns    1.45M
1859 // shmtx_rd_pri(64thread_all_write)                 468.82%   376.23ns    2.66M
1860 // folly_ticket(64thread_all_write)                 294.72%   598.49ns    1.67M
1861 // boost_shared(64thread_all_write)                  23.39%     7.54us  132.58K
1862 // pthrd_rwlock(64thread_all_write)                 321.39%   548.83ns    1.82M
1863 // pthrd_mutex_(64thread_all_write)                1165.04%   151.40ns    6.61M
1864 // ----------------------------------------------------------------------------
1865 // ----------------------------------------------------------------------------
1866 // folly_rwspin(1thread_10pct_write)                           19.51ns   51.26M
1867 // shmtx_wr_pri(1thread_10pct_write)                 83.25%    23.43ns   42.67M
1868 // shmtx_rd_pri(1thread_10pct_write)                 83.31%    23.42ns   42.71M
1869 // folly_ticket(1thread_10pct_write)                 70.88%    27.52ns   36.34M
1870 // boost_shared(1thread_10pct_write)                 13.09%   148.99ns    6.71M
1871 // pthrd_rwlock(1thread_10pct_write)                 47.41%    41.15ns   24.30M
1872 // ----------------------------------------------------------------------------
1873 // folly_rwspin(2thread_10pct_write)                          159.42ns    6.27M
1874 // shmtx_wr_pri(2thread_10pct_write)                188.44%    84.60ns   11.82M
1875 // shmtx_rd_pri(2thread_10pct_write)                188.29%    84.67ns   11.81M
1876 // folly_ticket(2thread_10pct_write)                140.28%   113.64ns    8.80M
1877 // boost_shared(2thread_10pct_write)                 42.09%   378.81ns    2.64M
1878 // pthrd_rwlock(2thread_10pct_write)                103.86%   153.49ns    6.51M
1879 // ----------------------------------------------------------------------------
1880 // folly_rwspin(4thread_10pct_write)                          193.35ns    5.17M
1881 // shmtx_wr_pri(4thread_10pct_write)                184.30%   104.91ns    9.53M
1882 // shmtx_rd_pri(4thread_10pct_write)                163.76%   118.07ns    8.47M
1883 // folly_ticket(4thread_10pct_write)                124.07%   155.84ns    6.42M
1884 // boost_shared(4thread_10pct_write)                 16.32%     1.18us  843.92K
1885 // pthrd_rwlock(4thread_10pct_write)                 48.59%   397.94ns    2.51M
1886 // ----------------------------------------------------------------------------
1887 // folly_rwspin(8thread_10pct_write)                          373.17ns    2.68M
1888 // shmtx_wr_pri(8thread_10pct_write)                252.02%   148.08ns    6.75M
1889 // shmtx_rd_pri(8thread_10pct_write)                203.59%   183.30ns    5.46M
1890 // folly_ticket(8thread_10pct_write)                184.37%   202.40ns    4.94M
1891 // boost_shared(8thread_10pct_write)                 15.85%     2.35us  424.72K
1892 // pthrd_rwlock(8thread_10pct_write)                 83.03%   449.45ns    2.22M
1893 // ----------------------------------------------------------------------------
1894 // folly_rwspin(16thread_10pct_write)                         742.87ns    1.35M
1895 // shmtx_wr_pri(16thread_10pct_write)               344.27%   215.78ns    4.63M
1896 // shmtx_rd_pri(16thread_10pct_write)               287.04%   258.80ns    3.86M
1897 // folly_ticket(16thread_10pct_write)               277.25%   267.94ns    3.73M
1898 // boost_shared(16thread_10pct_write)                15.33%     4.85us  206.30K
1899 // pthrd_rwlock(16thread_10pct_write)               158.34%   469.16ns    2.13M
1900 // ----------------------------------------------------------------------------
1901 // folly_rwspin(32thread_10pct_write)                         799.97ns    1.25M
1902 // shmtx_wr_pri(32thread_10pct_write)               351.40%   227.65ns    4.39M
1903 // shmtx_rd_pri(32thread_10pct_write)               341.71%   234.11ns    4.27M
1904 // folly_ticket(32thread_10pct_write)               245.91%   325.31ns    3.07M
1905 // boost_shared(32thread_10pct_write)                 7.72%    10.36us   96.56K
1906 // pthrd_rwlock(32thread_10pct_write)               165.87%   482.30ns    2.07M
1907 // ----------------------------------------------------------------------------
1908 // folly_rwspin(64thread_10pct_write)                           1.12us  892.01K
1909 // shmtx_wr_pri(64thread_10pct_write)               429.84%   260.81ns    3.83M
1910 // shmtx_rd_pri(64thread_10pct_write)               456.93%   245.35ns    4.08M
1911 // folly_ticket(64thread_10pct_write)               219.21%   511.42ns    1.96M
1912 // boost_shared(64thread_10pct_write)                 5.43%    20.65us   48.44K
1913 // pthrd_rwlock(64thread_10pct_write)               233.93%   479.23ns    2.09M
1914 // ----------------------------------------------------------------------------
1915 // ----------------------------------------------------------------------------
1916 // folly_rwspin(1thread_1pct_write)                            18.88ns   52.98M
1917 // shmtx_wr_pri(1thread_1pct_write)                  81.53%    23.15ns   43.19M
1918 // shmtx_w_bare(1thread_1pct_write)                  67.90%    27.80ns   35.97M
1919 // shmtx_rd_pri(1thread_1pct_write)                  81.50%    23.16ns   43.18M
1920 // shmtx_r_bare(1thread_1pct_write)                  67.74%    27.86ns   35.89M
1921 // folly_ticket(1thread_1pct_write)                  68.68%    27.48ns   36.39M
1922 // boost_shared(1thread_1pct_write)                  12.80%   147.51ns    6.78M
1923 // pthrd_rwlock(1thread_1pct_write)                  45.81%    41.20ns   24.27M
1924 // ----------------------------------------------------------------------------
1925 // folly_rwspin(2thread_1pct_write)                           125.85ns    7.95M
1926 // shmtx_wr_pri(2thread_1pct_write)                 359.04%    35.05ns   28.53M
1927 // shmtx_w_bare(2thread_1pct_write)                 475.60%    26.46ns   37.79M
1928 // shmtx_rd_pri(2thread_1pct_write)                 332.75%    37.82ns   26.44M
1929 // shmtx_r_bare(2thread_1pct_write)                 115.64%   108.83ns    9.19M
1930 // folly_ticket(2thread_1pct_write)                 140.24%    89.74ns   11.14M
1931 // boost_shared(2thread_1pct_write)                  40.62%   309.82ns    3.23M
1932 // pthrd_rwlock(2thread_1pct_write)                 134.67%    93.45ns   10.70M
1933 // ----------------------------------------------------------------------------
1934 // folly_rwspin(4thread_1pct_write)                           126.70ns    7.89M
1935 // shmtx_wr_pri(4thread_1pct_write)                 422.20%    30.01ns   33.32M
1936 // shmtx_w_bare(4thread_1pct_write)                 403.52%    31.40ns   31.85M
1937 // shmtx_rd_pri(4thread_1pct_write)                 282.50%    44.85ns   22.30M
1938 // shmtx_r_bare(4thread_1pct_write)                  66.30%   191.10ns    5.23M
1939 // folly_ticket(4thread_1pct_write)                  91.93%   137.83ns    7.26M
1940 // boost_shared(4thread_1pct_write)                  22.74%   557.10ns    1.80M
1941 // pthrd_rwlock(4thread_1pct_write)                  55.66%   227.62ns    4.39M
1942 // ----------------------------------------------------------------------------
1943 // folly_rwspin(8thread_1pct_write)                           169.42ns    5.90M
1944 // shmtx_wr_pri(8thread_1pct_write)                 567.81%    29.84ns   33.51M
1945 // shmtx_w_bare(8thread_1pct_write)                 519.18%    32.63ns   30.64M
1946 // shmtx_rd_pri(8thread_1pct_write)                 172.36%    98.30ns   10.17M
1947 // shmtx_r_bare(8thread_1pct_write)                  75.56%   224.21ns    4.46M
1948 // folly_ticket(8thread_1pct_write)                 104.03%   162.85ns    6.14M
1949 // boost_shared(8thread_1pct_write)                  22.01%   769.73ns    1.30M
1950 // pthrd_rwlock(8thread_1pct_write)                  71.79%   235.99ns    4.24M
1951 // ----------------------------------------------------------------------------
1952 // folly_rwspin(16thread_1pct_write)                          385.88ns    2.59M
1953 // shmtx_wr_pri(16thread_1pct_write)               1039.03%    37.14ns   26.93M
1954 // shmtx_w_bare(16thread_1pct_write)                997.26%    38.69ns   25.84M
1955 // shmtx_rd_pri(16thread_1pct_write)                263.60%   146.39ns    6.83M
1956 // shmtx_r_bare(16thread_1pct_write)                173.16%   222.85ns    4.49M
1957 // folly_ticket(16thread_1pct_write)                179.37%   215.13ns    4.65M
1958 // boost_shared(16thread_1pct_write)                 26.95%     1.43us  698.42K
1959 // pthrd_rwlock(16thread_1pct_write)                166.70%   231.48ns    4.32M
1960 // ----------------------------------------------------------------------------
1961 // folly_rwspin(32thread_1pct_write)                          382.49ns    2.61M
1962 // shmtx_wr_pri(32thread_1pct_write)               1046.64%    36.54ns   27.36M
1963 // shmtx_w_bare(32thread_1pct_write)                922.87%    41.45ns   24.13M
1964 // shmtx_rd_pri(32thread_1pct_write)                251.93%   151.82ns    6.59M
1965 // shmtx_r_bare(32thread_1pct_write)                176.44%   216.78ns    4.61M
1966 // folly_ticket(32thread_1pct_write)                131.07%   291.82ns    3.43M
1967 // boost_shared(32thread_1pct_write)                 12.77%     2.99us  333.95K
1968 // pthrd_rwlock(32thread_1pct_write)                173.43%   220.55ns    4.53M
1969 // ----------------------------------------------------------------------------
1970 // folly_rwspin(64thread_1pct_write)                          510.54ns    1.96M
1971 // shmtx_wr_pri(64thread_1pct_write)               1378.27%    37.04ns   27.00M
1972 // shmtx_w_bare(64thread_1pct_write)               1178.24%    43.33ns   23.08M
1973 // shmtx_rd_pri(64thread_1pct_write)                325.29%   156.95ns    6.37M
1974 // shmtx_r_bare(64thread_1pct_write)                247.82%   206.02ns    4.85M
1975 // folly_ticket(64thread_1pct_write)                117.87%   433.13ns    2.31M
1976 // boost_shared(64thread_1pct_write)                  9.45%     5.40us  185.09K
1977 // pthrd_rwlock(64thread_1pct_write)                236.72%   215.68ns    4.64M
1978 // ----------------------------------------------------------------------------
1979 // folly_rwspin(2thr_2lock_50pct_write)                        10.85ns   92.15M
1980 // shmtx_wr_pri(2thr_2lock_50pct_write)              81.73%    13.28ns   75.32M
1981 // shmtx_rd_pri(2thr_2lock_50pct_write)              81.82%    13.26ns   75.40M
1982 // folly_rwspin(4thr_4lock_50pct_write)                         5.29ns  188.90M
1983 // shmtx_wr_pri(4thr_4lock_50pct_write)              80.89%     6.54ns  152.80M
1984 // shmtx_rd_pri(4thr_4lock_50pct_write)              81.07%     6.53ns  153.14M
1985 // folly_rwspin(8thr_8lock_50pct_write)                         2.63ns  380.57M
1986 // shmtx_wr_pri(8thr_8lock_50pct_write)              80.56%     3.26ns  306.57M
1987 // shmtx_rd_pri(8thr_8lock_50pct_write)              80.29%     3.27ns  305.54M
1988 // folly_rwspin(16thr_16lock_50pct_write)                       1.31ns  764.70M
1989 // shmtx_wr_pri(16thr_16lock_50pct_write)            79.32%     1.65ns  606.54M
1990 // shmtx_rd_pri(16thr_16lock_50pct_write)            79.62%     1.64ns  608.84M
1991 // folly_rwspin(32thr_32lock_50pct_write)                       1.20ns  836.75M
1992 // shmtx_wr_pri(32thr_32lock_50pct_write)            91.67%     1.30ns  767.07M
1993 // shmtx_rd_pri(32thr_32lock_50pct_write)            92.00%     1.30ns  769.82M
1994 // folly_rwspin(64thr_64lock_50pct_write)                       1.39ns  717.80M
1995 // shmtx_wr_pri(64thr_64lock_50pct_write)            93.21%     1.49ns  669.08M
1996 // shmtx_rd_pri(64thr_64lock_50pct_write)            92.49%     1.51ns  663.89M
1997 // ----------------------------------------------------------------------------
1998 // folly_rwspin(2thr_2lock_10pct_write)                        10.24ns   97.70M
1999 // shmtx_wr_pri(2thr_2lock_10pct_write)              76.46%    13.39ns   74.70M
2000 // shmtx_rd_pri(2thr_2lock_10pct_write)              76.35%    13.41ns   74.60M
2001 // folly_rwspin(4thr_4lock_10pct_write)                         5.02ns  199.03M
2002 // shmtx_wr_pri(4thr_4lock_10pct_write)              75.83%     6.63ns  150.91M
2003 // shmtx_rd_pri(4thr_4lock_10pct_write)              76.10%     6.60ns  151.46M
2004 // folly_rwspin(8thr_8lock_10pct_write)                         2.47ns  405.50M
2005 // shmtx_wr_pri(8thr_8lock_10pct_write)              74.54%     3.31ns  302.27M
2006 // shmtx_rd_pri(8thr_8lock_10pct_write)              74.85%     3.29ns  303.52M
2007 // folly_rwspin(16thr_16lock_10pct_write)                       1.22ns  818.68M
2008 // shmtx_wr_pri(16thr_16lock_10pct_write)            73.35%     1.67ns  600.47M
2009 // shmtx_rd_pri(16thr_16lock_10pct_write)            73.38%     1.66ns  600.73M
2010 // folly_rwspin(32thr_32lock_10pct_write)                       1.21ns  827.95M
2011 // shmtx_wr_pri(32thr_32lock_10pct_write)            96.13%     1.26ns  795.89M
2012 // shmtx_rd_pri(32thr_32lock_10pct_write)            96.01%     1.26ns  794.95M
2013 // folly_rwspin(64thr_64lock_10pct_write)                       1.40ns  716.17M
2014 // shmtx_wr_pri(64thr_64lock_10pct_write)            96.91%     1.44ns  694.03M
2015 // shmtx_rd_pri(64thr_64lock_10pct_write)            96.85%     1.44ns  693.64M
2016 // ----------------------------------------------------------------------------
2017 // folly_rwspin(2thr_2lock_1pct_write)                         10.11ns   98.91M
2018 // shmtx_wr_pri(2thr_2lock_1pct_write)               75.07%    13.47ns   74.25M
2019 // shmtx_rd_pri(2thr_2lock_1pct_write)               74.98%    13.48ns   74.16M
2020 // folly_rwspin(4thr_4lock_1pct_write)                          4.96ns  201.77M
2021 // shmtx_wr_pri(4thr_4lock_1pct_write)               74.59%     6.64ns  150.49M
2022 // shmtx_rd_pri(4thr_4lock_1pct_write)               74.60%     6.64ns  150.51M
2023 // folly_rwspin(8thr_8lock_1pct_write)                          2.44ns  410.42M
2024 // shmtx_wr_pri(8thr_8lock_1pct_write)               73.68%     3.31ns  302.41M
2025 // shmtx_rd_pri(8thr_8lock_1pct_write)               73.38%     3.32ns  301.16M
2026 // folly_rwspin(16thr_16lock_1pct_write)                        1.21ns  827.53M
2027 // shmtx_wr_pri(16thr_16lock_1pct_write)             72.11%     1.68ns  596.74M
2028 // shmtx_rd_pri(16thr_16lock_1pct_write)             72.23%     1.67ns  597.73M
2029 // folly_rwspin(32thr_32lock_1pct_write)                        1.22ns  819.53M
2030 // shmtx_wr_pri(32thr_32lock_1pct_write)             98.17%     1.24ns  804.50M
2031 // shmtx_rd_pri(32thr_32lock_1pct_write)             98.21%     1.24ns  804.86M
2032 // folly_rwspin(64thr_64lock_1pct_write)                        1.41ns  710.26M
2033 // shmtx_wr_pri(64thr_64lock_1pct_write)             97.81%     1.44ns  694.71M
2034 // shmtx_rd_pri(64thr_64lock_1pct_write)             99.44%     1.42ns  706.28M
2035 // ----------------------------------------------------------------------------
2036 // ----------------------------------------------------------------------------
2037 // folly_rwspin_ping_pong(burn0)                              641.24ns    1.56M
2038 // shmtx_w_bare_ping_pong(burn0)                     91.07%   704.12ns    1.42M
2039 // shmtx_r_bare_ping_pong(burn0)                     78.70%   814.84ns    1.23M
2040 // folly_ticket_ping_pong(burn0)                     85.67%   748.53ns    1.34M
2041 // boost_shared_ping_pong(burn0)                      5.58%    11.50us   86.96K
2042 // pthrd_rwlock_ping_pong(burn0)                      8.81%     7.28us  137.40K
2043 // ----------------------------------------------------------------------------
2044 // folly_rwspin_ping_pong(burn100k)                           678.97ns    1.47M
2045 // shmtx_w_bare_ping_pong(burn100k)                  99.73%   680.78ns    1.47M
2046 // shmtx_r_bare_ping_pong(burn100k)                  98.67%   688.13ns    1.45M
2047 // folly_ticket_ping_pong(burn100k)                  99.31%   683.68ns    1.46M
2048 // boost_shared_ping_pong(burn100k)                  58.23%     1.17us  857.64K
2049 // pthrd_rwlock_ping_pong(burn100k)                  57.43%     1.18us  845.86K
2050 // ----------------------------------------------------------------------------
2051 // folly_rwspin_ping_pong(burn300k)                             2.03us  492.99K
2052 // shmtx_w_bare_ping_pong(burn300k)                  99.98%     2.03us  492.88K
2053 // shmtx_r_bare_ping_pong(burn300k)                  99.94%     2.03us  492.68K
2054 // folly_ticket_ping_pong(burn300k)                  99.88%     2.03us  492.40K
2055 // boost_shared_ping_pong(burn300k)                  81.43%     2.49us  401.47K
2056 // pthrd_rwlock_ping_pong(burn300k)                  83.22%     2.44us  410.29K
2057 // ----------------------------------------------------------------------------
2058 // folly_rwspin_ping_pong(burn1M)                             677.07ns    1.48M
2059 // shmtx_w_bare_ping_pong(burn1M)                   100.50%   673.74ns    1.48M
2060 // shmtx_r_bare_ping_pong(burn1M)                   100.14%   676.12ns    1.48M
2061 // folly_ticket_ping_pong(burn1M)                   100.44%   674.14ns    1.48M
2062 // boost_shared_ping_pong(burn1M)                    93.04%   727.72ns    1.37M
2063 // pthrd_rwlock_ping_pong(burn1M)                    94.52%   716.30ns    1.40M
2064 // ============================================================================
2065
2066 int main(int argc, char** argv) {
2067   (void)folly_rwspin_reads;
2068   (void)shmtx_wr_pri_reads;
2069   (void)shmtx_w_bare_reads;
2070   (void)shmtx_rd_pri_reads;
2071   (void)shmtx_r_bare_reads;
2072   (void)folly_ticket_reads;
2073   (void)boost_shared_reads;
2074   (void)pthrd_rwlock_reads;
2075   (void)folly_rwspin;
2076   (void)shmtx_wr_pri;
2077   (void)shmtx_w_bare;
2078   (void)shmtx_rd_pri;
2079   (void)shmtx_r_bare;
2080   (void)folly_ticket;
2081   (void)boost_shared;
2082   (void)pthrd_rwlock;
2083   (void)pthrd_mutex_;
2084   (void)folly_rwspin_ping_pong;
2085   (void)shmtx_w_bare_ping_pong;
2086   (void)shmtx_r_bare_ping_pong;
2087   (void)folly_ticket_ping_pong;
2088   (void)boost_shared_ping_pong;
2089   (void)pthrd_rwlock_ping_pong;
2090
2091   testing::InitGoogleTest(&argc, argv);
2092   gflags::ParseCommandLineFlags(&argc, &argv, true);
2093   int rv = RUN_ALL_TESTS();
2094   folly::runBenchmarksOnFlag();
2095   return rv;
2096 }