/usr/bin/ld: /tmp/ccXgrLBz.o:(.data.rel.ro._ZTIN7leveldb9SimpleEnvE[_ZTIN7leveldb9SimpleEnvE]+0x10): undefined reference to `typeinfo for leveldb::Env' /usr/bin/ld: /tmp/ccXgrLBz.o:(.data.rel.ro._ZTIN7leveldb18SimpleWritableFileE[_ZTIN7leveldb18SimpleWritableFileE]+0x10): undefined reference to `typeinfo for leveldb::WritableFile' /usr/bin/ld: /tmp/ccXgrLBz.o:(.data.rel.ro._ZTIN7leveldb22SimpleRandomAccessFileE[_ZTIN7leveldb22SimpleRandomAccessFileE]+0x10): undefined reference to `typeinfo for leveldb::RandomAccessFile' /usr/bin/ld: /tmp/ccXgrLBz.o:(.data.rel.ro._ZTIN7leveldb20SimpleSequentialFileE[_ZTIN7leveldb20SimpleSequentialFileE]+0x10): undefined reference to `typeinfo for leveldb::SequentialFile'
而后调转方向,查看undefined reference to `typeinfo for leveldb::SequentialFile’错误信息的相关博客,找到了以下博客 Undefined Reference to Typeinfo 混用了no-RTTI代码和RTTI代码,查看LevelDB的CMakeLists.txt,发现确实禁用了RTTI
std::string fname = TableFileName(dbname, meta->number); if (iter->Valid()) { WritableFile* file; s = env->NewWritableFile(fname, &file); if (!s.ok()) { return s; }
TableBuilder* builder = new TableBuilder(options, file); meta->smallest.DecodeFrom(iter->key()); Slice key; for (; iter->Valid(); iter->Next()) { key = iter->key(); builder->Add(key, iter->value()); } if (!key.empty()) { meta->largest.DecodeFrom(key); }
// Finish and check for builder errors s = builder->Finish(); if (s.ok()) { meta->file_size = builder->FileSize(); assert(meta->file_size > 0); } delete builder;
// Finish and check for file errors if (s.ok()) { s = file->Sync(); } if (s.ok()) { s = file->Close(); } delete file; file = nullptr;
if (s.ok()) { // Verify that the table is usable Iterator* it = table_cache->NewIterator(ReadOptions(), meta->number, meta->file_size); s = it->status(); delete it; } }
// Check for input iterator errors if (!iter->status().ok()) { s = iter->status(); }
if (s.ok() && meta->file_size > 0) { // Keep it } else { env->RemoveFile(fname); } return s; }
关注以下两行,阅读InMemoryEnv的相关实现
1 2
s = env->NewWritableFile(fname, &file); delete file;
voidDBImpl::MaybeScheduleCompaction() { mutex_.AssertHeld(); if (background_compaction_scheduled_) { // Already scheduled } elseif (shutting_down_.load(std::memory_order_acquire)) { // DB is being deleted; no more background compactions } elseif (!bg_error_.ok()) { // Already got an error; no more changes } elseif (imm_ == nullptr && manual_compaction_ == nullptr && !versions_->NeedsCompaction()) { // No work to be done } else { background_compaction_scheduled_ = true; env_->Schedule(&DBImpl::BGWork, this); } }
// Start the background thread, if we haven't done so already. if (!started_background_thread_) { // 只开启一个线程进行合并操作 started_background_thread_ = true; std::thread background_thread(PosixEnv::BackgroundThreadEntryPoint, this); background_thread.detach(); }
// If the queue is empty, the background thread may be waiting for work. if (background_work_queue_.empty()) { background_work_cv_.Signal(); }
// Store in *result the names of the children of the specified directory. // The names are relative to "dir". // Original contents of *results are dropped. virtual Status GetChildren(const std::string& dir, std::vector<std::string>* result)= 0; // 使用场景,若GetChildren实现错误则RemoveObsoleteFiles无法移除无用文件 voidDBImpl::RemoveObsoleteFiles(){ mutex_.AssertHeld();
if (!bg_error_.ok()) { // After a background error, we don't know whether a new version may // or may not have been committed, so we cannot safely garbage collect. return; }
// Make a set of all of the live files std::set<uint64_t> live = pending_outputs_; versions_->AddLiveFiles(&live);
std::vector<std::string> filenames; env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose uint64_t number; FileType type; std::vector<std::string> files_to_delete; for (std::string& filename : filenames) { if (ParseFileName(filename, &number, &type)) { bool keep = true; switch (type) { case kLogFile: keep = ((number >= versions_->LogNumber()) || (number == versions_->PrevLogNumber())); break; case kDescriptorFile: // Keep my manifest file, and any newer incarnations' // (in case there is a race that allows other incarnations) keep = (number >= versions_->ManifestFileNumber()); break; case kTableFile: keep = (live.find(number) != live.end()); break; case kTempFile: // Any temp files that are currently being written to must // be recorded in pending_outputs_, which is inserted into "live" keep = (live.find(number) != live.end()); break; case kCurrentFile: case kDBLockFile: case kInfoLogFile: keep = true; break; }
if (!keep) { files_to_delete.push_back(std::move(filename)); if (type == kTableFile) { table_cache_->Evict(number); } Log(options_.info_log, "Delete type=%d #%lld\n", static_cast<int>(type), static_cast<unsignedlonglong>(number)); } } }
// While deleting all files unblock other threads. All files being deleted // have unique names which will not collide with newly created files and // are therefore safe to delete while allowing other threads to proceed. mutex_.Unlock(); for (const std::string& filename : files_to_delete) { env_->RemoveFile(dbname_ + "/" + filename); } mutex_.Lock(); }