@@ -497,6 +497,63 @@ TEST_F(RateLimiterTest, AutoTuneIncreaseWhenFull) {
497
497
ASSERT_LT (new_bytes_per_sec, orig_bytes_per_sec);
498
498
}
499
499
500
+ TEST_F (RateLimiterTest, WaitHangingBug) {
501
+ // At t=0: Threads 0 and 1 request `kBytesPerRefill` bytes at low-pri. One
502
+ // will be granted immediately and the other will enter `TimedWait()`.
503
+ //
504
+ // At t=`kMicrosPerRefill`: Thread 2 requests `kBytesPerRefill` bytes at
505
+ // low-pri. Thread 2's request enters the queue. To expose the bug scenario,
506
+ // `SyncPoint`s ensure this happens while the lock is temporarily released in
507
+ // `TimedWait()`. Before the bug fix, Thread 2's request would then hang in
508
+ // `Wait()` interminably.
509
+ const int kBytesPerSecond = 100 ;
510
+ const int kMicrosPerSecond = 1000 * 1000 ;
511
+ const int kMicrosPerRefill = kMicrosPerSecond ;
512
+ const int kBytesPerRefill =
513
+ kBytesPerSecond * kMicrosPerRefill / kMicrosPerSecond ;
514
+
515
+ SpecialEnv special_env (Env::Default (), true /* time_elapse_only_sleep */ );
516
+ std::unique_ptr<RateLimiter> limiter (new GenericRateLimiter (
517
+ kBytesPerSecond , kMicrosPerRefill , 10 /* fairness */ ,
518
+ RateLimiter::Mode::kWritesOnly , special_env.GetSystemClock (),
519
+ false /* auto_tuned */ ));
520
+ std::array<std::thread, 3 > request_threads;
521
+
522
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance ()->LoadDependency (
523
+ {{" RateLimiterTest::WaitHangingBug:InitialRequestsReady" ,
524
+ " SpecialEnv::TimedWait:UnlockedPreSleep" },
525
+ {" SpecialEnv::TimedWait:UnlockedPostSleep1" ,
526
+ " RateLimiterTest::WaitHangingBug:TestThreadRequestBegin" },
527
+ {" RateLimiterTest::WaitHangingBug:TestThreadRequestEnd" ,
528
+ " SpecialEnv::TimedWait:UnlockedPostSleep2" }});
529
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance ()->EnableProcessing ();
530
+
531
+ for (int i = 0 ; i < 2 ; i++) {
532
+ request_threads[i] = std::thread ([&]() {
533
+ limiter->Request (kBytesPerRefill /* bytes */ , Env::IOPriority::IO_LOW,
534
+ nullptr /* stats */ , RateLimiter::OpType::kWrite ,
535
+ &special_env);
536
+ });
537
+ }
538
+ while (limiter->GetTotalRequests () < 2 ) {
539
+ }
540
+ TEST_SYNC_POINT (" RateLimiterTest::WaitHangingBug:InitialRequestsReady" );
541
+
542
+ TEST_SYNC_POINT (" RateLimiterTest::WaitHangingBug:TestThreadRequestBegin" );
543
+ request_threads[2 ] = std::thread ([&]() {
544
+ limiter->Request (kBytesPerRefill /* bytes */ , Env::IOPriority::IO_LOW,
545
+ nullptr /* stats */ , RateLimiter::OpType::kWrite ,
546
+ &special_env);
547
+ });
548
+ while (limiter->GetTotalRequests () < 3 ) {
549
+ }
550
+ TEST_SYNC_POINT (" RateLimiterTest::WaitHangingBug:TestThreadRequestEnd" );
551
+
552
+ for (int i = 0 ; i < 3 ; i++) {
553
+ request_threads[i].join ();
554
+ }
555
+ }
556
+
500
557
} // namespace ROCKSDB_NAMESPACE
501
558
502
559
int main (int argc, char ** argv) {
0 commit comments