proxygen
SharedMutexTest.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2015-present 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 #include <folly/SharedMutex.h>
17 
18 #include <stdlib.h>
19 #include <thread>
20 #include <vector>
21 
22 #include <boost/optional.hpp>
23 #include <boost/thread/shared_mutex.hpp>
24 
25 #include <folly/Benchmark.h>
26 #include <folly/MPMCQueue.h>
31 #include <folly/test/TestUtils.h>
32 
33 using namespace folly;
34 using namespace folly::test;
35 using namespace std;
36 using namespace std::chrono;
37 
43 
44 template <typename Lock>
45 void runBasicTest() {
46  Lock lock;
47  SharedMutexToken token1;
48  SharedMutexToken token2;
49  SharedMutexToken token3;
50 
51  EXPECT_TRUE(lock.try_lock());
52  EXPECT_FALSE(lock.try_lock());
53  EXPECT_FALSE(lock.try_lock_shared(token1));
54  lock.unlock();
55 
56  EXPECT_TRUE(lock.try_lock_shared(token1));
57  EXPECT_FALSE(lock.try_lock());
58  EXPECT_TRUE(lock.try_lock_shared(token2));
59  lock.lock_shared(token3);
60  lock.unlock_shared(token3);
61  lock.unlock_shared(token2);
62  lock.unlock_shared(token1);
63 
64  lock.lock();
65  lock.unlock();
66 
67  lock.lock_shared(token1);
68  lock.lock_shared(token2);
69  lock.unlock_shared(token1);
70  lock.unlock_shared(token2);
71 
72  lock.lock();
73  lock.unlock_and_lock_shared(token1);
74  lock.lock_shared(token2);
75  lock.unlock_shared(token2);
76  lock.unlock_shared(token1);
77 }
78 
80  runBasicTest<SharedMutexReadPriority>();
81  runBasicTest<SharedMutexWritePriority>();
82  runBasicTest<SharedMutexSuppressTSAN>();
83 }
84 
85 template <typename Lock>
87  Lock lock;
88  SharedMutexToken token;
89 
90  {
91  // create an exclusive write lock via holder
92  typename Lock::WriteHolder holder(lock);
93  EXPECT_FALSE(lock.try_lock());
94  EXPECT_FALSE(lock.try_lock_shared(token));
95 
96  // move ownership to another write holder via move constructor
97  typename Lock::WriteHolder holder2(std::move(holder));
98  EXPECT_FALSE(lock.try_lock());
99  EXPECT_FALSE(lock.try_lock_shared(token));
100 
101  // move ownership to another write holder via assign operator
102  typename Lock::WriteHolder holder3(nullptr);
103  holder3 = std::move(holder2);
104  EXPECT_FALSE(lock.try_lock());
105  EXPECT_FALSE(lock.try_lock_shared(token));
106 
107  // downgrade from exclusive to upgrade lock via move constructor
108  typename Lock::UpgradeHolder holder4(std::move(holder3));
109 
110  // ensure we can lock from a shared source
111  EXPECT_FALSE(lock.try_lock());
112  EXPECT_TRUE(lock.try_lock_shared(token));
113  lock.unlock_shared(token);
114 
115  // promote from upgrade to exclusive lock via move constructor
116  typename Lock::WriteHolder holder5(std::move(holder4));
117  EXPECT_FALSE(lock.try_lock());
118  EXPECT_FALSE(lock.try_lock_shared(token));
119 
120  // downgrade exclusive to shared lock via move constructor
121  typename Lock::ReadHolder holder6(std::move(holder5));
122 
123  // ensure we can lock from another shared source
124  EXPECT_FALSE(lock.try_lock());
125  EXPECT_TRUE(lock.try_lock_shared(token));
126  lock.unlock_shared(token);
127  }
128 
129  {
130  typename Lock::WriteHolder holder(lock);
131  EXPECT_FALSE(lock.try_lock());
132  }
133 
134  {
135  typename Lock::ReadHolder holder(lock);
136  typename Lock::ReadHolder holder2(lock);
137  typename Lock::UpgradeHolder holder3(lock);
138  }
139 
140  {
141  typename Lock::UpgradeHolder holder(lock);
142  typename Lock::ReadHolder holder2(lock);
143  typename Lock::ReadHolder holder3(std::move(holder));
144  }
145 }
146 
147 TEST(SharedMutex, basic_holders) {
148  runBasicHoldersTest<SharedMutexReadPriority>();
149  runBasicHoldersTest<SharedMutexWritePriority>();
150  runBasicHoldersTest<SharedMutexSuppressTSAN>();
151 }
152 
153 template <typename Lock>
155  Lock lock;
156 
157  vector<SharedMutexToken> tokens;
158  for (int i = 0; i < 1000; ++i) {
159  tokens.emplace_back();
160  EXPECT_TRUE(lock.try_lock_shared(tokens.back()));
161  }
162  for (auto& token : tokens) {
163  lock.unlock_shared(token);
164  }
165  EXPECT_TRUE(lock.try_lock());
166  lock.unlock();
167 }
168 
169 TEST(SharedMutex, many_read_locks_with_tokens) {
170  // This test fails in an assertion in the TSAN library because there are too
171  // many mutexes
173  runManyReadLocksTestWithTokens<SharedMutexReadPriority>();
174  runManyReadLocksTestWithTokens<SharedMutexWritePriority>();
175  runManyReadLocksTestWithTokens<SharedMutexSuppressTSAN>();
176 }
177 
178 template <typename Lock>
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  // This test fails in an assertion in the TSAN library because there are too
194  // many mutexes
196  runManyReadLocksTestWithoutTokens<SharedMutexReadPriority>();
197  runManyReadLocksTestWithoutTokens<SharedMutexWritePriority>();
198  runManyReadLocksTestWithoutTokens<SharedMutexSuppressTSAN>();
199 }
200 
201 template <typename Lock>
203  Lock lock;
204 
205  EXPECT_TRUE(lock.try_lock_for(milliseconds(0)));
206  lock.unlock();
207  EXPECT_TRUE(lock.try_lock_for(milliseconds(-1)));
208  lock.unlock();
209  EXPECT_TRUE(lock.try_lock_shared_for(milliseconds(0)));
210  lock.unlock_shared();
211  EXPECT_TRUE(lock.try_lock_shared_for(milliseconds(-1)));
212  lock.unlock_shared();
213  EXPECT_TRUE(lock.try_lock_until(system_clock::now() - milliseconds(1)));
214  lock.unlock();
215  EXPECT_TRUE(
216  lock.try_lock_shared_until(system_clock::now() - milliseconds(1)));
217  lock.unlock_shared();
218  EXPECT_TRUE(lock.try_lock_until(steady_clock::now() - milliseconds(1)));
219  lock.unlock();
220  EXPECT_TRUE(
221  lock.try_lock_shared_until(steady_clock::now() - milliseconds(1)));
222  lock.unlock_shared();
223 }
224 
225 TEST(SharedMutex, timeout_in_past) {
226  runTimeoutInPastTest<SharedMutexReadPriority>();
227  runTimeoutInPastTest<SharedMutexWritePriority>();
228  runTimeoutInPastTest<SharedMutexSuppressTSAN>();
229 }
230 
231 template <class Func>
232 bool funcHasDuration(milliseconds expectedDuration, Func func) {
233  // elapsed time should eventually fall within expectedDuration +- 25%
234  for (int tries = 0; tries < 100; ++tries) {
235  auto start = steady_clock::now();
236  func();
237  auto elapsed = steady_clock::now() - start;
238  if (elapsed > expectedDuration - expectedDuration / 4 &&
239  elapsed < expectedDuration + expectedDuration / 4) {
240  return true;
241  }
242  }
243  return false;
244 }
245 
246 template <typename Lock>
248  Lock lock;
249  lock.lock();
250  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
251  EXPECT_FALSE(lock.try_lock_for(milliseconds(10)));
252  }));
253  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
254  typename Lock::Token token;
255  EXPECT_FALSE(lock.try_lock_shared_for(milliseconds(10), token));
256  }));
257  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
258  EXPECT_FALSE(lock.try_lock_upgrade_for(milliseconds(10)));
259  }));
260  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
261  EXPECT_FALSE(lock.try_lock_until(steady_clock::now() + milliseconds(10)));
262  }));
263  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
264  typename Lock::Token token;
265  EXPECT_FALSE(lock.try_lock_shared_until(
266  steady_clock::now() + milliseconds(10), token));
267  }));
268  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
269  EXPECT_FALSE(
270  lock.try_lock_upgrade_until(steady_clock::now() + milliseconds(10)));
271  }));
272  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
273  EXPECT_FALSE(lock.try_lock_until(system_clock::now() + milliseconds(10)));
274  }));
275  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
276  typename Lock::Token token;
277  EXPECT_FALSE(lock.try_lock_shared_until(
278  system_clock::now() + milliseconds(10), token));
279  }));
280  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
281  EXPECT_FALSE(
282  lock.try_lock_upgrade_until(system_clock::now() + milliseconds(10)));
283  }));
284  lock.unlock();
285 
286  lock.lock_shared();
287  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
288  EXPECT_FALSE(lock.try_lock_for(milliseconds(10)));
289  }));
290  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
291  EXPECT_FALSE(lock.try_lock_until(steady_clock::now() + milliseconds(10)));
292  }));
293  EXPECT_TRUE(funcHasDuration(milliseconds(10), [&] {
294  EXPECT_FALSE(lock.try_lock_until(system_clock::now() + milliseconds(10)));
295  }));
296  lock.unlock_shared();
297 
298  lock.lock();
299  for (int p = 0; p < 8; ++p) {
300  EXPECT_FALSE(lock.try_lock_for(nanoseconds(1 << p)));
301  }
302  lock.unlock();
303 
304  for (int p = 0; p < 8; ++p) {
305  typename Lock::ReadHolder holder1(lock);
306  typename Lock::ReadHolder holder2(lock);
307  typename Lock::ReadHolder holder3(lock);
308  EXPECT_FALSE(lock.try_lock_for(nanoseconds(1 << p)));
309  }
310 }
311 
312 TEST(SharedMutex, failing_try_timeout) {
313  runFailingTryTimeoutTest<SharedMutexReadPriority>();
314  runFailingTryTimeoutTest<SharedMutexWritePriority>();
315  runFailingTryTimeoutTest<SharedMutexSuppressTSAN>();
316 }
317 
318 template <typename Lock>
320  Lock lock;
321  typename Lock::Token token1;
322  typename Lock::Token token2;
323 
324  lock.lock_upgrade();
325  EXPECT_FALSE(lock.try_lock());
326  EXPECT_TRUE(lock.try_lock_shared(token1));
327  lock.unlock_shared(token1);
328  lock.unlock_upgrade();
329 
330  lock.lock_upgrade();
331  lock.unlock_upgrade_and_lock();
332  EXPECT_FALSE(lock.try_lock_shared(token1));
333  lock.unlock();
334 
335  lock.lock_upgrade();
336  lock.unlock_upgrade_and_lock_shared(token1);
337  lock.lock_upgrade();
338  lock.unlock_upgrade_and_lock_shared(token2);
339  lock.unlock_shared(token1);
340  lock.unlock_shared(token2);
341 
342  lock.lock();
343  lock.unlock_and_lock_upgrade();
344  EXPECT_TRUE(lock.try_lock_shared(token1));
345  lock.unlock_upgrade();
346  lock.unlock_shared(token1);
347 }
348 
349 TEST(SharedMutex, basic_upgrade_tests) {
350  runBasicUpgradeTest<SharedMutexReadPriority>();
351  runBasicUpgradeTest<SharedMutexWritePriority>();
352  runBasicUpgradeTest<SharedMutexSuppressTSAN>();
353 }
354 
355 TEST(SharedMutex, read_has_prio) {
357  SharedMutexToken token1;
358  SharedMutexToken token2;
359  lock.lock_shared(token1);
360  bool exclusiveAcquired = false;
361  auto writer = thread([&] {
362  lock.lock();
363  exclusiveAcquired = true;
364  lock.unlock();
365  });
366 
367  // lock() can't complete until we unlock token1, but it should stake
368  // its claim with regards to other exclusive or upgrade locks. We can
369  // use try_lock_upgrade to poll for that eventuality.
370  while (lock.try_lock_upgrade()) {
371  lock.unlock_upgrade();
373  }
374  EXPECT_FALSE(exclusiveAcquired);
375 
376  // Even though lock() is stuck we should be able to get token2
377  EXPECT_TRUE(lock.try_lock_shared(token2));
378  lock.unlock_shared(token1);
379  lock.unlock_shared(token2);
380  writer.join();
381  EXPECT_TRUE(exclusiveAcquired);
382 }
383 
384 TEST(SharedMutex, write_has_prio) {
386  SharedMutexToken token1;
387  SharedMutexToken token2;
388  lock.lock_shared(token1);
389  auto writer = thread([&] {
390  lock.lock();
391  lock.unlock();
392  });
393 
394  // eventually lock() should block readers
395  while (lock.try_lock_shared(token2)) {
396  lock.unlock_shared(token2);
398  }
399 
400  lock.unlock_shared(token1);
401  writer.join();
402 }
403 
404 struct TokenLocker {
406 
407  template <typename T>
408  void lock(T* lockable) {
409  lockable->lock();
410  }
411 
412  template <typename T>
413  void unlock(T* lockable) {
414  lockable->unlock();
415  }
416 
417  template <typename T>
418  void lock_shared(T* lockable) {
419  lockable->lock_shared(token);
420  }
421 
422  template <typename T>
423  void unlock_shared(T* lockable) {
424  lockable->unlock_shared(token);
425  }
426 };
427 
428 struct Locker {
429  template <typename T>
430  void lock(T* lockable) {
431  lockable->lock();
432  }
433 
434  template <typename T>
435  void unlock(T* lockable) {
436  lockable->unlock();
437  }
438 
439  template <typename T>
440  void lock_shared(T* lockable) {
441  lockable->lock_shared();
442  }
443 
444  template <typename T>
445  void unlock_shared(T* lockable) {
446  lockable->unlock_shared();
447  }
448 };
449 
450 struct EnterLocker {
451  template <typename T>
452  void lock(T* lockable) {
453  lockable->lock(0);
454  }
455 
456  template <typename T>
457  void unlock(T* lockable) {
458  lockable->unlock();
459  }
460 
461  template <typename T>
462  void lock_shared(T* lockable) {
463  lockable->enter(0);
464  }
465 
466  template <typename T>
467  void unlock_shared(T* lockable) {
468  lockable->leave();
469  }
470 };
471 
472 struct PosixRWLock {
473  pthread_rwlock_t lock_;
474 
476  pthread_rwlock_init(&lock_, nullptr);
477  }
478 
480  pthread_rwlock_destroy(&lock_);
481  }
482 
483  void lock() {
484  pthread_rwlock_wrlock(&lock_);
485  }
486 
487  void unlock() {
488  pthread_rwlock_unlock(&lock_);
489  }
490 
491  void lock_shared() {
492  pthread_rwlock_rdlock(&lock_);
493  }
494 
495  void unlock_shared() {
496  pthread_rwlock_unlock(&lock_);
497  }
498 };
499 
500 struct PosixMutex {
501  pthread_mutex_t lock_;
502 
504  pthread_mutex_init(&lock_, nullptr);
505  }
506 
508  pthread_mutex_destroy(&lock_);
509  }
510 
511  void lock() {
512  pthread_mutex_lock(&lock_);
513  }
514 
515  void unlock() {
516  pthread_mutex_unlock(&lock_);
517  }
518 
519  void lock_shared() {
520  pthread_mutex_lock(&lock_);
521  }
522 
523  void unlock_shared() {
524  pthread_mutex_unlock(&lock_);
525  }
526 };
527 
528 template <template <typename> class Atom, typename Lock, typename Locker>
529 static void
530 runContendedReaders(size_t numOps, size_t numThreads, bool useSeparateLocks) {
531  char padding1[64];
532  (void)padding1;
533  Lock globalLock;
534  int valueProtectedByLock = 10;
535  char padding2[64];
536  (void)padding2;
537  Atom<bool> go(false);
538  Atom<bool>* goPtr = &go; // workaround for clang bug
539  vector<thread> threads(numThreads);
540 
542  for (size_t t = 0; t < numThreads; ++t) {
543  threads[t] = DSched::thread([&, t, numThreads] {
544  Lock privateLock;
545  Lock* lock = useSeparateLocks ? &privateLock : &globalLock;
546  Locker locker;
547  while (!goPtr->load()) {
549  }
550  for (size_t op = t; op < numOps; op += numThreads) {
551  locker.lock_shared(lock);
552  // note: folly::doNotOptimizeAway reads and writes to its arg,
553  // so the following two lines are very different than a call
554  // to folly::doNotOptimizeAway(valueProtectedByLock);
555  auto copy = valueProtectedByLock;
557  locker.unlock_shared(lock);
558  }
559  });
560  }
561  }
562 
563  go.store(true);
564  for (auto& thr : threads) {
565  DSched::join(thr);
566  }
567 }
568 
569 static void
570 folly_rwspin_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks) {
571  runContendedReaders<atomic, RWSpinLock, Locker>(
572  numOps, numThreads, useSeparateLocks);
573 }
574 
575 static void
576 shmtx_wr_pri_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks) {
577  runContendedReaders<atomic, SharedMutexWritePriority, TokenLocker>(
578  numOps, numThreads, useSeparateLocks);
579 }
580 
581 static void
582 shmtx_w_bare_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks) {
583  runContendedReaders<atomic, SharedMutexWritePriority, Locker>(
584  numOps, numThreads, useSeparateLocks);
585 }
586 
587 static void
588 shmtx_rd_pri_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks) {
589  runContendedReaders<atomic, SharedMutexReadPriority, TokenLocker>(
590  numOps, numThreads, useSeparateLocks);
591 }
592 
593 static void
594 shmtx_r_bare_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks) {
595  runContendedReaders<atomic, SharedMutexReadPriority, Locker>(
596  numOps, numThreads, useSeparateLocks);
597 }
598 
599 static void
600 folly_ticket_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks) {
601  runContendedReaders<atomic, RWTicketSpinLock64, Locker>(
602  numOps, numThreads, useSeparateLocks);
603 }
604 
605 static void
606 boost_shared_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks) {
607  runContendedReaders<atomic, boost::shared_mutex, Locker>(
608  numOps, numThreads, useSeparateLocks);
609 }
610 
611 static void
612 pthrd_rwlock_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks) {
613  runContendedReaders<atomic, PosixRWLock, Locker>(
614  numOps, numThreads, useSeparateLocks);
615 }
616 
617 template <template <typename> class Atom, typename Lock, typename Locker>
618 static void runMixed(
619  size_t numOps,
620  size_t numThreads,
621  double writeFraction,
622  bool useSeparateLocks) {
623  char padding1[64];
624  (void)padding1;
625  Lock globalLock;
626  int valueProtectedByLock = 0;
627  char padding2[64];
628  (void)padding2;
629  Atom<bool> go(false);
630  Atom<bool>* goPtr = &go; // workaround for clang bug
631  vector<thread> threads(numThreads);
632 
634  for (size_t t = 0; t < numThreads; ++t) {
635  threads[t] = DSched::thread([&, t, numThreads] {
636  struct drand48_data buffer;
637  srand48_r(t, &buffer);
638  long writeThreshold = writeFraction * 0x7fffffff;
639  Lock privateLock;
640  Lock* lock = useSeparateLocks ? &privateLock : &globalLock;
641  Locker locker;
642  while (!goPtr->load()) {
644  }
645  for (size_t op = t; op < numOps; op += numThreads) {
646  long randVal;
647  lrand48_r(&buffer, &randVal);
648  bool writeOp = randVal < writeThreshold;
649  if (writeOp) {
650  locker.lock(lock);
651  if (!useSeparateLocks) {
652  ++valueProtectedByLock;
653  }
654  locker.unlock(lock);
655  } else {
656  locker.lock_shared(lock);
657  auto v = valueProtectedByLock;
659  locker.unlock_shared(lock);
660  }
661  }
662  });
663  }
664  }
665 
666  go.store(true);
667  for (auto& thr : threads) {
668  DSched::join(thr);
669  }
670 }
671 
672 static void folly_rwspin(
673  size_t numOps,
674  size_t numThreads,
675  double writeFraction,
676  bool useSeparateLocks) {
677  runMixed<atomic, RWSpinLock, Locker>(
678  numOps, numThreads, writeFraction, useSeparateLocks);
679 }
680 
681 static void shmtx_wr_pri(
682  uint32_t numOps,
683  size_t numThreads,
684  double writeFraction,
685  bool useSeparateLocks) {
686  runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
687  numOps, numThreads, writeFraction, useSeparateLocks);
688 }
689 
690 static void shmtx_w_bare(
691  uint32_t numOps,
692  size_t numThreads,
693  double writeFraction,
694  bool useSeparateLocks) {
695  runMixed<atomic, SharedMutexWritePriority, Locker>(
696  numOps, numThreads, writeFraction, useSeparateLocks);
697 }
698 
699 static void shmtx_rd_pri(
700  uint32_t numOps,
701  size_t numThreads,
702  double writeFraction,
703  bool useSeparateLocks) {
704  runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
705  numOps, numThreads, writeFraction, useSeparateLocks);
706 }
707 
708 static void shmtx_r_bare(
709  uint32_t numOps,
710  size_t numThreads,
711  double writeFraction,
712  bool useSeparateLocks) {
713  runMixed<atomic, SharedMutexReadPriority, Locker>(
714  numOps, numThreads, writeFraction, useSeparateLocks);
715 }
716 
717 static void folly_ticket(
718  size_t numOps,
719  size_t numThreads,
720  double writeFraction,
721  bool useSeparateLocks) {
722  runMixed<atomic, RWTicketSpinLock64, Locker>(
723  numOps, numThreads, writeFraction, useSeparateLocks);
724 }
725 
726 static void boost_shared(
727  size_t numOps,
728  size_t numThreads,
729  double writeFraction,
730  bool useSeparateLocks) {
731  runMixed<atomic, boost::shared_mutex, Locker>(
732  numOps, numThreads, writeFraction, useSeparateLocks);
733 }
734 
735 static void pthrd_rwlock(
736  size_t numOps,
737  size_t numThreads,
738  double writeFraction,
739  bool useSeparateLocks) {
740  runMixed<atomic, PosixRWLock, Locker>(
741  numOps, numThreads, writeFraction, useSeparateLocks);
742 }
743 
744 static void pthrd_mutex_(
745  size_t numOps,
746  size_t numThreads,
747  double writeFraction,
748  bool useSeparateLocks) {
749  runMixed<atomic, PosixMutex, Locker>(
750  numOps, numThreads, writeFraction, useSeparateLocks);
751 }
752 
753 template <typename Lock, template <typename> class Atom>
754 static void runAllAndValidate(size_t numOps, size_t numThreads) {
755  Lock globalLock;
756  Atom<int> globalExclusiveCount(0);
757  Atom<int> globalUpgradeCount(0);
758  Atom<int> globalSharedCount(0);
759 
760  Atom<bool> go(false);
761 
762  // clang crashes on access to Atom<> captured by ref in closure
763  Atom<int>* globalExclusiveCountPtr = &globalExclusiveCount;
764  Atom<int>* globalUpgradeCountPtr = &globalUpgradeCount;
765  Atom<int>* globalSharedCountPtr = &globalSharedCount;
766  Atom<bool>* goPtr = &go;
767 
768  vector<thread> threads(numThreads);
769 
771  for (size_t t = 0; t < numThreads; ++t) {
772  threads[t] = DSched::thread([&, t, numThreads] {
773  struct drand48_data buffer;
774  srand48_r(t, &buffer);
775 
776  bool exclusive = false;
777  bool upgrade = false;
778  bool shared = false;
779  bool ourGlobalTokenUsed = false;
780  SharedMutexToken ourGlobalToken;
781 
782  Lock privateLock;
783  vector<SharedMutexToken> privateTokens;
784 
785  while (!goPtr->load()) {
787  }
788  for (size_t op = t; op < numOps; op += numThreads) {
789  // randVal in [0,1000)
790  long randVal;
791  lrand48_r(&buffer, &randVal);
792  randVal = (long)((randVal * (uint64_t)1000) / 0x7fffffff);
793 
794  // make as many assertions as possible about the global state
795  if (exclusive) {
796  EXPECT_EQ(1, globalExclusiveCountPtr->load(memory_order_acquire));
797  EXPECT_EQ(0, globalUpgradeCountPtr->load(memory_order_acquire));
798  EXPECT_EQ(0, globalSharedCountPtr->load(memory_order_acquire));
799  }
800  if (upgrade) {
801  EXPECT_EQ(0, globalExclusiveCountPtr->load(memory_order_acquire));
802  EXPECT_EQ(1, globalUpgradeCountPtr->load(memory_order_acquire));
803  }
804  if (shared) {
805  EXPECT_EQ(0, globalExclusiveCountPtr->load(memory_order_acquire));
806  EXPECT_TRUE(globalSharedCountPtr->load(memory_order_acquire) > 0);
807  } else {
808  EXPECT_FALSE(ourGlobalTokenUsed);
809  }
810 
811  // independent 20% chance we do something to the private lock
812  if (randVal < 200) {
813  // it's okay to take multiple private shared locks because
814  // we never take an exclusive lock, so reader versus writer
815  // priority doesn't cause deadlocks
816  if (randVal < 100 && privateTokens.size() > 0) {
817  auto i = randVal % privateTokens.size();
818  privateLock.unlock_shared(privateTokens[i]);
819  privateTokens.erase(privateTokens.begin() + i);
820  } else {
821  SharedMutexToken token;
822  privateLock.lock_shared(token);
823  privateTokens.push_back(token);
824  }
825  continue;
826  }
827 
828  // if we've got a lock, the only thing we can do is release it
829  // or transform it into a different kind of lock
830  if (exclusive) {
831  exclusive = false;
832  --*globalExclusiveCountPtr;
833  if (randVal < 500) {
834  globalLock.unlock();
835  } else if (randVal < 700) {
836  globalLock.unlock_and_lock_shared();
837  ++*globalSharedCountPtr;
838  shared = true;
839  } else if (randVal < 900) {
840  globalLock.unlock_and_lock_shared(ourGlobalToken);
841  ++*globalSharedCountPtr;
842  shared = true;
843  ourGlobalTokenUsed = true;
844  } else {
845  globalLock.unlock_and_lock_upgrade();
846  ++*globalUpgradeCountPtr;
847  upgrade = true;
848  }
849  } else if (upgrade) {
850  upgrade = false;
851  --*globalUpgradeCountPtr;
852  if (randVal < 500) {
853  globalLock.unlock_upgrade();
854  } else if (randVal < 700) {
855  globalLock.unlock_upgrade_and_lock_shared();
856  ++*globalSharedCountPtr;
857  shared = true;
858  } else if (randVal < 900) {
859  globalLock.unlock_upgrade_and_lock_shared(ourGlobalToken);
860  ++*globalSharedCountPtr;
861  shared = true;
862  ourGlobalTokenUsed = true;
863  } else {
864  globalLock.unlock_upgrade_and_lock();
865  ++*globalExclusiveCountPtr;
866  exclusive = true;
867  }
868  } else if (shared) {
869  shared = false;
870  --*globalSharedCountPtr;
871  if (ourGlobalTokenUsed) {
872  globalLock.unlock_shared(ourGlobalToken);
873  ourGlobalTokenUsed = false;
874  } else {
875  globalLock.unlock_shared();
876  }
877  } else if (randVal < 400) {
878  // 40% chance of shared lock with token, 5 ways to get it
879 
880  // delta t goes from -1 millis to 7 millis
881  auto dt = microseconds(10 * (randVal - 100));
882 
883  if (randVal < 400) {
884  globalLock.lock_shared(ourGlobalToken);
885  shared = true;
886  } else if (randVal < 500) {
887  shared = globalLock.try_lock_shared(ourGlobalToken);
888  } else if (randVal < 600) {
889  shared = globalLock.try_lock_shared_for(dt, ourGlobalToken);
890  } else if (randVal < 800) {
891  shared = globalLock.try_lock_shared_until(
892  system_clock::now() + dt, ourGlobalToken);
893  }
894  if (shared) {
895  ourGlobalTokenUsed = true;
896  ++*globalSharedCountPtr;
897  }
898  } else if (randVal < 800) {
899  // 40% chance of shared lock without token
900  auto dt = microseconds(10 * (randVal - 100));
901  if (randVal < 400) {
902  globalLock.lock_shared();
903  shared = true;
904  } else if (randVal < 500) {
905  shared = globalLock.try_lock_shared();
906  } else if (randVal < 600) {
907  shared = globalLock.try_lock_shared_for(dt);
908  } else if (randVal < 800) {
909  shared =
910  globalLock.try_lock_shared_until(system_clock::now() + dt);
911  }
912  if (shared) {
913  ++*globalSharedCountPtr;
914  }
915  } else if (randVal < 900) {
916  // 10% change of upgrade lock
917  globalLock.lock_upgrade();
918  upgrade = true;
919  ++*globalUpgradeCountPtr;
920  } else {
921  // 10% chance of exclusive lock, 5 ways to get it
922 
923  // delta t goes from -1 millis to 9 millis
924  auto dt = microseconds(100 * (randVal - 910));
925 
926  if (randVal < 400) {
927  globalLock.lock();
928  exclusive = true;
929  } else if (randVal < 500) {
930  exclusive = globalLock.try_lock();
931  } else if (randVal < 600) {
932  exclusive = globalLock.try_lock_for(dt);
933  } else if (randVal < 700) {
934  exclusive = globalLock.try_lock_until(steady_clock::now() + dt);
935  } else {
936  exclusive = globalLock.try_lock_until(system_clock::now() + dt);
937  }
938  if (exclusive) {
939  ++*globalExclusiveCountPtr;
940  }
941  }
942  }
943 
944  if (exclusive) {
945  --*globalExclusiveCountPtr;
946  globalLock.unlock();
947  }
948  if (upgrade) {
949  --*globalUpgradeCountPtr;
950  globalLock.unlock_upgrade();
951  }
952  if (shared) {
953  --*globalSharedCountPtr;
954  if (ourGlobalTokenUsed) {
955  globalLock.unlock_shared(ourGlobalToken);
956  ourGlobalTokenUsed = false;
957  } else {
958  globalLock.unlock_shared();
959  }
960  }
961  for (auto& token : privateTokens) {
962  privateLock.unlock_shared(token);
963  }
964  });
965  }
966  }
967 
968  go.store(true);
969  for (auto& thr : threads) {
970  DSched::join(thr);
971  }
972 }
973 
974 TEST(SharedMutex, deterministic_concurrent_readers_of_one_lock_read_prio) {
975  for (int pass = 0; pass < 3; ++pass) {
976  DSched sched(DSched::uniform(pass));
977  runContendedReaders<DeterministicAtomic, DSharedMutexReadPriority, Locker>(
978  1000, 3, false);
979  }
980 }
981 
982 TEST(SharedMutex, deterministic_concurrent_readers_of_one_lock_write_prio) {
983  for (int pass = 0; pass < 3; ++pass) {
984  DSched sched(DSched::uniform(pass));
985  runContendedReaders<DeterministicAtomic, DSharedMutexWritePriority, Locker>(
986  1000, 3, false);
987  }
988 }
989 
990 TEST(SharedMutex, concurrent_readers_of_one_lock_read_prio) {
991  for (int pass = 0; pass < 10; ++pass) {
992  runContendedReaders<atomic, SharedMutexReadPriority, Locker>(
993  100000, 32, false);
994  }
995 }
996 
997 TEST(SharedMutex, concurrent_readers_of_one_lock_write_prio) {
998  for (int pass = 0; pass < 10; ++pass) {
999  runContendedReaders<atomic, SharedMutexWritePriority, Locker>(
1000  100000, 32, false);
1001  }
1002 }
1003 
1004 TEST(SharedMutex, deterministic_readers_of_concurrent_locks_read_prio) {
1005  for (int pass = 0; pass < 3; ++pass) {
1006  DSched sched(DSched::uniform(pass));
1007  runContendedReaders<DeterministicAtomic, DSharedMutexReadPriority, Locker>(
1008  1000, 3, true);
1009  }
1010 }
1011 
1012 TEST(SharedMutex, deterministic_readers_of_concurrent_locks_write_prio) {
1013  for (int pass = 0; pass < 3; ++pass) {
1014  DSched sched(DSched::uniform(pass));
1015  runContendedReaders<DeterministicAtomic, DSharedMutexWritePriority, Locker>(
1016  1000, 3, true);
1017  }
1018 }
1019 
1020 TEST(SharedMutex, readers_of_concurrent_locks_read_prio) {
1021  for (int pass = 0; pass < 10; ++pass) {
1022  runContendedReaders<atomic, SharedMutexReadPriority, TokenLocker>(
1023  100000, 32, true);
1024  }
1025 }
1026 
1027 TEST(SharedMutex, readers_of_concurrent_locks_write_prio) {
1028  for (int pass = 0; pass < 10; ++pass) {
1029  runContendedReaders<atomic, SharedMutexWritePriority, TokenLocker>(
1030  100000, 32, true);
1031  }
1032 }
1033 
1034 TEST(SharedMutex, deterministic_mixed_mostly_read_read_prio) {
1035  for (int pass = 0; pass < 3; ++pass) {
1036  DSched sched(DSched::uniform(pass));
1037  runMixed<DeterministicAtomic, DSharedMutexReadPriority, Locker>(
1038  1000, 3, 0.1, false);
1039  }
1040 }
1041 
1042 TEST(SharedMutex, deterministic_mixed_mostly_read_write_prio) {
1043  for (int pass = 0; pass < 3; ++pass) {
1044  DSched sched(DSched::uniform(pass));
1045  runMixed<DeterministicAtomic, DSharedMutexWritePriority, Locker>(
1046  1000, 3, 0.1, false);
1047  }
1048 }
1049 
1050 TEST(SharedMutex, mixed_mostly_read_read_prio) {
1051  for (int pass = 0; pass < 5; ++pass) {
1052  runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
1053  10000, 32, 0.1, false);
1054  }
1055 }
1056 
1057 TEST(SharedMutex, mixed_mostly_read_write_prio) {
1058  for (int pass = 0; pass < 5; ++pass) {
1059  runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
1060  10000, 32, 0.1, false);
1061  }
1062 }
1063 
1064 TEST(SharedMutex, deterministic_mixed_mostly_write_read_prio) {
1065  for (int pass = 0; pass < 1; ++pass) {
1066  DSched sched(DSched::uniform(pass));
1067  runMixed<DeterministicAtomic, DSharedMutexReadPriority, TokenLocker>(
1068  1000, 10, 0.9, false);
1069  }
1070 }
1071 
1072 TEST(SharedMutex, deterministic_mixed_mostly_write_write_prio) {
1073  for (int pass = 0; pass < 1; ++pass) {
1074  DSched sched(DSched::uniform(pass));
1075  runMixed<DeterministicAtomic, DSharedMutexWritePriority, TokenLocker>(
1076  1000, 10, 0.9, false);
1077  }
1078 }
1079 
1080 TEST(SharedMutex, deterministic_lost_wakeup_write_prio) {
1081  for (int pass = 0; pass < 10; ++pass) {
1082  DSched sched(DSched::uniformSubset(pass, 2, 200));
1083  runMixed<DeterministicAtomic, DSharedMutexWritePriority, TokenLocker>(
1084  1000, 3, 1.0, false);
1085  }
1086 }
1087 
1088 // In TSAN, tests run a lot slower. To avoid test timeouts, adjust the number
1089 // of repetitions we need for tests.
1090 static std::size_t adjustReps(std::size_t reps) {
1092  return reps / 10;
1093  }
1094  return reps;
1095 }
1096 
1097 TEST(SharedMutex, mixed_mostly_write_read_prio) {
1098  for (int pass = 0; pass < (folly::kIsSanitizeAddress ? 1 : 5); ++pass) {
1099  runMixed<atomic, SharedMutexReadPriority, TokenLocker>(
1100  adjustReps(50000), adjustReps(300), 0.9, false);
1101  }
1102 }
1103 
1104 TEST(SharedMutex, mixed_mostly_write_write_prio) {
1105  for (int pass = 0; pass < (folly::kIsSanitizeAddress ? 1 : 5); ++pass) {
1106  runMixed<atomic, SharedMutexWritePriority, TokenLocker>(
1107  adjustReps(50000), adjustReps(300), 0.9, false);
1108  }
1109 }
1110 
1111 TEST(SharedMutex, deterministic_all_ops_read_prio) {
1112  for (int pass = 0; pass < 5; ++pass) {
1113  DSched sched(DSched::uniform(pass));
1114  runAllAndValidate<DSharedMutexReadPriority, DeterministicAtomic>(1000, 8);
1115  }
1116 }
1117 
1118 TEST(SharedMutex, deterministic_all_ops_write_prio) {
1119  // This test fails in TSAN because of noisy lock ordering inversions.
1121  for (int pass = 0; pass < 5; ++pass) {
1122  DSched sched(DSched::uniform(pass));
1123  runAllAndValidate<DSharedMutexWritePriority, DeterministicAtomic>(1000, 8);
1124  }
1125 }
1126 
1127 TEST(SharedMutex, all_ops_read_prio) {
1128  for (int pass = 0; pass < 5; ++pass) {
1129  runAllAndValidate<SharedMutexReadPriority, atomic>(100000, 32);
1130  }
1131 }
1132 
1133 TEST(SharedMutex, all_ops_write_prio) {
1134  // This test fails in TSAN because of noisy lock ordering inversions.
1136  for (int pass = 0; pass < 5; ++pass) {
1137  runAllAndValidate<SharedMutexWritePriority, atomic>(100000, 32);
1138  }
1139 }
1140 
1142  boost::optional<boost::optional<SharedMutexToken>>)
1143 
1144 // Setup is a set of threads that either grab a shared lock, or exclusive
1145 // and then downgrade it, or upgrade then upgrade and downgrade, then
1146 // enqueue the shared lock to a second set of threads that just performs
1147 // unlocks. Half of the shared locks use tokens, the others don't.
1148 template <typename Lock, template <typename> class Atom>
1149 static void runRemoteUnlock(
1150  size_t numOps,
1151  double preWriteFraction,
1152  double preUpgradeFraction,
1153  size_t numSendingThreads,
1154  size_t numReceivingThreads) {
1155  Lock globalLock;
1157  auto queuePtr = &queue; // workaround for clang crash
1158 
1159  Atom<bool> go(false);
1160  auto goPtr = &go; // workaround for clang crash
1161  Atom<int> pendingSenders(numSendingThreads);
1162  auto pendingSendersPtr = &pendingSenders; // workaround for clang crash
1163  vector<thread> threads(numSendingThreads + numReceivingThreads);
1164 
1166  for (size_t t = 0; t < threads.size(); ++t) {
1167  threads[t] = DSched::thread([&, t, numSendingThreads] {
1168  if (t >= numSendingThreads) {
1169  // we're a receiver
1170  typename decltype(queue)::value_type elem;
1171  while (true) {
1172  queuePtr->blockingRead(elem);
1173  if (!elem) {
1174  // EOF, pass the EOF token
1175  queuePtr->blockingWrite(std::move(elem));
1176  break;
1177  }
1178  if (*elem) {
1179  globalLock.unlock_shared(**elem);
1180  } else {
1181  globalLock.unlock_shared();
1182  }
1183  }
1184  return;
1185  }
1186  // else we're a sender
1187 
1188  struct drand48_data buffer;
1189  srand48_r(t, &buffer);
1190 
1191  while (!goPtr->load()) {
1193  }
1194  for (size_t op = t; op < numOps; op += numSendingThreads) {
1195  long unscaledRandVal;
1196  lrand48_r(&buffer, &unscaledRandVal);
1197 
1198  // randVal in [0,1]
1199  double randVal = ((double)unscaledRandVal) / 0x7fffffff;
1200 
1201  // extract a bit and rescale
1202  bool useToken = randVal >= 0.5;
1203  randVal = (randVal - (useToken ? 0.5 : 0.0)) * 2;
1204 
1205  boost::optional<SharedMutexToken> maybeToken;
1206 
1207  if (useToken) {
1208  SharedMutexToken token;
1209  if (randVal < preWriteFraction) {
1210  globalLock.lock();
1211  globalLock.unlock_and_lock_shared(token);
1212  } else if (randVal < preWriteFraction + preUpgradeFraction / 2) {
1213  globalLock.lock_upgrade();
1214  globalLock.unlock_upgrade_and_lock_shared(token);
1215  } else if (randVal < preWriteFraction + preUpgradeFraction) {
1216  globalLock.lock_upgrade();
1217  globalLock.unlock_upgrade_and_lock();
1218  globalLock.unlock_and_lock_shared(token);
1219  } else {
1220  globalLock.lock_shared(token);
1221  }
1222  maybeToken = token;
1223  } else {
1224  if (randVal < preWriteFraction) {
1225  globalLock.lock();
1226  globalLock.unlock_and_lock_shared();
1227  } else if (randVal < preWriteFraction + preUpgradeFraction / 2) {
1228  globalLock.lock_upgrade();
1229  globalLock.unlock_upgrade_and_lock_shared();
1230  } else if (randVal < preWriteFraction + preUpgradeFraction) {
1231  globalLock.lock_upgrade();
1232  globalLock.unlock_upgrade_and_lock();
1233  globalLock.unlock_and_lock_shared();
1234  } else {
1235  globalLock.lock_shared();
1236  }
1237  }
1238 
1239  // blockingWrite is emplace-like, so this automatically adds
1240  // another level of wrapping
1241  queuePtr->blockingWrite(maybeToken);
1242  }
1243  if (--*pendingSendersPtr == 0) {
1244  queuePtr->blockingWrite(boost::none);
1245  }
1246  });
1247  }
1248  }
1249 
1250  go.store(true);
1251  for (auto& thr : threads) {
1252  DSched::join(thr);
1253  }
1254 }
1255 
1256 TEST(SharedMutex, deterministic_remote_write_prio) {
1257  // This test fails in an assertion in the TSAN library because there are too
1258  // many mutexes
1260  for (int pass = 0; pass < 1; ++pass) {
1261  DSched sched(DSched::uniform(pass));
1262  runRemoteUnlock<DSharedMutexWritePriority, DeterministicAtomic>(
1263  500, 0.1, 0.1, 5, 5);
1264  }
1265 }
1266 
1267 TEST(SharedMutex, deterministic_remote_read_prio) {
1268  for (int pass = 0; pass < 1; ++pass) {
1269  DSched sched(DSched::uniform(pass));
1270  runRemoteUnlock<DSharedMutexReadPriority, DeterministicAtomic>(
1271  500, 0.1, 0.1, 5, 5);
1272  }
1273 }
1274 
1275 TEST(SharedMutex, remote_write_prio) {
1276  // This test fails in an assertion in the TSAN library because there are too
1277  // many mutexes
1279  for (int pass = 0; pass < 10; ++pass) {
1280  runRemoteUnlock<SharedMutexWritePriority, atomic>(100000, 0.1, 0.1, 5, 5);
1281  }
1282 }
1283 
1284 TEST(SharedMutex, remote_read_prio) {
1285  // This test fails in an assertion in the TSAN library because there are too
1286  // many mutexes
1288  for (int pass = 0; pass < (folly::kIsSanitizeAddress ? 1 : 100); ++pass) {
1289  runRemoteUnlock<SharedMutexReadPriority, atomic>(100000, 0.1, 0.1, 5, 5);
1290  }
1291 }
1292 
1293 static void burn(size_t n) {
1294  for (size_t i = 0; i < n; ++i) {
1296  }
1297 }
1298 
1299 // Two threads and three locks, arranged so that they have to proceed
1300 // in turn with reader/writer conflict
1301 template <typename Lock, template <typename> class Atom = atomic>
1302 static void runPingPong(size_t numRounds, size_t burnCount) {
1303  char padding1[56];
1304  (void)padding1;
1305  pair<Lock, char[56]> locks[3];
1306  char padding2[56];
1307  (void)padding2;
1308 
1309  Atom<int> avail(0);
1310  auto availPtr = &avail; // workaround for clang crash
1311  Atom<bool> go(false);
1312  auto goPtr = &go; // workaround for clang crash
1313  vector<thread> threads(2);
1314 
1315  locks[0].first.lock();
1316  locks[1].first.lock();
1317  locks[2].first.lock_shared();
1318 
1320  threads[0] = DSched::thread([&] {
1321  ++*availPtr;
1322  while (!goPtr->load()) {
1324  }
1325  for (size_t i = 0; i < numRounds; ++i) {
1326  locks[i % 3].first.unlock();
1327  locks[(i + 2) % 3].first.lock();
1328  burn(burnCount);
1329  }
1330  });
1331  threads[1] = DSched::thread([&] {
1332  ++*availPtr;
1333  while (!goPtr->load()) {
1335  }
1336  for (size_t i = 0; i < numRounds; ++i) {
1337  locks[i % 3].first.lock_shared();
1338  burn(burnCount);
1339  locks[(i + 2) % 3].first.unlock_shared();
1340  }
1341  });
1342 
1343  while (avail.load() < 2) {
1345  }
1346  }
1347 
1348  go.store(true);
1349  for (auto& thr : threads) {
1350  DSched::join(thr);
1351  }
1352  locks[numRounds % 3].first.unlock();
1353  locks[(numRounds + 1) % 3].first.unlock();
1354  locks[(numRounds + 2) % 3].first.unlock_shared();
1355 }
1356 
1357 static void folly_rwspin_ping_pong(size_t n, size_t scale, size_t burnCount) {
1358  runPingPong<RWSpinLock>(n / scale, burnCount);
1359 }
1360 
1361 static void shmtx_w_bare_ping_pong(size_t n, size_t scale, size_t burnCount) {
1362  runPingPong<SharedMutexWritePriority>(n / scale, burnCount);
1363 }
1364 
1365 static void shmtx_r_bare_ping_pong(size_t n, size_t scale, size_t burnCount) {
1366  runPingPong<SharedMutexReadPriority>(n / scale, burnCount);
1367 }
1368 
1369 static void folly_ticket_ping_pong(size_t n, size_t scale, size_t burnCount) {
1370  runPingPong<RWTicketSpinLock64>(n / scale, burnCount);
1371 }
1372 
1373 static void boost_shared_ping_pong(size_t n, size_t scale, size_t burnCount) {
1374  runPingPong<boost::shared_mutex>(n / scale, burnCount);
1375 }
1376 
1377 static void pthrd_rwlock_ping_pong(size_t n, size_t scale, size_t burnCount) {
1378  runPingPong<PosixRWLock>(n / scale, burnCount);
1379 }
1380 
1381 TEST(SharedMutex, deterministic_ping_pong_write_prio) {
1382  // This test fails in TSAN because some mutexes are lock_shared() in one
1383  // thread and unlock_shared() in a different thread.
1385  for (int pass = 0; pass < 1; ++pass) {
1386  DSched sched(DSched::uniform(pass));
1387  runPingPong<DSharedMutexWritePriority, DeterministicAtomic>(500, 0);
1388  }
1389 }
1390 
1391 TEST(SharedMutex, deterministic_ping_pong_read_prio) {
1392  for (int pass = 0; pass < 1; ++pass) {
1393  DSched sched(DSched::uniform(pass));
1394  runPingPong<DSharedMutexReadPriority, DeterministicAtomic>(500, 0);
1395  }
1396 }
1397 
1398 TEST(SharedMutex, ping_pong_write_prio) {
1399  // This test fails in TSAN because some mutexes are lock_shared() in one
1400  // thread and unlock_shared() in a different thread.
1402  for (int pass = 0; pass < 1; ++pass) {
1403  runPingPong<SharedMutexWritePriority, atomic>(50000, 0);
1404  }
1405 }
1406 
1407 TEST(SharedMutex, ping_pong_read_prio) {
1408  for (int pass = 0; pass < 1; ++pass) {
1409  runPingPong<SharedMutexReadPriority, atomic>(50000, 0);
1410  }
1411 }
1412 
1413 // This is here so you can tell how much of the runtime reported by the
1414 // more complex harnesses is due to the harness, although due to the
1415 // magic of compiler optimization it may also be slower
1416 BENCHMARK(single_thread_lock_shared_unlock_shared, iters) {
1417  SharedMutex lock;
1418  for (size_t n = 0; n < iters; ++n) {
1419  SharedMutex::Token token;
1420  lock.lock_shared(token);
1422  lock.unlock_shared(token);
1423  }
1424 }
1425 
1426 BENCHMARK(single_thread_lock_unlock, iters) {
1427  SharedMutex lock;
1428  for (size_t n = 0; n < iters; ++n) {
1429  lock.lock();
1431  lock.unlock();
1432  }
1433 }
1434 
1435 #define BENCH_BASE(...) FB_VA_GLUE(BENCHMARK_NAMED_PARAM, (__VA_ARGS__))
1436 #define BENCH_REL(...) FB_VA_GLUE(BENCHMARK_RELATIVE_NAMED_PARAM, (__VA_ARGS__))
1437 
1438 // 100% reads. Best-case scenario for deferred locks. Lock is colocated
1439 // with read data, so inline lock takes cache miss every time but deferred
1440 // lock has only cache hits and local access.
1443 BENCH_BASE(folly_rwspin_reads, 1thread, 1, false)
1444 BENCH_REL(shmtx_wr_pri_reads, 1thread, 1, false)
1445 BENCH_REL(shmtx_w_bare_reads, 1thread, 1, false)
1446 BENCH_REL(shmtx_rd_pri_reads, 1thread, 1, false)
1447 BENCH_REL(shmtx_r_bare_reads, 1thread, 1, false)
1448 BENCH_REL(folly_ticket_reads, 1thread, 1, false)
1449 BENCH_REL(boost_shared_reads, 1thread, 1, false)
1450 BENCH_REL(pthrd_rwlock_reads, 1thread, 1, false)
1452 BENCH_BASE(folly_rwspin_reads, 2thread, 2, false)
1453 BENCH_REL(shmtx_wr_pri_reads, 2thread, 2, false)
1454 BENCH_REL(shmtx_w_bare_reads, 2thread, 2, false)
1455 BENCH_REL(shmtx_rd_pri_reads, 2thread, 2, false)
1456 BENCH_REL(shmtx_r_bare_reads, 2thread, 2, false)
1457 BENCH_REL(folly_ticket_reads, 2thread, 2, false)
1458 BENCH_REL(boost_shared_reads, 2thread, 2, false)
1459 BENCH_REL(pthrd_rwlock_reads, 2thread, 2, false)
1460 BENCHMARK_DRAW_LINE();
1461 BENCH_BASE(folly_rwspin_reads, 4thread, 4, false)
1462 BENCH_REL(shmtx_wr_pri_reads, 4thread, 4, false)
1463 BENCH_REL(shmtx_w_bare_reads, 4thread, 4, false)
1464 BENCH_REL(shmtx_rd_pri_reads, 4thread, 4, false)
1465 BENCH_REL(shmtx_r_bare_reads, 4thread, 4, false)
1466 BENCH_REL(folly_ticket_reads, 4thread, 4, false)
1467 BENCH_REL(boost_shared_reads, 4thread, 4, false)
1468 BENCH_REL(pthrd_rwlock_reads, 4thread, 4, false)
1469 BENCHMARK_DRAW_LINE();
1470 BENCH_BASE(folly_rwspin_reads, 8thread, 8, false)
1471 BENCH_REL(shmtx_wr_pri_reads, 8thread, 8, false)
1472 BENCH_REL(shmtx_w_bare_reads, 8thread, 8, false)
1473 BENCH_REL(shmtx_rd_pri_reads, 8thread, 8, false)
1474 BENCH_REL(shmtx_r_bare_reads, 8thread, 8, false)
1475 BENCH_REL(folly_ticket_reads, 8thread, 8, false)
1476 BENCH_REL(boost_shared_reads, 8thread, 8, false)
1477 BENCH_REL(pthrd_rwlock_reads, 8thread, 8, false)
1478 BENCHMARK_DRAW_LINE();
1479 BENCH_BASE(folly_rwspin_reads, 16thread, 16, false)
1480 BENCH_REL(shmtx_wr_pri_reads, 16thread, 16, false)
1481 BENCH_REL(shmtx_w_bare_reads, 16thread, 16, false)
1482 BENCH_REL(shmtx_rd_pri_reads, 16thread, 16, false)
1483 BENCH_REL(shmtx_r_bare_reads, 16thread, 16, false)
1484 BENCH_REL(folly_ticket_reads, 16thread, 16, false)
1485 BENCH_REL(boost_shared_reads, 16thread, 16, false)
1486 BENCH_REL(pthrd_rwlock_reads, 16thread, 16, false)
1487 BENCHMARK_DRAW_LINE();
1488 BENCH_BASE(folly_rwspin_reads, 32thread, 32, false)
1489 BENCH_REL(shmtx_wr_pri_reads, 32thread, 32, false)
1490 BENCH_REL(shmtx_w_bare_reads, 32thread, 32, false)
1491 BENCH_REL(shmtx_rd_pri_reads, 32thread, 32, false)
1492 BENCH_REL(shmtx_r_bare_reads, 32thread, 32, false)
1493 BENCH_REL(folly_ticket_reads, 32thread, 32, false)
1494 BENCH_REL(boost_shared_reads, 32thread, 32, false)
1495 BENCH_REL(pthrd_rwlock_reads, 32thread, 32, false)
1496 BENCHMARK_DRAW_LINE();
1497 BENCH_BASE(folly_rwspin_reads, 64thread, 64, false)
1498 BENCH_REL(shmtx_wr_pri_reads, 64thread, 64, false)
1499 BENCH_REL(shmtx_w_bare_reads, 64thread, 64, false)
1500 BENCH_REL(shmtx_rd_pri_reads, 64thread, 64, false)
1501 BENCH_REL(shmtx_r_bare_reads, 64thread, 64, false)
1502 BENCH_REL(folly_ticket_reads, 64thread, 64, false)
1503 BENCH_REL(boost_shared_reads, 64thread, 64, false)
1504 BENCH_REL(pthrd_rwlock_reads, 64thread, 64, false)
1505 
1506 // 1 lock used by everybody, 100% writes. Threads only hurt, but it is
1507 // good to not fail catastrophically. Compare to single_thread_lock_unlock
1508 // to see the overhead of the generic driver (and its pseudo-random number
1509 // generator). pthrd_mutex_ is a pthread_mutex_t (default, not adaptive),
1510 // which is better than any of the reader-writer locks for this scenario.
1511 BENCHMARK_DRAW_LINE();
1512 BENCHMARK_DRAW_LINE();
1513 BENCH_BASE(folly_rwspin, 1thread_all_write, 1, 1.0, false)
1514 BENCH_REL(shmtx_wr_pri, 1thread_all_write, 1, 1.0, false)
1515 BENCH_REL(shmtx_rd_pri, 1thread_all_write, 1, 1.0, false)
1516 BENCH_REL(folly_ticket, 1thread_all_write, 1, 1.0, false)
1517 BENCH_REL(boost_shared, 1thread_all_write, 1, 1.0, false)
1518 BENCH_REL(pthrd_rwlock, 1thread_all_write, 1, 1.0, false)
1519 BENCH_REL(pthrd_mutex_, 1thread_all_write, 1, 1.0, false)
1520 BENCHMARK_DRAW_LINE();
1521 BENCH_BASE(folly_rwspin, 2thread_all_write, 2, 1.0, false)
1522 BENCH_REL(shmtx_wr_pri, 2thread_all_write, 2, 1.0, false)
1523 BENCH_REL(shmtx_rd_pri, 2thread_all_write, 2, 1.0, false)
1524 BENCH_REL(folly_ticket, 2thread_all_write, 2, 1.0, false)
1525 BENCH_REL(boost_shared, 2thread_all_write, 2, 1.0, false)
1526 BENCH_REL(pthrd_rwlock, 2thread_all_write, 2, 1.0, false)
1527 BENCH_REL(pthrd_mutex_, 2thread_all_write, 2, 1.0, false)
1528 BENCHMARK_DRAW_LINE();
1529 BENCH_BASE(folly_rwspin, 4thread_all_write, 4, 1.0, false)
1530 BENCH_REL(shmtx_wr_pri, 4thread_all_write, 4, 1.0, false)
1531 BENCH_REL(shmtx_rd_pri, 4thread_all_write, 4, 1.0, false)
1532 BENCH_REL(folly_ticket, 4thread_all_write, 4, 1.0, false)
1533 BENCH_REL(boost_shared, 4thread_all_write, 4, 1.0, false)
1534 BENCH_REL(pthrd_rwlock, 4thread_all_write, 4, 1.0, false)
1535 BENCH_REL(pthrd_mutex_, 4thread_all_write, 4, 1.0, false)
1536 BENCHMARK_DRAW_LINE();
1537 BENCH_BASE(folly_rwspin, 8thread_all_write, 8, 1.0, false)
1538 BENCH_REL(shmtx_wr_pri, 8thread_all_write, 8, 1.0, false)
1539 BENCH_REL(shmtx_rd_pri, 8thread_all_write, 8, 1.0, false)
1540 BENCH_REL(folly_ticket, 8thread_all_write, 8, 1.0, false)
1541 BENCH_REL(boost_shared, 8thread_all_write, 8, 1.0, false)
1542 BENCH_REL(pthrd_rwlock, 8thread_all_write, 8, 1.0, false)
1543 BENCH_REL(pthrd_mutex_, 8thread_all_write, 8, 1.0, false)
1544 BENCHMARK_DRAW_LINE();
1545 BENCH_BASE(folly_rwspin, 16thread_all_write, 16, 1.0, false)
1546 BENCH_REL(shmtx_wr_pri, 16thread_all_write, 16, 1.0, false)
1547 BENCH_REL(shmtx_rd_pri, 16thread_all_write, 16, 1.0, false)
1548 BENCH_REL(folly_ticket, 16thread_all_write, 16, 1.0, false)
1549 BENCH_REL(boost_shared, 16thread_all_write, 16, 1.0, false)
1550 BENCH_REL(pthrd_rwlock, 16thread_all_write, 16, 1.0, false)
1551 BENCH_REL(pthrd_mutex_, 16thread_all_write, 16, 1.0, false)
1552 BENCHMARK_DRAW_LINE();
1553 BENCH_BASE(folly_rwspin, 32thread_all_write, 32, 1.0, false)
1554 BENCH_REL(shmtx_wr_pri, 32thread_all_write, 32, 1.0, false)
1555 BENCH_REL(shmtx_rd_pri, 32thread_all_write, 32, 1.0, false)
1556 BENCH_REL(folly_ticket, 32thread_all_write, 32, 1.0, false)
1557 BENCH_REL(boost_shared, 32thread_all_write, 32, 1.0, false)
1558 BENCH_REL(pthrd_rwlock, 32thread_all_write, 32, 1.0, false)
1559 BENCH_REL(pthrd_mutex_, 32thread_all_write, 32, 1.0, false)
1560 BENCHMARK_DRAW_LINE();
1561 BENCH_BASE(folly_rwspin, 64thread_all_write, 64, 1.0, false)
1562 BENCH_REL(shmtx_wr_pri, 64thread_all_write, 64, 1.0, false)
1563 BENCH_REL(shmtx_rd_pri, 64thread_all_write, 64, 1.0, false)
1564 BENCH_REL(folly_ticket, 64thread_all_write, 64, 1.0, false)
1565 BENCH_REL(boost_shared, 64thread_all_write, 64, 1.0, false)
1566 BENCH_REL(pthrd_rwlock, 64thread_all_write, 64, 1.0, false)
1567 BENCH_REL(pthrd_mutex_, 64thread_all_write, 64, 1.0, false)
1568 
1569 // 1 lock used by everybody, 10% writes. Not much scaling to be had. Perf
1570 // is best at 1 thread, once you've got multiple threads > 8 threads hurts.
1571 BENCHMARK_DRAW_LINE();
1572 BENCHMARK_DRAW_LINE();
1573 BENCH_BASE(folly_rwspin, 1thread_10pct_write, 1, 0.10, false)
1574 BENCH_REL(shmtx_wr_pri, 1thread_10pct_write, 1, 0.10, false)
1575 BENCH_REL(shmtx_rd_pri, 1thread_10pct_write, 1, 0.10, false)
1576 BENCH_REL(folly_ticket, 1thread_10pct_write, 1, 0.10, false)
1577 BENCH_REL(boost_shared, 1thread_10pct_write, 1, 0.10, false)
1578 BENCH_REL(pthrd_rwlock, 1thread_10pct_write, 1, 0.10, false)
1579 BENCHMARK_DRAW_LINE();
1580 BENCH_BASE(folly_rwspin, 2thread_10pct_write, 2, 0.10, false)
1581 BENCH_REL(shmtx_wr_pri, 2thread_10pct_write, 2, 0.10, false)
1582 BENCH_REL(shmtx_rd_pri, 2thread_10pct_write, 2, 0.10, false)
1583 BENCH_REL(folly_ticket, 2thread_10pct_write, 2, 0.10, false)
1584 BENCH_REL(boost_shared, 2thread_10pct_write, 2, 0.10, false)
1585 BENCH_REL(pthrd_rwlock, 2thread_10pct_write, 2, 0.10, false)
1586 BENCHMARK_DRAW_LINE();
1587 BENCH_BASE(folly_rwspin, 4thread_10pct_write, 4, 0.10, false)
1588 BENCH_REL(shmtx_wr_pri, 4thread_10pct_write, 4, 0.10, false)
1589 BENCH_REL(shmtx_rd_pri, 4thread_10pct_write, 4, 0.10, false)
1590 BENCH_REL(folly_ticket, 4thread_10pct_write, 4, 0.10, false)
1591 BENCH_REL(boost_shared, 4thread_10pct_write, 4, 0.10, false)
1592 BENCH_REL(pthrd_rwlock, 4thread_10pct_write, 4, 0.10, false)
1593 BENCHMARK_DRAW_LINE();
1594 BENCH_BASE(folly_rwspin, 8thread_10pct_write, 8, 0.10, false)
1595 BENCH_REL(shmtx_wr_pri, 8thread_10pct_write, 8, 0.10, false)
1596 BENCH_REL(shmtx_rd_pri, 8thread_10pct_write, 8, 0.10, false)
1597 BENCH_REL(folly_ticket, 8thread_10pct_write, 8, 0.10, false)
1598 BENCH_REL(boost_shared, 8thread_10pct_write, 8, 0.10, false)
1599 BENCH_REL(pthrd_rwlock, 8thread_10pct_write, 8, 0.10, false)
1600 BENCHMARK_DRAW_LINE();
1601 BENCH_BASE(folly_rwspin, 16thread_10pct_write, 16, 0.10, false)
1602 BENCH_REL(shmtx_wr_pri, 16thread_10pct_write, 16, 0.10, false)
1603 BENCH_REL(shmtx_rd_pri, 16thread_10pct_write, 16, 0.10, false)
1604 BENCH_REL(folly_ticket, 16thread_10pct_write, 16, 0.10, false)
1605 BENCH_REL(boost_shared, 16thread_10pct_write, 16, 0.10, false)
1606 BENCH_REL(pthrd_rwlock, 16thread_10pct_write, 16, 0.10, false)
1607 BENCHMARK_DRAW_LINE();
1608 BENCH_BASE(folly_rwspin, 32thread_10pct_write, 32, 0.10, false)
1609 BENCH_REL(shmtx_wr_pri, 32thread_10pct_write, 32, 0.10, false)
1610 BENCH_REL(shmtx_rd_pri, 32thread_10pct_write, 32, 0.10, false)
1611 BENCH_REL(folly_ticket, 32thread_10pct_write, 32, 0.10, false)
1612 BENCH_REL(boost_shared, 32thread_10pct_write, 32, 0.10, false)
1613 BENCH_REL(pthrd_rwlock, 32thread_10pct_write, 32, 0.10, false)
1614 BENCHMARK_DRAW_LINE();
1615 BENCH_BASE(folly_rwspin, 64thread_10pct_write, 64, 0.10, false)
1616 BENCH_REL(shmtx_wr_pri, 64thread_10pct_write, 64, 0.10, false)
1617 BENCH_REL(shmtx_rd_pri, 64thread_10pct_write, 64, 0.10, false)
1618 BENCH_REL(folly_ticket, 64thread_10pct_write, 64, 0.10, false)
1619 BENCH_REL(boost_shared, 64thread_10pct_write, 64, 0.10, false)
1620 BENCH_REL(pthrd_rwlock, 64thread_10pct_write, 64, 0.10, false)
1621 
1622 // 1 lock used by everybody, 1% writes. This is a more realistic example
1623 // than the concurrent_*_reads benchmark, but still shows SharedMutex locks
1624 // winning over all of the others
1625 BENCHMARK_DRAW_LINE();
1626 BENCHMARK_DRAW_LINE();
1627 BENCH_BASE(folly_rwspin, 1thread_1pct_write, 1, 0.01, false)
1628 BENCH_REL(shmtx_wr_pri, 1thread_1pct_write, 1, 0.01, false)
1629 BENCH_REL(shmtx_w_bare, 1thread_1pct_write, 1, 0.01, false)
1630 BENCH_REL(shmtx_rd_pri, 1thread_1pct_write, 1, 0.01, false)
1631 BENCH_REL(shmtx_r_bare, 1thread_1pct_write, 1, 0.01, false)
1632 BENCH_REL(folly_ticket, 1thread_1pct_write, 1, 0.01, false)
1633 BENCH_REL(boost_shared, 1thread_1pct_write, 1, 0.01, false)
1634 BENCH_REL(pthrd_rwlock, 1thread_1pct_write, 1, 0.01, false)
1635 BENCHMARK_DRAW_LINE();
1636 BENCH_BASE(folly_rwspin, 2thread_1pct_write, 2, 0.01, false)
1637 BENCH_REL(shmtx_wr_pri, 2thread_1pct_write, 2, 0.01, false)
1638 BENCH_REL(shmtx_w_bare, 2thread_1pct_write, 2, 0.01, false)
1639 BENCH_REL(shmtx_rd_pri, 2thread_1pct_write, 2, 0.01, false)
1640 BENCH_REL(shmtx_r_bare, 2thread_1pct_write, 2, 0.01, false)
1641 BENCH_REL(folly_ticket, 2thread_1pct_write, 2, 0.01, false)
1642 BENCH_REL(boost_shared, 2thread_1pct_write, 2, 0.01, false)
1643 BENCH_REL(pthrd_rwlock, 2thread_1pct_write, 2, 0.01, false)
1644 BENCHMARK_DRAW_LINE();
1645 BENCH_BASE(folly_rwspin, 4thread_1pct_write, 4, 0.01, false)
1646 BENCH_REL(shmtx_wr_pri, 4thread_1pct_write, 4, 0.01, false)
1647 BENCH_REL(shmtx_w_bare, 4thread_1pct_write, 4, 0.01, false)
1648 BENCH_REL(shmtx_rd_pri, 4thread_1pct_write, 4, 0.01, false)
1649 BENCH_REL(shmtx_r_bare, 4thread_1pct_write, 4, 0.01, false)
1650 BENCH_REL(folly_ticket, 4thread_1pct_write, 4, 0.01, false)
1651 BENCH_REL(boost_shared, 4thread_1pct_write, 4, 0.01, false)
1652 BENCH_REL(pthrd_rwlock, 4thread_1pct_write, 4, 0.01, false)
1653 BENCHMARK_DRAW_LINE();
1654 BENCH_BASE(folly_rwspin, 8thread_1pct_write, 8, 0.01, false)
1655 BENCH_REL(shmtx_wr_pri, 8thread_1pct_write, 8, 0.01, false)
1656 BENCH_REL(shmtx_w_bare, 8thread_1pct_write, 8, 0.01, false)
1657 BENCH_REL(shmtx_rd_pri, 8thread_1pct_write, 8, 0.01, false)
1658 BENCH_REL(shmtx_r_bare, 8thread_1pct_write, 8, 0.01, false)
1659 BENCH_REL(folly_ticket, 8thread_1pct_write, 8, 0.01, false)
1660 BENCH_REL(boost_shared, 8thread_1pct_write, 8, 0.01, false)
1661 BENCH_REL(pthrd_rwlock, 8thread_1pct_write, 8, 0.01, false)
1662 BENCHMARK_DRAW_LINE();
1663 BENCH_BASE(folly_rwspin, 16thread_1pct_write, 16, 0.01, false)
1664 BENCH_REL(shmtx_wr_pri, 16thread_1pct_write, 16, 0.01, false)
1665 BENCH_REL(shmtx_w_bare, 16thread_1pct_write, 16, 0.01, false)
1666 BENCH_REL(shmtx_rd_pri, 16thread_1pct_write, 16, 0.01, false)
1667 BENCH_REL(shmtx_r_bare, 16thread_1pct_write, 16, 0.01, false)
1668 BENCH_REL(folly_ticket, 16thread_1pct_write, 16, 0.01, false)
1669 BENCH_REL(boost_shared, 16thread_1pct_write, 16, 0.01, false)
1670 BENCH_REL(pthrd_rwlock, 16thread_1pct_write, 16, 0.01, false)
1671 BENCHMARK_DRAW_LINE();
1672 BENCH_BASE(folly_rwspin, 32thread_1pct_write, 32, 0.01, false)
1673 BENCH_REL(shmtx_wr_pri, 32thread_1pct_write, 32, 0.01, false)
1674 BENCH_REL(shmtx_w_bare, 32thread_1pct_write, 32, 0.01, false)
1675 BENCH_REL(shmtx_rd_pri, 32thread_1pct_write, 32, 0.01, false)
1676 BENCH_REL(shmtx_r_bare, 32thread_1pct_write, 32, 0.01, false)
1677 BENCH_REL(folly_ticket, 32thread_1pct_write, 32, 0.01, false)
1678 BENCH_REL(boost_shared, 32thread_1pct_write, 32, 0.01, false)
1679 BENCH_REL(pthrd_rwlock, 32thread_1pct_write, 32, 0.01, false)
1680 BENCHMARK_DRAW_LINE();
1681 BENCH_BASE(folly_rwspin, 64thread_1pct_write, 64, 0.01, false)
1682 BENCH_REL(shmtx_wr_pri, 64thread_1pct_write, 64, 0.01, false)
1683 BENCH_REL(shmtx_w_bare, 64thread_1pct_write, 64, 0.01, false)
1684 BENCH_REL(shmtx_rd_pri, 64thread_1pct_write, 64, 0.01, false)
1685 BENCH_REL(shmtx_r_bare, 64thread_1pct_write, 64, 0.01, false)
1686 BENCH_REL(folly_ticket, 64thread_1pct_write, 64, 0.01, false)
1687 BENCH_REL(boost_shared, 64thread_1pct_write, 64, 0.01, false)
1688 BENCH_REL(pthrd_rwlock, 64thread_1pct_write, 64, 0.01, false)
1689 
1690 // Worst case scenario for deferred locks. No actual sharing, likely that
1691 // read operations will have to first set the kDeferredReadersPossibleBit,
1692 // and likely that writers will have to scan deferredReaders[].
1693 BENCHMARK_DRAW_LINE();
1694 BENCH_BASE(folly_rwspin, 2thr_2lock_50pct_write, 2, 0.50, true)
1695 BENCH_REL(shmtx_wr_pri, 2thr_2lock_50pct_write, 2, 0.50, true)
1696 BENCH_REL(shmtx_rd_pri, 2thr_2lock_50pct_write, 2, 0.50, true)
1697 BENCH_BASE(folly_rwspin, 4thr_4lock_50pct_write, 4, 0.50, true)
1698 BENCH_REL(shmtx_wr_pri, 4thr_4lock_50pct_write, 4, 0.50, true)
1699 BENCH_REL(shmtx_rd_pri, 4thr_4lock_50pct_write, 4, 0.50, true)
1700 BENCH_BASE(folly_rwspin, 8thr_8lock_50pct_write, 8, 0.50, true)
1701 BENCH_REL(shmtx_wr_pri, 8thr_8lock_50pct_write, 8, 0.50, true)
1702 BENCH_REL(shmtx_rd_pri, 8thr_8lock_50pct_write, 8, 0.50, true)
1703 BENCH_BASE(folly_rwspin, 16thr_16lock_50pct_write, 16, 0.50, true)
1704 BENCH_REL(shmtx_wr_pri, 16thr_16lock_50pct_write, 16, 0.50, true)
1705 BENCH_REL(shmtx_rd_pri, 16thr_16lock_50pct_write, 16, 0.50, true)
1706 BENCH_BASE(folly_rwspin, 32thr_32lock_50pct_write, 32, 0.50, true)
1707 BENCH_REL(shmtx_wr_pri, 32thr_32lock_50pct_write, 32, 0.50, true)
1708 BENCH_REL(shmtx_rd_pri, 32thr_32lock_50pct_write, 32, 0.50, true)
1709 BENCH_BASE(folly_rwspin, 64thr_64lock_50pct_write, 64, 0.50, true)
1710 BENCH_REL(shmtx_wr_pri, 64thr_64lock_50pct_write, 64, 0.50, true)
1711 BENCH_REL(shmtx_rd_pri, 64thr_64lock_50pct_write, 64, 0.50, true)
1712 BENCHMARK_DRAW_LINE();
1713 BENCH_BASE(folly_rwspin, 2thr_2lock_10pct_write, 2, 0.10, true)
1714 BENCH_REL(shmtx_wr_pri, 2thr_2lock_10pct_write, 2, 0.10, true)
1715 BENCH_REL(shmtx_rd_pri, 2thr_2lock_10pct_write, 2, 0.10, true)
1716 BENCH_BASE(folly_rwspin, 4thr_4lock_10pct_write, 4, 0.10, true)
1717 BENCH_REL(shmtx_wr_pri, 4thr_4lock_10pct_write, 4, 0.10, true)
1718 BENCH_REL(shmtx_rd_pri, 4thr_4lock_10pct_write, 4, 0.10, true)
1719 BENCH_BASE(folly_rwspin, 8thr_8lock_10pct_write, 8, 0.10, true)
1720 BENCH_REL(shmtx_wr_pri, 8thr_8lock_10pct_write, 8, 0.10, true)
1721 BENCH_REL(shmtx_rd_pri, 8thr_8lock_10pct_write, 8, 0.10, true)
1722 BENCH_BASE(folly_rwspin, 16thr_16lock_10pct_write, 16, 0.10, true)
1723 BENCH_REL(shmtx_wr_pri, 16thr_16lock_10pct_write, 16, 0.10, true)
1724 BENCH_REL(shmtx_rd_pri, 16thr_16lock_10pct_write, 16, 0.10, true)
1725 BENCH_BASE(folly_rwspin, 32thr_32lock_10pct_write, 32, 0.10, true)
1726 BENCH_REL(shmtx_wr_pri, 32thr_32lock_10pct_write, 32, 0.10, true)
1727 BENCH_REL(shmtx_rd_pri, 32thr_32lock_10pct_write, 32, 0.10, true)
1728 BENCH_BASE(folly_rwspin, 64thr_64lock_10pct_write, 64, 0.10, true)
1729 BENCH_REL(shmtx_wr_pri, 64thr_64lock_10pct_write, 64, 0.10, true)
1730 BENCH_REL(shmtx_rd_pri, 64thr_64lock_10pct_write, 64, 0.10, true)
1731 BENCHMARK_DRAW_LINE();
1732 BENCH_BASE(folly_rwspin, 2thr_2lock_1pct_write, 2, 0.01, true)
1733 BENCH_REL(shmtx_wr_pri, 2thr_2lock_1pct_write, 2, 0.01, true)
1734 BENCH_REL(shmtx_rd_pri, 2thr_2lock_1pct_write, 2, 0.01, true)
1735 BENCH_BASE(folly_rwspin, 4thr_4lock_1pct_write, 4, 0.01, true)
1736 BENCH_REL(shmtx_wr_pri, 4thr_4lock_1pct_write, 4, 0.01, true)
1737 BENCH_REL(shmtx_rd_pri, 4thr_4lock_1pct_write, 4, 0.01, true)
1738 BENCH_BASE(folly_rwspin, 8thr_8lock_1pct_write, 8, 0.01, true)
1739 BENCH_REL(shmtx_wr_pri, 8thr_8lock_1pct_write, 8, 0.01, true)
1740 BENCH_REL(shmtx_rd_pri, 8thr_8lock_1pct_write, 8, 0.01, true)
1741 BENCH_BASE(folly_rwspin, 16thr_16lock_1pct_write, 16, 0.01, true)
1742 BENCH_REL(shmtx_wr_pri, 16thr_16lock_1pct_write, 16, 0.01, true)
1743 BENCH_REL(shmtx_rd_pri, 16thr_16lock_1pct_write, 16, 0.01, true)
1744 BENCH_BASE(folly_rwspin, 32thr_32lock_1pct_write, 32, 0.01, true)
1745 BENCH_REL(shmtx_wr_pri, 32thr_32lock_1pct_write, 32, 0.01, true)
1746 BENCH_REL(shmtx_rd_pri, 32thr_32lock_1pct_write, 32, 0.01, true)
1747 BENCH_BASE(folly_rwspin, 64thr_64lock_1pct_write, 64, 0.01, true)
1748 BENCH_REL(shmtx_wr_pri, 64thr_64lock_1pct_write, 64, 0.01, true)
1749 BENCH_REL(shmtx_rd_pri, 64thr_64lock_1pct_write, 64, 0.01, true)
1750 
1751 // Ping-pong tests have a scaled number of iterations, because their burn
1752 // loop would make them too slow otherwise. Ping-pong with burn count of
1753 // 100k or 300k shows the advantage of soft-spin, reducing the cost of
1754 // each wakeup by about 20 usec. (Take benchmark reported difference,
1755 // ~400 nanos, multiply by the scale of 100, then divide by 2 because
1756 // each round has two wakeups.)
1757 BENCHMARK_DRAW_LINE();
1758 BENCHMARK_DRAW_LINE();
1761 BENCH_REL(shmtx_r_bare_ping_pong, burn0, 1, 0)
1763 BENCH_REL(boost_shared_ping_pong, burn0, 1, 0)
1765 BENCHMARK_DRAW_LINE();
1766 BENCH_BASE(folly_rwspin_ping_pong, burn100k, 100, 100000)
1767 BENCH_REL(shmtx_w_bare_ping_pong, burn100k, 100, 100000)
1768 BENCH_REL(shmtx_r_bare_ping_pong, burn100k, 100, 100000)
1769 BENCH_REL(folly_ticket_ping_pong, burn100k, 100, 100000)
1770 BENCH_REL(boost_shared_ping_pong, burn100k, 100, 100000)
1771 BENCH_REL(pthrd_rwlock_ping_pong, burn100k, 100, 100000)
1772 BENCHMARK_DRAW_LINE();
1773 BENCH_BASE(folly_rwspin_ping_pong, burn300k, 100, 300000)
1774 BENCH_REL(shmtx_w_bare_ping_pong, burn300k, 100, 300000)
1775 BENCH_REL(shmtx_r_bare_ping_pong, burn300k, 100, 300000)
1776 BENCH_REL(folly_ticket_ping_pong, burn300k, 100, 300000)
1777 BENCH_REL(boost_shared_ping_pong, burn300k, 100, 300000)
1778 BENCH_REL(pthrd_rwlock_ping_pong, burn300k, 100, 300000)
1779 BENCHMARK_DRAW_LINE();
1780 BENCH_BASE(folly_rwspin_ping_pong, burn1M, 1000, 1000000)
1781 BENCH_REL(shmtx_w_bare_ping_pong, burn1M, 1000, 1000000)
1782 BENCH_REL(shmtx_r_bare_ping_pong, burn1M, 1000, 1000000)
1783 BENCH_REL(folly_ticket_ping_pong, burn1M, 1000, 1000000)
1784 BENCH_REL(boost_shared_ping_pong, burn1M, 1000, 1000000)
1785 BENCH_REL(pthrd_rwlock_ping_pong, burn1M, 1000, 1000000)
1786 
1787 // Reproduce with 10 minutes and
1788 // sudo nice -n -20
1789 // shared_mutex_test --benchmark --bm_min_iters=1000000
1790 //
1791 // Comparison use folly::RWSpinLock as the baseline, with the
1792 // following row being the default SharedMutex (using *Holder or
1793 // Token-ful methods).
1794 //
1795 // Following results on 2-socket Intel(R) Xeon(R) CPU E5-2660 0 @ 2.20GHz
1796 //
1797 // ============================================================================
1798 // folly/test/SharedMutexTest.cpp relative time/iter iters/s
1799 // ============================================================================
1800 // single_thread_lock_shared_unlock_shared 25.17ns 39.74M
1801 // single_thread_lock_unlock 25.88ns 38.64M
1802 // ----------------------------------------------------------------------------
1803 // ----------------------------------------------------------------------------
1804 // folly_rwspin_reads(1thread) 15.16ns 65.95M
1805 // shmtx_wr_pri_reads(1thread) 69.18% 21.92ns 45.63M
1806 // shmtx_w_bare_reads(1thread) 56.07% 27.04ns 36.98M
1807 // shmtx_rd_pri_reads(1thread) 69.06% 21.95ns 45.55M
1808 // shmtx_r_bare_reads(1thread) 56.36% 26.90ns 37.17M
1809 // folly_ticket_reads(1thread) 57.56% 26.34ns 37.96M
1810 // boost_shared_reads(1thread) 10.55% 143.72ns 6.96M
1811 // pthrd_rwlock_reads(1thread) 39.61% 38.28ns 26.12M
1812 // ----------------------------------------------------------------------------
1813 // folly_rwspin_reads(2thread) 45.05ns 22.20M
1814 // shmtx_wr_pri_reads(2thread) 379.98% 11.86ns 84.34M
1815 // shmtx_w_bare_reads(2thread) 319.27% 14.11ns 70.87M
1816 // shmtx_rd_pri_reads(2thread) 385.59% 11.68ns 85.59M
1817 // shmtx_r_bare_reads(2thread) 306.56% 14.70ns 68.04M
1818 // folly_ticket_reads(2thread) 61.07% 73.78ns 13.55M
1819 // boost_shared_reads(2thread) 13.54% 332.66ns 3.01M
1820 // pthrd_rwlock_reads(2thread) 34.22% 131.65ns 7.60M
1821 // ----------------------------------------------------------------------------
1822 // folly_rwspin_reads(4thread) 62.19ns 16.08M
1823 // shmtx_wr_pri_reads(4thread) 1022.82% 6.08ns 164.48M
1824 // shmtx_w_bare_reads(4thread) 875.37% 7.10ns 140.76M
1825 // shmtx_rd_pri_reads(4thread) 1060.46% 5.86ns 170.53M
1826 // shmtx_r_bare_reads(4thread) 879.88% 7.07ns 141.49M
1827 // folly_ticket_reads(4thread) 64.62% 96.23ns 10.39M
1828 // boost_shared_reads(4thread) 14.86% 418.49ns 2.39M
1829 // pthrd_rwlock_reads(4thread) 25.01% 248.65ns 4.02M
1830 // ----------------------------------------------------------------------------
1831 // folly_rwspin_reads(8thread) 64.09ns 15.60M
1832 // shmtx_wr_pri_reads(8thread) 2191.99% 2.92ns 342.03M
1833 // shmtx_w_bare_reads(8thread) 1804.92% 3.55ns 281.63M
1834 // shmtx_rd_pri_reads(8thread) 2194.60% 2.92ns 342.44M
1835 // shmtx_r_bare_reads(8thread) 1800.53% 3.56ns 280.95M
1836 // folly_ticket_reads(8thread) 54.90% 116.74ns 8.57M
1837 // boost_shared_reads(8thread) 18.25% 351.24ns 2.85M
1838 // pthrd_rwlock_reads(8thread) 28.19% 227.31ns 4.40M
1839 // ----------------------------------------------------------------------------
1840 // folly_rwspin_reads(16thread) 70.06ns 14.27M
1841 // shmtx_wr_pri_reads(16thread) 4970.09% 1.41ns 709.38M
1842 // shmtx_w_bare_reads(16thread) 4143.75% 1.69ns 591.44M
1843 // shmtx_rd_pri_reads(16thread) 5009.31% 1.40ns 714.98M
1844 // shmtx_r_bare_reads(16thread) 4067.36% 1.72ns 580.54M
1845 // folly_ticket_reads(16thread) 46.78% 149.77ns 6.68M
1846 // boost_shared_reads(16thread) 21.67% 323.37ns 3.09M
1847 // pthrd_rwlock_reads(16thread) 35.05% 199.90ns 5.00M
1848 // ----------------------------------------------------------------------------
1849 // folly_rwspin_reads(32thread) 58.83ns 17.00M
1850 // shmtx_wr_pri_reads(32thread) 5158.37% 1.14ns 876.79M
1851 // shmtx_w_bare_reads(32thread) 4246.03% 1.39ns 721.72M
1852 // shmtx_rd_pri_reads(32thread) 4845.97% 1.21ns 823.69M
1853 // shmtx_r_bare_reads(32thread) 4721.44% 1.25ns 802.52M
1854 // folly_ticket_reads(32thread) 28.40% 207.15ns 4.83M
1855 // boost_shared_reads(32thread) 17.08% 344.54ns 2.90M
1856 // pthrd_rwlock_reads(32thread) 30.01% 196.02ns 5.10M
1857 // ----------------------------------------------------------------------------
1858 // folly_rwspin_reads(64thread) 59.19ns 16.89M
1859 // shmtx_wr_pri_reads(64thread) 3804.54% 1.56ns 642.76M
1860 // shmtx_w_bare_reads(64thread) 3625.06% 1.63ns 612.43M
1861 // shmtx_rd_pri_reads(64thread) 3418.19% 1.73ns 577.48M
1862 // shmtx_r_bare_reads(64thread) 3416.98% 1.73ns 577.28M
1863 // folly_ticket_reads(64thread) 30.53% 193.90ns 5.16M
1864 // boost_shared_reads(64thread) 18.59% 318.47ns 3.14M
1865 // pthrd_rwlock_reads(64thread) 31.35% 188.81ns 5.30M
1866 // ----------------------------------------------------------------------------
1867 // ----------------------------------------------------------------------------
1868 // folly_rwspin(1thread_all_write) 23.77ns 42.06M
1869 // shmtx_wr_pri(1thread_all_write) 85.09% 27.94ns 35.79M
1870 // shmtx_rd_pri(1thread_all_write) 85.32% 27.87ns 35.89M
1871 // folly_ticket(1thread_all_write) 88.11% 26.98ns 37.06M
1872 // boost_shared(1thread_all_write) 16.49% 144.14ns 6.94M
1873 // pthrd_rwlock(1thread_all_write) 53.99% 44.04ns 22.71M
1874 // pthrd_mutex_(1thread_all_write) 86.05% 27.63ns 36.20M
1875 // ----------------------------------------------------------------------------
1876 // folly_rwspin(2thread_all_write) 76.05ns 13.15M
1877 // shmtx_wr_pri(2thread_all_write) 60.67% 125.35ns 7.98M
1878 // shmtx_rd_pri(2thread_all_write) 60.36% 125.99ns 7.94M
1879 // folly_ticket(2thread_all_write) 129.10% 58.91ns 16.98M
1880 // boost_shared(2thread_all_write) 18.65% 407.74ns 2.45M
1881 // pthrd_rwlock(2thread_all_write) 40.90% 185.92ns 5.38M
1882 // pthrd_mutex_(2thread_all_write) 127.37% 59.71ns 16.75M
1883 // ----------------------------------------------------------------------------
1884 // folly_rwspin(4thread_all_write) 207.17ns 4.83M
1885 // shmtx_wr_pri(4thread_all_write) 119.42% 173.49ns 5.76M
1886 // shmtx_rd_pri(4thread_all_write) 117.68% 176.05ns 5.68M
1887 // folly_ticket(4thread_all_write) 182.39% 113.59ns 8.80M
1888 // boost_shared(4thread_all_write) 11.98% 1.73us 578.46K
1889 // pthrd_rwlock(4thread_all_write) 27.50% 753.25ns 1.33M
1890 // pthrd_mutex_(4thread_all_write) 117.75% 175.95ns 5.68M
1891 // ----------------------------------------------------------------------------
1892 // folly_rwspin(8thread_all_write) 326.50ns 3.06M
1893 // shmtx_wr_pri(8thread_all_write) 125.47% 260.22ns 3.84M
1894 // shmtx_rd_pri(8thread_all_write) 124.73% 261.76ns 3.82M
1895 // folly_ticket(8thread_all_write) 253.39% 128.85ns 7.76M
1896 // boost_shared(8thread_all_write) 6.36% 5.13us 194.87K
1897 // pthrd_rwlock(8thread_all_write) 38.54% 847.09ns 1.18M
1898 // pthrd_mutex_(8thread_all_write) 166.31% 196.32ns 5.09M
1899 // ----------------------------------------------------------------------------
1900 // folly_rwspin(16thread_all_write) 729.89ns 1.37M
1901 // shmtx_wr_pri(16thread_all_write) 219.91% 331.91ns 3.01M
1902 // shmtx_rd_pri(16thread_all_write) 220.09% 331.62ns 3.02M
1903 // folly_ticket(16thread_all_write) 390.06% 187.12ns 5.34M
1904 // boost_shared(16thread_all_write) 10.27% 7.11us 140.72K
1905 // pthrd_rwlock(16thread_all_write) 113.90% 640.84ns 1.56M
1906 // pthrd_mutex_(16thread_all_write) 401.97% 181.58ns 5.51M
1907 // ----------------------------------------------------------------------------
1908 // folly_rwspin(32thread_all_write) 1.55us 645.01K
1909 // shmtx_wr_pri(32thread_all_write) 415.05% 373.54ns 2.68M
1910 // shmtx_rd_pri(32thread_all_write) 258.45% 599.88ns 1.67M
1911 // folly_ticket(32thread_all_write) 525.40% 295.09ns 3.39M
1912 // boost_shared(32thread_all_write) 20.84% 7.44us 134.45K
1913 // pthrd_rwlock(32thread_all_write) 254.16% 610.00ns 1.64M
1914 // pthrd_mutex_(32thread_all_write) 852.51% 181.86ns 5.50M
1915 // ----------------------------------------------------------------------------
1916 // folly_rwspin(64thread_all_write) 2.03us 492.00K
1917 // shmtx_wr_pri(64thread_all_write) 517.65% 392.64ns 2.55M
1918 // shmtx_rd_pri(64thread_all_write) 288.20% 705.24ns 1.42M
1919 // folly_ticket(64thread_all_write) 638.22% 318.47ns 3.14M
1920 // boost_shared(64thread_all_write) 27.56% 7.37us 135.61K
1921 // pthrd_rwlock(64thread_all_write) 326.75% 622.04ns 1.61M
1922 // pthrd_mutex_(64thread_all_write) 1231.57% 165.04ns 6.06M
1923 // ----------------------------------------------------------------------------
1924 // ----------------------------------------------------------------------------
1925 // folly_rwspin(1thread_10pct_write) 19.39ns 51.58M
1926 // shmtx_wr_pri(1thread_10pct_write) 93.87% 20.65ns 48.42M
1927 // shmtx_rd_pri(1thread_10pct_write) 93.60% 20.71ns 48.28M
1928 // folly_ticket(1thread_10pct_write) 73.75% 26.29ns 38.04M
1929 // boost_shared(1thread_10pct_write) 12.97% 149.53ns 6.69M
1930 // pthrd_rwlock(1thread_10pct_write) 44.15% 43.92ns 22.77M
1931 // ----------------------------------------------------------------------------
1932 // folly_rwspin(2thread_10pct_write) 227.88ns 4.39M
1933 // shmtx_wr_pri(2thread_10pct_write) 321.08% 70.98ns 14.09M
1934 // shmtx_rd_pri(2thread_10pct_write) 280.65% 81.20ns 12.32M
1935 // folly_ticket(2thread_10pct_write) 220.43% 103.38ns 9.67M
1936 // boost_shared(2thread_10pct_write) 58.78% 387.71ns 2.58M
1937 // pthrd_rwlock(2thread_10pct_write) 112.68% 202.23ns 4.94M
1938 // ----------------------------------------------------------------------------
1939 // folly_rwspin(4thread_10pct_write) 444.94ns 2.25M
1940 // shmtx_wr_pri(4thread_10pct_write) 470.35% 94.60ns 10.57M
1941 // shmtx_rd_pri(4thread_10pct_write) 349.08% 127.46ns 7.85M
1942 // folly_ticket(4thread_10pct_write) 305.64% 145.58ns 6.87M
1943 // boost_shared(4thread_10pct_write) 44.43% 1.00us 998.57K
1944 // pthrd_rwlock(4thread_10pct_write) 100.59% 442.31ns 2.26M
1945 // ----------------------------------------------------------------------------
1946 // folly_rwspin(8thread_10pct_write) 424.67ns 2.35M
1947 // shmtx_wr_pri(8thread_10pct_write) 337.53% 125.82ns 7.95M
1948 // shmtx_rd_pri(8thread_10pct_write) 232.32% 182.79ns 5.47M
1949 // folly_ticket(8thread_10pct_write) 206.59% 205.56ns 4.86M
1950 // boost_shared(8thread_10pct_write) 19.45% 2.18us 457.90K
1951 // pthrd_rwlock(8thread_10pct_write) 78.58% 540.42ns 1.85M
1952 // ----------------------------------------------------------------------------
1953 // folly_rwspin(16thread_10pct_write) 727.04ns 1.38M
1954 // shmtx_wr_pri(16thread_10pct_write) 400.60% 181.49ns 5.51M
1955 // shmtx_rd_pri(16thread_10pct_write) 312.94% 232.33ns 4.30M
1956 // folly_ticket(16thread_10pct_write) 283.67% 256.30ns 3.90M
1957 // boost_shared(16thread_10pct_write) 15.87% 4.58us 218.32K
1958 // pthrd_rwlock(16thread_10pct_write) 131.28% 553.82ns 1.81M
1959 // ----------------------------------------------------------------------------
1960 // folly_rwspin(32thread_10pct_write) 810.61ns 1.23M
1961 // shmtx_wr_pri(32thread_10pct_write) 429.61% 188.68ns 5.30M
1962 // shmtx_rd_pri(32thread_10pct_write) 321.13% 252.42ns 3.96M
1963 // folly_ticket(32thread_10pct_write) 247.65% 327.32ns 3.06M
1964 // boost_shared(32thread_10pct_write) 8.34% 9.71us 102.94K
1965 // pthrd_rwlock(32thread_10pct_write) 144.28% 561.85ns 1.78M
1966 // ----------------------------------------------------------------------------
1967 // folly_rwspin(64thread_10pct_write) 1.10us 912.30K
1968 // shmtx_wr_pri(64thread_10pct_write) 486.68% 225.22ns 4.44M
1969 // shmtx_rd_pri(64thread_10pct_write) 412.96% 265.43ns 3.77M
1970 // folly_ticket(64thread_10pct_write) 280.23% 391.15ns 2.56M
1971 // boost_shared(64thread_10pct_write) 6.16% 17.79us 56.22K
1972 // pthrd_rwlock(64thread_10pct_write) 198.81% 551.34ns 1.81M
1973 // ----------------------------------------------------------------------------
1974 // ----------------------------------------------------------------------------
1975 // folly_rwspin(1thread_1pct_write) 19.02ns 52.57M
1976 // shmtx_wr_pri(1thread_1pct_write) 94.46% 20.14ns 49.66M
1977 // shmtx_w_bare(1thread_1pct_write) 76.60% 24.83ns 40.27M
1978 // shmtx_rd_pri(1thread_1pct_write) 93.83% 20.27ns 49.33M
1979 // shmtx_r_bare(1thread_1pct_write) 77.04% 24.69ns 40.50M
1980 // folly_ticket(1thread_1pct_write) 72.83% 26.12ns 38.29M
1981 // boost_shared(1thread_1pct_write) 12.48% 152.44ns 6.56M
1982 // pthrd_rwlock(1thread_1pct_write) 42.85% 44.39ns 22.53M
1983 // ----------------------------------------------------------------------------
1984 // folly_rwspin(2thread_1pct_write) 110.63ns 9.04M
1985 // shmtx_wr_pri(2thread_1pct_write) 442.12% 25.02ns 39.96M
1986 // shmtx_w_bare(2thread_1pct_write) 374.65% 29.53ns 33.86M
1987 // shmtx_rd_pri(2thread_1pct_write) 371.08% 29.81ns 33.54M
1988 // shmtx_r_bare(2thread_1pct_write) 138.02% 80.15ns 12.48M
1989 // folly_ticket(2thread_1pct_write) 131.34% 84.23ns 11.87M
1990 // boost_shared(2thread_1pct_write) 30.35% 364.58ns 2.74M
1991 // pthrd_rwlock(2thread_1pct_write) 95.48% 115.87ns 8.63M
1992 // ----------------------------------------------------------------------------
1993 // folly_rwspin(4thread_1pct_write) 140.62ns 7.11M
1994 // shmtx_wr_pri(4thread_1pct_write) 627.13% 22.42ns 44.60M
1995 // shmtx_w_bare(4thread_1pct_write) 552.94% 25.43ns 39.32M
1996 // shmtx_rd_pri(4thread_1pct_write) 226.06% 62.21ns 16.08M
1997 // shmtx_r_bare(4thread_1pct_write) 77.61% 181.19ns 5.52M
1998 // folly_ticket(4thread_1pct_write) 119.58% 117.60ns 8.50M
1999 // boost_shared(4thread_1pct_write) 25.36% 554.54ns 1.80M
2000 // pthrd_rwlock(4thread_1pct_write) 45.55% 308.72ns 3.24M
2001 // ----------------------------------------------------------------------------
2002 // folly_rwspin(8thread_1pct_write) 166.23ns 6.02M
2003 // shmtx_wr_pri(8thread_1pct_write) 687.09% 24.19ns 41.33M
2004 // shmtx_w_bare(8thread_1pct_write) 611.80% 27.17ns 36.80M
2005 // shmtx_rd_pri(8thread_1pct_write) 140.37% 118.43ns 8.44M
2006 // shmtx_r_bare(8thread_1pct_write) 80.32% 206.97ns 4.83M
2007 // folly_ticket(8thread_1pct_write) 117.06% 142.01ns 7.04M
2008 // boost_shared(8thread_1pct_write) 22.29% 745.67ns 1.34M
2009 // pthrd_rwlock(8thread_1pct_write) 49.84% 333.55ns 3.00M
2010 // ----------------------------------------------------------------------------
2011 // folly_rwspin(16thread_1pct_write) 419.79ns 2.38M
2012 // shmtx_wr_pri(16thread_1pct_write) 1397.92% 30.03ns 33.30M
2013 // shmtx_w_bare(16thread_1pct_write) 1324.60% 31.69ns 31.55M
2014 // shmtx_rd_pri(16thread_1pct_write) 278.12% 150.94ns 6.63M
2015 // shmtx_r_bare(16thread_1pct_write) 194.25% 216.11ns 4.63M
2016 // folly_ticket(16thread_1pct_write) 255.38% 164.38ns 6.08M
2017 // boost_shared(16thread_1pct_write) 33.71% 1.25us 803.01K
2018 // pthrd_rwlock(16thread_1pct_write) 131.96% 318.12ns 3.14M
2019 // ----------------------------------------------------------------------------
2020 // folly_rwspin(32thread_1pct_write) 395.99ns 2.53M
2021 // shmtx_wr_pri(32thread_1pct_write) 1332.76% 29.71ns 33.66M
2022 // shmtx_w_bare(32thread_1pct_write) 1208.86% 32.76ns 30.53M
2023 // shmtx_rd_pri(32thread_1pct_write) 252.97% 156.54ns 6.39M
2024 // shmtx_r_bare(32thread_1pct_write) 193.79% 204.35ns 4.89M
2025 // folly_ticket(32thread_1pct_write) 173.16% 228.69ns 4.37M
2026 // boost_shared(32thread_1pct_write) 17.00% 2.33us 429.40K
2027 // pthrd_rwlock(32thread_1pct_write) 129.88% 304.89ns 3.28M
2028 // ----------------------------------------------------------------------------
2029 // folly_rwspin(64thread_1pct_write) 424.07ns 2.36M
2030 // shmtx_wr_pri(64thread_1pct_write) 1297.89% 32.67ns 30.61M
2031 // shmtx_w_bare(64thread_1pct_write) 1228.88% 34.51ns 28.98M
2032 // shmtx_rd_pri(64thread_1pct_write) 270.40% 156.83ns 6.38M
2033 // shmtx_r_bare(64thread_1pct_write) 218.05% 194.48ns 5.14M
2034 // folly_ticket(64thread_1pct_write) 171.44% 247.36ns 4.04M
2035 // boost_shared(64thread_1pct_write) 10.60% 4.00us 249.95K
2036 // pthrd_rwlock(64thread_1pct_write) 143.80% 294.91ns 3.39M
2037 // ----------------------------------------------------------------------------
2038 // folly_rwspin(2thr_2lock_50pct_write) 10.87ns 91.99M
2039 // shmtx_wr_pri(2thr_2lock_50pct_write) 83.71% 12.99ns 77.01M
2040 // shmtx_rd_pri(2thr_2lock_50pct_write) 84.08% 12.93ns 77.34M
2041 // folly_rwspin(4thr_4lock_50pct_write) 5.32ns 188.12M
2042 // shmtx_wr_pri(4thr_4lock_50pct_write) 82.21% 6.47ns 154.65M
2043 // shmtx_rd_pri(4thr_4lock_50pct_write) 81.20% 6.55ns 152.75M
2044 // folly_rwspin(8thr_8lock_50pct_write) 2.64ns 379.06M
2045 // shmtx_wr_pri(8thr_8lock_50pct_write) 81.26% 3.25ns 308.03M
2046 // shmtx_rd_pri(8thr_8lock_50pct_write) 80.95% 3.26ns 306.86M
2047 // folly_rwspin(16thr_16lock_50pct_write) 1.52ns 656.77M
2048 // shmtx_wr_pri(16thr_16lock_50pct_write) 86.24% 1.77ns 566.41M
2049 // shmtx_rd_pri(16thr_16lock_50pct_write) 83.72% 1.82ns 549.82M
2050 // folly_rwspin(32thr_32lock_50pct_write) 1.19ns 841.03M
2051 // shmtx_wr_pri(32thr_32lock_50pct_write) 85.08% 1.40ns 715.55M
2052 // shmtx_rd_pri(32thr_32lock_50pct_write) 86.44% 1.38ns 727.00M
2053 // folly_rwspin(64thr_64lock_50pct_write) 1.46ns 684.28M
2054 // shmtx_wr_pri(64thr_64lock_50pct_write) 84.53% 1.73ns 578.43M
2055 // shmtx_rd_pri(64thr_64lock_50pct_write) 82.80% 1.76ns 566.58M
2056 // ----------------------------------------------------------------------------
2057 // folly_rwspin(2thr_2lock_10pct_write) 10.01ns 99.85M
2058 // shmtx_wr_pri(2thr_2lock_10pct_write) 92.02% 10.88ns 91.88M
2059 // shmtx_rd_pri(2thr_2lock_10pct_write) 92.35% 10.84ns 92.22M
2060 // folly_rwspin(4thr_4lock_10pct_write) 4.81ns 207.87M
2061 // shmtx_wr_pri(4thr_4lock_10pct_write) 89.32% 5.39ns 185.67M
2062 // shmtx_rd_pri(4thr_4lock_10pct_write) 88.96% 5.41ns 184.93M
2063 // folly_rwspin(8thr_8lock_10pct_write) 2.39ns 417.62M
2064 // shmtx_wr_pri(8thr_8lock_10pct_write) 91.17% 2.63ns 380.76M
2065 // shmtx_rd_pri(8thr_8lock_10pct_write) 89.53% 2.67ns 373.92M
2066 // folly_rwspin(16thr_16lock_10pct_write) 1.16ns 860.47M
2067 // shmtx_wr_pri(16thr_16lock_10pct_write) 74.35% 1.56ns 639.77M
2068 // shmtx_rd_pri(16thr_16lock_10pct_write) 91.34% 1.27ns 785.97M
2069 // folly_rwspin(32thr_32lock_10pct_write) 1.15ns 866.23M
2070 // shmtx_wr_pri(32thr_32lock_10pct_write) 92.32% 1.25ns 799.72M
2071 // shmtx_rd_pri(32thr_32lock_10pct_write) 94.40% 1.22ns 817.71M
2072 // folly_rwspin(64thr_64lock_10pct_write) 1.41ns 710.54M
2073 // shmtx_wr_pri(64thr_64lock_10pct_write) 94.14% 1.50ns 668.88M
2074 // shmtx_rd_pri(64thr_64lock_10pct_write) 94.80% 1.48ns 673.56M
2075 // ----------------------------------------------------------------------------
2076 // folly_rwspin(2thr_2lock_1pct_write) 9.58ns 104.36M
2077 // shmtx_wr_pri(2thr_2lock_1pct_write) 92.00% 10.42ns 96.01M
2078 // shmtx_rd_pri(2thr_2lock_1pct_write) 91.79% 10.44ns 95.79M
2079 // folly_rwspin(4thr_4lock_1pct_write) 4.71ns 212.30M
2080 // shmtx_wr_pri(4thr_4lock_1pct_write) 90.37% 5.21ns 191.85M
2081 // shmtx_rd_pri(4thr_4lock_1pct_write) 89.94% 5.24ns 190.95M
2082 // folly_rwspin(8thr_8lock_1pct_write) 2.33ns 429.91M
2083 // shmtx_wr_pri(8thr_8lock_1pct_write) 90.67% 2.57ns 389.80M
2084 // shmtx_rd_pri(8thr_8lock_1pct_write) 90.61% 2.57ns 389.55M
2085 // folly_rwspin(16thr_16lock_1pct_write) 1.10ns 905.23M
2086 // shmtx_wr_pri(16thr_16lock_1pct_write) 91.96% 1.20ns 832.46M
2087 // shmtx_rd_pri(16thr_16lock_1pct_write) 92.29% 1.20ns 835.42M
2088 // folly_rwspin(32thr_32lock_1pct_write) 1.14ns 879.85M
2089 // shmtx_wr_pri(32thr_32lock_1pct_write) 93.41% 1.22ns 821.86M
2090 // shmtx_rd_pri(32thr_32lock_1pct_write) 94.18% 1.21ns 828.66M
2091 // folly_rwspin(64thr_64lock_1pct_write) 1.34ns 748.83M
2092 // shmtx_wr_pri(64thr_64lock_1pct_write) 94.39% 1.41ns 706.84M
2093 // shmtx_rd_pri(64thr_64lock_1pct_write) 94.02% 1.42ns 704.06M
2094 // ----------------------------------------------------------------------------
2095 // ----------------------------------------------------------------------------
2096 // folly_rwspin_ping_pong(burn0) 605.63ns 1.65M
2097 // shmtx_w_bare_ping_pong(burn0) 102.17% 592.76ns 1.69M
2098 // shmtx_r_bare_ping_pong(burn0) 88.75% 682.44ns 1.47M
2099 // folly_ticket_ping_pong(burn0) 63.92% 947.56ns 1.06M
2100 // boost_shared_ping_pong(burn0) 8.52% 7.11us 140.73K
2101 // pthrd_rwlock_ping_pong(burn0) 7.88% 7.68us 130.15K
2102 // ----------------------------------------------------------------------------
2103 // folly_rwspin_ping_pong(burn100k) 727.76ns 1.37M
2104 // shmtx_w_bare_ping_pong(burn100k) 100.79% 722.09ns 1.38M
2105 // shmtx_r_bare_ping_pong(burn100k) 101.98% 713.61ns 1.40M
2106 // folly_ticket_ping_pong(burn100k) 102.80% 707.95ns 1.41M
2107 // boost_shared_ping_pong(burn100k) 81.49% 893.02ns 1.12M
2108 // pthrd_rwlock_ping_pong(burn100k) 71.05% 1.02us 976.30K
2109 // ----------------------------------------------------------------------------
2110 // folly_rwspin_ping_pong(burn300k) 2.11us 473.46K
2111 // shmtx_w_bare_ping_pong(burn300k) 100.06% 2.11us 473.72K
2112 // shmtx_r_bare_ping_pong(burn300k) 98.93% 2.13us 468.39K
2113 // folly_ticket_ping_pong(burn300k) 96.68% 2.18us 457.73K
2114 // boost_shared_ping_pong(burn300k) 84.72% 2.49us 401.13K
2115 // pthrd_rwlock_ping_pong(burn300k) 84.62% 2.50us 400.66K
2116 // ----------------------------------------------------------------------------
2117 // folly_rwspin_ping_pong(burn1M) 709.70ns 1.41M
2118 // shmtx_w_bare_ping_pong(burn1M) 100.28% 707.73ns 1.41M
2119 // shmtx_r_bare_ping_pong(burn1M) 99.63% 712.37ns 1.40M
2120 // folly_ticket_ping_pong(burn1M) 100.09% 709.05ns 1.41M
2121 // boost_shared_ping_pong(burn1M) 94.09% 754.29ns 1.33M
2122 // pthrd_rwlock_ping_pong(burn1M) 96.32% 736.82ns 1.36M
2123 // ============================================================================
2124 
2125 int main(int argc, char** argv) {
2126  (void)folly_rwspin_reads;
2127  (void)shmtx_wr_pri_reads;
2128  (void)shmtx_w_bare_reads;
2129  (void)shmtx_rd_pri_reads;
2130  (void)shmtx_r_bare_reads;
2131  (void)folly_ticket_reads;
2132  (void)boost_shared_reads;
2133  (void)pthrd_rwlock_reads;
2134  (void)folly_rwspin;
2135  (void)shmtx_wr_pri;
2136  (void)shmtx_w_bare;
2137  (void)shmtx_rd_pri;
2138  (void)shmtx_r_bare;
2139  (void)folly_ticket;
2140  (void)boost_shared;
2141  (void)pthrd_rwlock;
2142  (void)pthrd_mutex_;
2143  (void)folly_rwspin_ping_pong;
2144  (void)shmtx_w_bare_ping_pong;
2145  (void)shmtx_r_bare_ping_pong;
2146  (void)folly_ticket_ping_pong;
2147  (void)boost_shared_ping_pong;
2148  (void)pthrd_rwlock_ping_pong;
2149 
2150  testing::InitGoogleTest(&argc, argv);
2151  gflags::ParseCommandLineFlags(&argc, &argv, true);
2152  int rv = RUN_ALL_TESTS();
2154  return rv;
2155 }
void runManyReadLocksTestWithoutTokens()
void lock(T *lockable)
false shmtx_rd_pri_reads
pthread_rwlock_t lock_
pthread_mutex_t lock_
static std::unique_ptr< SSLLock[]> & locks()
static void shmtx_r_bare_ping_pong(size_t n, size_t scale, size_t burnCount)
static void runPingPong(size_t numRounds, size_t burnCount)
false shmtx_rd_pri
shmtx_wr_pri_reads
constexpr bool kIsSanitizeThread
Definition: Portability.h:124
int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_
Definition: gtest.h:2232
static void shmtx_w_bare_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks)
static void runMixed(size_t numOps, size_t numThreads, double writeFraction, bool useSeparateLocks)
int main(int argc, char **argv)
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void unlock(T *lockable)
#define BENCHMARK_SUSPEND
Definition: Benchmark.h:576
static void shmtx_r_bare(uint32_t numOps, size_t numThreads, double writeFraction, bool useSeparateLocks)
std::chrono::steady_clock::time_point now()
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
void lock_shared(T *lockable)
void runTimeoutInPastTest()
STL namespace.
void runFailingTryTimeoutTest()
constexpr bool kIsSanitizeAddress
Definition: Portability.h:118
void unlock(T *lockable)
folly::std T
static void folly_rwspin_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks)
false false folly_ticket_reads
static void basic()
static std::thread thread(Func &&func, Args &&...args)
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
static void boost_shared_ping_pong(size_t n, size_t scale, size_t burnCount)
void runManyReadLocksTestWithTokens()
static void shmtx_w_bare(uint32_t numOps, size_t numThreads, double writeFraction, bool useSeparateLocks)
false folly_ticket
false false false pthrd_rwlock_reads
shmtx_w_bare_ping_pong
std::vector< std::thread::id > threads
static void runContendedReaders(size_t numOps, size_t numThreads, bool useSeparateLocks)
static void pthrd_mutex_(size_t numOps, size_t numThreads, double writeFraction, bool useSeparateLocks)
void lock(T *lockable)
constexpr std::decay< T >::type copy(T &&value) noexcept(noexcept(typename std::decay< T >::type(std::forward< T >(value))))
Definition: Utility.h:72
void runBasicHoldersTest()
char ** argv
static std::function< size_t(size_t)> uniform(uint64_t seed)
Function< void()> Func
Definition: Executor.h:27
bool funcHasDuration(milliseconds expectedDuration, Func func)
#define BENCH_BASE(...)
pthrd_rwlock_ping_pong
#define Atom
static void folly_rwspin_ping_pong(size_t n, size_t scale, size_t burnCount)
auto lock(Synchronized< D, M > &synchronized, Args &&...args)
static void boost_shared(size_t numOps, size_t numThreads, double writeFraction, bool useSeparateLocks)
shmtx_wr_pri
void unlock_shared()
FOLLY_ASSUME_FBVECTOR_COMPATIBLE(boost::optional< boost::optional< SharedMutexToken >>) template< typename Lock
#define BENCH_REL(...)
auto start
void unlock_shared(T *lockable)
static std::function< size_t(size_t)> uniformSubset(uint64_t seed, size_t n=2, size_t m=64)
SharedMutexToken token
TEST(ProgramOptionsTest, Errors)
false false pthrd_rwlock
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
class Atom static void runRemoteUnlock(size_t numOps, double preWriteFraction, double preUpgradeFraction, size_t numSendingThreads, size_t numReceivingThreads)
void unlock_shared(T *lockable)
true folly_rwspin
BENCHMARK(fbFollyGlobalBenchmarkBaseline)
Definition: Benchmark.cpp:84
static std::size_t adjustReps(std::size_t reps)
bool runBenchmarksOnFlag()
Definition: Benchmark.h:48
BENCHMARK_DRAW_LINE()
void runBasicTest()
void runBasicUpgradeTest()
static void burn(size_t n)
#define SKIP_IF(expr)
Definition: TestUtils.h:59
SharedMutexImpl< true, void, DeterministicAtomic, true > DSharedMutexReadPriority
GTEST_API_ void InitGoogleTest(int *argc, char **argv)
Definition: gtest.cc:5370
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
static void join(std::thread &child)
void lock_shared(T *lockable)
void lock(T *lockable)
SharedMutexImpl< false, void, DeterministicAtomic, true > DSharedMutexWritePriority
static void boost_shared_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks)
void unlock_shared(T *lockable)
DeterministicSchedule DSched
folly_ticket_ping_pong
static void runAllAndValidate(size_t numOps, size_t numThreads)
static void shmtx_r_bare_reads(uint32_t numOps, size_t numThreads, bool useSeparateLocks)
void unlock(T *lockable)
static const char tokens[256]
Definition: http_parser.c:184
constexpr None none
Definition: Optional.h:87
auto doNotOptimizeAway(const T &datum) -> typename std::enable_if< !detail::DoNotOptimizeAwayNeedsIndirect< T >::value >::type
Definition: Benchmark.h:258
void lock_shared(T *lockable)
constexpr detail::First first
Definition: Base-inl.h:2553