Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions db/db_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,10 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname)
background_compaction_scheduled_(false),
manual_compaction_(nullptr),
versions_(new VersionSet(dbname_, &options_, table_cache_,
&internal_comparator_)) {}
&internal_comparator_)) {
// Initialize mutex wrapper used for lifetime extension of iterators
mutex_state_ = std::make_shared<SharedMutexState>();
}

DBImpl::~DBImpl() {
// Wait for background work to finish.
Expand Down Expand Up @@ -1058,22 +1061,28 @@ Status DBImpl::DoCompactionWork(CompactionState* compact) {
namespace {

struct IterState {
port::Mutex* const mu;
Version* const version GUARDED_BY(mu);
MemTable* const mem GUARDED_BY(mu);
MemTable* const imm GUARDED_BY(mu);

IterState(port::Mutex* mutex, MemTable* mem, MemTable* imm, Version* version)
: mu(mutex), version(version), mem(mem), imm(imm) {}
std::shared_ptr<SharedMutexState> shared; // shared mutex state
Version* const version GUARDED_BY(shared);
MemTable* const mem GUARDED_BY(shared);
MemTable* const imm GUARDED_BY(shared);

IterState(std::shared_ptr<SharedMutexState> s, MemTable* mem, MemTable* imm,
Version* version)
: shared(std::move(s)), version(version), mem(mem), imm(imm) {}
};

static void CleanupIteratorState(void* arg1, void* arg2) {
IterState* state = reinterpret_cast<IterState*>(arg1);
state->mu->Lock();
// Use the shared mutex if available to guard cleanup; otherwise skip locking.
if (state->shared) {
state->shared->mutex.Lock();
}
state->mem->Unref();
if (state->imm != nullptr) state->imm->Unref();
state->version->Unref();
state->mu->Unlock();
if (state->shared) {
state->shared->mutex.Unlock();
}
delete state;
}

Expand All @@ -1098,7 +1107,7 @@ Iterator* DBImpl::NewInternalIterator(const ReadOptions& options,
NewMergingIterator(&internal_comparator_, &list[0], list.size());
versions_->current()->Ref();

IterState* cleanup = new IterState(&mutex_, mem_, imm_, versions_->current());
IterState* cleanup = new IterState(mutex_state_, mem_, imm_, versions_->current());
internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, nullptr);

*seed = ++seed_;
Expand Down
11 changes: 11 additions & 0 deletions db/db_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <deque>
#include <set>
#include <string>
#include <memory>

#include "db/dbformat.h"
#include "db/log_writer.h"
Expand All @@ -20,6 +21,14 @@

namespace leveldb {

// Lightweight shared state wrapper to extend the lifetime of the mutex used by
// iterators. This helps prevent Use-After-Free scenarios when a DB instance is
// destroyed while iterators created from it are still alive.
struct SharedMutexState {
port::Mutex mutex;
};


class MemTable;
class TableCache;
class Version;
Expand Down Expand Up @@ -172,6 +181,8 @@ class DBImpl : public DB {

// State below is protected by mutex_
port::Mutex mutex_;
// Shared state wrapper to extend mutex lifetime across iterators.
std::shared_ptr<SharedMutexState> mutex_state_;
std::atomic<bool> shutting_down_;
port::CondVar background_work_finished_signal_ GUARDED_BY(mutex_);
MemTable* mem_;
Expand Down