Fix RepeatableThreadTest::MockEnvTest hang (#4560)
Summary: When `MockTimeEnv` is used in test to mock time methods, we cannot use `CondVar::TimedWait` because it is using real time, not the mocked time for wait timeout. On Mac the method can return immediately without awaking other waiting threads, if the real time is larger than `wait_until` (which is a mocked time). When that happen, the `wait()` method will fall into an infinite loop. Pull Request resolved: https://github.com/facebook/rocksdb/pull/4560 Differential Revision: D10472851 Pulled By: yiwu-arbug fbshipit-source-id: 898902546ace7db7ac509337dd8677a527209d19
This commit is contained in:
parent
f959e88048
commit
933250e355
@ -46,6 +46,7 @@
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/compression.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/mock_time_env.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
#include "util/string_util.h"
|
||||
@ -582,37 +583,6 @@ class SpecialEnv : public EnvWrapper {
|
||||
std::atomic<size_t> compaction_readahead_size_{};
|
||||
};
|
||||
|
||||
class MockTimeEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit MockTimeEnv(Env* base) : EnvWrapper(base) {}
|
||||
|
||||
virtual Status GetCurrentTime(int64_t* time) override {
|
||||
assert(time != nullptr);
|
||||
assert(current_time_ <=
|
||||
static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
|
||||
*time = static_cast<int64_t>(current_time_);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual uint64_t NowMicros() override {
|
||||
assert(current_time_ <= std::numeric_limits<uint64_t>::max() / 1000000);
|
||||
return current_time_ * 1000000;
|
||||
}
|
||||
|
||||
virtual uint64_t NowNanos() override {
|
||||
assert(current_time_ <= std::numeric_limits<uint64_t>::max() / 1000000000);
|
||||
return current_time_ * 1000000000;
|
||||
}
|
||||
|
||||
void set_current_time(uint64_t time) {
|
||||
assert(time >= current_time_);
|
||||
current_time_ = time;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<uint64_t> current_time_{0};
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class OnFileDeletionListener : public EventListener {
|
||||
public:
|
||||
|
43
util/mock_time_env.h
Normal file
43
util/mock_time_env.h
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class MockTimeEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit MockTimeEnv(Env* base) : EnvWrapper(base) {}
|
||||
|
||||
virtual Status GetCurrentTime(int64_t* time) override {
|
||||
assert(time != nullptr);
|
||||
assert(current_time_ <=
|
||||
static_cast<uint64_t>(std::numeric_limits<int64_t>::max()));
|
||||
*time = static_cast<int64_t>(current_time_);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual uint64_t NowMicros() override {
|
||||
assert(current_time_ <= std::numeric_limits<uint64_t>::max() / 1000000);
|
||||
return current_time_ * 1000000;
|
||||
}
|
||||
|
||||
virtual uint64_t NowNanos() override {
|
||||
assert(current_time_ <= std::numeric_limits<uint64_t>::max() / 1000000000);
|
||||
return current_time_ * 1000000000;
|
||||
}
|
||||
|
||||
void set_current_time(uint64_t time) {
|
||||
assert(time >= current_time_);
|
||||
current_time_ = time;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<uint64_t> current_time_{0};
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/mock_time_env.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@ -80,7 +81,17 @@ class RepeatableThread {
|
||||
cond_var_.SignalAll();
|
||||
#endif
|
||||
while (running_) {
|
||||
#ifndef NDEBUG
|
||||
if (dynamic_cast<MockTimeEnv*>(env_) != nullptr) {
|
||||
// MockTimeEnv is used. Since it is not easy to mock TimedWait,
|
||||
// we wait without timeout to wait for TEST_WaitForRun to wake us up.
|
||||
cond_var_.Wait();
|
||||
} else {
|
||||
cond_var_.TimedWait(wait_until);
|
||||
}
|
||||
#else
|
||||
cond_var_.TimedWait(wait_until);
|
||||
#endif
|
||||
if (env_->NowMicros() >= wait_until) {
|
||||
break;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user