Fix data race #1

Summary:
This is first in a series of diffs that fixes data races detected by thread sanitizer.

Here the problem is that we call Ref() on a column family during a single-threaded write, without holding a mutex.

Test Plan: TSAN is no longer complaining about LevelLimitReopen.

Reviewers: yhchiang, rven, sdong

Reviewed By: sdong

Subscribers: dhruba, leveldb

Differential Revision: https://reviews.facebook.net/D32121
main
Igor Canadi 10 years ago
parent b08b2fe732
commit f1c8862479
  1. 4
      db/column_family.cc
  2. 15
      db/column_family.h
  3. 1
      db/flush_scheduler.h

@ -299,7 +299,7 @@ ColumnFamilyData::ColumnFamilyData(
// DB mutex held // DB mutex held
ColumnFamilyData::~ColumnFamilyData() { ColumnFamilyData::~ColumnFamilyData() {
assert(refs_ == 0); assert(refs_.load(std::memory_order_relaxed) == 0);
// remove from linked list // remove from linked list
auto prev = prev_; auto prev = prev_;
auto next = next_; auto next = next_;
@ -731,7 +731,7 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
void ColumnFamilySet::FreeDeadColumnFamilies() { void ColumnFamilySet::FreeDeadColumnFamilies() {
autovector<ColumnFamilyData*> to_delete; autovector<ColumnFamilyData*> to_delete;
for (auto cfd = dummy_cfd_->next_; cfd != dummy_cfd_; cfd = cfd->next_) { for (auto cfd = dummy_cfd_->next_; cfd != dummy_cfd_; cfd = cfd->next_) {
if (cfd->refs_ == 0) { if (cfd->refs_.load(std::memory_order_relaxed) == 0) {
to_delete.push_back(cfd); to_delete.push_back(cfd);
} }
} }

@ -134,14 +134,18 @@ class ColumnFamilyData {
// thread-safe // thread-safe
const std::string& GetName() const { return name_; } const std::string& GetName() const { return name_; }
void Ref() { ++refs_; } // Ref() can only be called whily holding a DB mutex or during a
// single-threaded write.
void Ref() { refs_.fetch_add(1, std::memory_order_relaxed); }
// will just decrease reference count to 0, but will not delete it. returns // will just decrease reference count to 0, but will not delete it. returns
// true if the ref count was decreased to zero. in that case, it can be // true if the ref count was decreased to zero. in that case, it can be
// deleted by the caller immediately, or later, by calling // deleted by the caller immediately, or later, by calling
// FreeDeadColumnFamilies() // FreeDeadColumnFamilies()
// Unref() can only be called while holding a DB mutex
bool Unref() { bool Unref() {
assert(refs_ > 0); int old_refs = refs_.fetch_sub(1, std::memory_order_relaxed);
return --refs_ == 0; assert(old_refs > 0);
return old_refs == 1;
} }
// SetDropped() can only be called under following conditions: // SetDropped() can only be called under following conditions:
@ -290,7 +294,7 @@ class ColumnFamilyData {
Version* dummy_versions_; // Head of circular doubly-linked list of versions. Version* dummy_versions_; // Head of circular doubly-linked list of versions.
Version* current_; // == dummy_versions->prev_ Version* current_; // == dummy_versions->prev_
int refs_; // outstanding references to ColumnFamilyData std::atomic<int> refs_; // outstanding references to ColumnFamilyData
bool dropped_; // true if client dropped it bool dropped_; // true if client dropped it
const InternalKeyComparator internal_comparator_; const InternalKeyComparator internal_comparator_;
@ -373,7 +377,8 @@ class ColumnFamilySet {
// dummy is never dead or dropped, so this will never be infinite // dummy is never dead or dropped, so this will never be infinite
do { do {
current_ = current_->next_; current_ = current_->next_;
} while (current_->refs_ == 0 || current_->IsDropped()); } while (current_->refs_.load(std::memory_order_relaxed) == 0 ||
current_->IsDropped());
return *this; return *this;
} }
bool operator!=(const iterator& other) { bool operator!=(const iterator& other) {

@ -23,6 +23,7 @@ class FlushScheduler {
void ScheduleFlush(ColumnFamilyData* cfd); void ScheduleFlush(ColumnFamilyData* cfd);
// Returns Ref()-ed column family. Client needs to Unref() // Returns Ref()-ed column family. Client needs to Unref()
// REQUIRES: db mutex is held (exception is single-threaded recovery)
ColumnFamilyData* GetNextColumnFamily(); ColumnFamilyData* GetNextColumnFamily();
bool Empty(); bool Empty();

Loading…
Cancel
Save