mirror of
https://github.com/MaskRay/ccls.git
synced 2025-01-19 12:05:50 +00:00
Preload code completion, maintain LRU cache for multiple completion files.
This commit is contained in:
parent
2e3e1e0427
commit
cdc268d549
@ -238,9 +238,9 @@ void BuildDetailString(CXCompletionString completion_string, std::string& label,
|
||||
|
||||
void EnsureDocumentParsed(CompletionSession* session,
|
||||
std::unique_ptr<clang::TranslationUnit>* tu,
|
||||
std::unique_ptr<clang::Index>* index) {
|
||||
// Nothing to do. We already have a translation unit and an index.
|
||||
if (*tu && *index)
|
||||
clang::Index* index) {
|
||||
// Nothing to do. We already have a translation unit.
|
||||
if (*tu)
|
||||
return;
|
||||
|
||||
std::vector<std::string> args = session->file.args;
|
||||
@ -249,40 +249,51 @@ void EnsureDocumentParsed(CompletionSession* session,
|
||||
std::vector<CXUnsavedFile> unsaved = session->working_files->AsUnsavedFiles();
|
||||
|
||||
std::cerr << "[complete] Creating completion session with arguments " << StringJoin(args) << std::endl;
|
||||
*index = MakeUnique<clang::Index>(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/);
|
||||
*tu = MakeUnique<clang::TranslationUnit>(index->get(), session->file.filename, args, unsaved, Flags());
|
||||
*tu = MakeUnique<clang::TranslationUnit>(index, session->file.filename, args, unsaved, Flags());
|
||||
std::cerr << "[complete] Done creating active; did_fail=" << (*tu)->did_fail << std::endl;
|
||||
}
|
||||
|
||||
void CompletionParseMain(CompletionManager* completion_manager) {
|
||||
while (true) {
|
||||
// Fetching the completion request blocks until we have a request.
|
||||
std::unique_ptr<std::string> path = completion_manager->reparse_request.Take();
|
||||
CompletionManager::ParseRequest request = completion_manager->parse_requests_.Dequeue();
|
||||
|
||||
CompletionSession* session = completion_manager->GetOrOpenSession(*path);
|
||||
// If we don't get a session then that means we don't care about the file
|
||||
// anymore - abandon the request.
|
||||
CompletionSession* session = completion_manager->TryGetSession(request.path, false /*create_if_needed*/);
|
||||
if (!session)
|
||||
continue;
|
||||
|
||||
// If we've parsed it more recently than the request time, don't bother
|
||||
// reparsing.
|
||||
if (session->tu_last_parsed_at &&
|
||||
*session->tu_last_parsed_at > request.request_time) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::unique_ptr<clang::TranslationUnit> parsing;
|
||||
std::unique_ptr<clang::Index> parsing_index;
|
||||
EnsureDocumentParsed(session, &parsing, &session->index);
|
||||
|
||||
EnsureDocumentParsed(session, &parsing, &parsing_index);
|
||||
|
||||
// Swap out active.
|
||||
std::lock_guard<std::mutex> lock(session->usage_lock);
|
||||
session->active = std::move(parsing);
|
||||
session->active_index = std::move(parsing_index);
|
||||
// Activate new translation unit.
|
||||
// tu_last_parsed_at is only read by this thread, so it doesn't need to be under the mutex.
|
||||
session->tu_last_parsed_at = std::chrono::high_resolution_clock::now();
|
||||
std::lock_guard<std::mutex> lock(session->tu_lock);
|
||||
session->tu = std::move(parsing);
|
||||
}
|
||||
}
|
||||
|
||||
void CompletionQueryMain(CompletionManager* completion_manager) {
|
||||
while (true) {
|
||||
// Fetching the completion request blocks until we have a request.
|
||||
std::unique_ptr<CompletionManager::CompletionRequest> request = completion_manager->completion_request.Take();
|
||||
std::unique_ptr<CompletionManager::CompletionRequest> request = completion_manager->completion_request_.Take();
|
||||
std::string path = request->location.textDocument.uri.GetPath();
|
||||
|
||||
CompletionSession* session = completion_manager->TryGetSession(path, true /*create_if_needed*/);
|
||||
|
||||
std::lock_guard<std::mutex> lock(session->tu_lock);
|
||||
EnsureDocumentParsed(session, &session->tu, &session->index);
|
||||
|
||||
CompletionSession* session = completion_manager->GetOrOpenSession(request->location.textDocument.uri.GetPath());
|
||||
std::lock_guard<std::mutex> lock(session->usage_lock);
|
||||
|
||||
EnsureDocumentParsed(session, &session->active, &session->active_index);
|
||||
|
||||
// Language server is 0-based, clang is 1-based.
|
||||
unsigned line = request->location.position.line + 1;
|
||||
unsigned column = request->location.position.character + 1;
|
||||
|
||||
@ -291,18 +302,18 @@ void CompletionQueryMain(CompletionManager* completion_manager) {
|
||||
|
||||
Timer timer;
|
||||
|
||||
std::vector<CXUnsavedFile> unsaved = completion_manager->working_files->AsUnsavedFiles();
|
||||
std::vector<CXUnsavedFile> unsaved = completion_manager->working_files_->AsUnsavedFiles();
|
||||
timer.ResetAndPrint("[complete] Fetching unsaved files");
|
||||
|
||||
timer.Reset();
|
||||
unsigned const kCompleteOptions = CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments;
|
||||
CXCodeCompleteResults* cx_results = clang_codeCompleteAt(
|
||||
session->active->cx_tu,
|
||||
session->tu->cx_tu,
|
||||
session->file.filename.c_str(), line, column,
|
||||
unsaved.data(), (unsigned)unsaved.size(),
|
||||
kCompleteOptions);
|
||||
if (!cx_results) {
|
||||
std::cerr << "[complete] Code completion failed" << std::endl;
|
||||
timer.ResetAndPrint("[complete] Code completion failed");
|
||||
request->on_complete({}, {});
|
||||
continue;
|
||||
}
|
||||
@ -355,6 +366,7 @@ void CompletionQueryMain(CompletionManager* completion_manager) {
|
||||
}
|
||||
timer.ResetAndPrint("[complete] Build diagnostics");
|
||||
|
||||
|
||||
clang_disposeCodeCompleteResults(cx_results);
|
||||
timer.ResetAndPrint("[complete] clang_disposeCodeCompleteResults");
|
||||
|
||||
@ -367,12 +379,32 @@ void CompletionQueryMain(CompletionManager* completion_manager) {
|
||||
} // namespace
|
||||
|
||||
CompletionSession::CompletionSession(const Project::Entry& file, WorkingFiles* working_files)
|
||||
: file(file), working_files(working_files) {}
|
||||
: file(file), working_files(working_files), index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/) {}
|
||||
|
||||
CompletionSession::~CompletionSession() {}
|
||||
|
||||
LruSessionCache::LruSessionCache(int max_entries) : max_entries_(max_entries) {}
|
||||
|
||||
CompletionSession* LruSessionCache::TryGetEntry(const std::string& filename) {
|
||||
for (int i = 0; i < entries_.size(); ++i) {
|
||||
if (entries_[i]->file.filename == filename)
|
||||
return entries_[i].get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LruSessionCache::InsertEntry(std::unique_ptr<CompletionSession> session) {
|
||||
if (entries_.size() >= max_entries_)
|
||||
entries_.pop_back();
|
||||
entries_.insert(entries_.begin(), std::move(session));
|
||||
}
|
||||
|
||||
CompletionManager::ParseRequest::ParseRequest(const std::string& path)
|
||||
: path(path), request_time(std::chrono::high_resolution_clock::now()) {}
|
||||
|
||||
CompletionManager::CompletionManager(Config* config, Project* project, WorkingFiles* working_files)
|
||||
: config(config), project(project), working_files(working_files) {
|
||||
: config_(config), project_(project), working_files_(working_files),
|
||||
view_sessions_(kMaxViewSessions), edit_sessions_(kMaxEditSessions) {
|
||||
new std::thread([&]() {
|
||||
SetCurrentThreadName("completequery");
|
||||
CompletionQueryMain(this);
|
||||
@ -385,47 +417,81 @@ CompletionManager::CompletionManager(Config* config, Project* project, WorkingFi
|
||||
}
|
||||
|
||||
void CompletionManager::CodeComplete(const lsTextDocumentPositionParams& completion_location, const OnComplete& on_complete) {
|
||||
// completion thread will create the CompletionSession if needed.
|
||||
|
||||
auto request = MakeUnique<CompletionRequest>();
|
||||
request->location = completion_location;
|
||||
request->on_complete = on_complete;
|
||||
completion_request.Set(std::move(request));
|
||||
completion_request_.Set(std::move(request));
|
||||
}
|
||||
|
||||
CompletionSession* CompletionManager::GetOrOpenSession(const std::string& filename) {
|
||||
// Try to find existing session.
|
||||
for (auto& session : sessions) {
|
||||
if (session->file.filename == filename)
|
||||
return session.get();
|
||||
}
|
||||
void CompletionManager::NotifyView(const std::string& filename) {
|
||||
//
|
||||
// On view, we reparse only if the file has not been parsed. The existence of
|
||||
// a CompletionSession instance implies the file is already parsed or will be
|
||||
// parsed soon.
|
||||
//
|
||||
|
||||
// Create new session. Note that this will block.
|
||||
std::cerr << "[complete] Creating new code completion session for " << filename << std::endl;
|
||||
optional<Project::Entry> entry = project->FindCompilationEntryForFile(filename);
|
||||
if (!entry) {
|
||||
std::cerr << "[complete] Unable to find compilation entry" << std::endl;
|
||||
entry = Project::Entry();
|
||||
entry->filename = filename;
|
||||
}
|
||||
else {
|
||||
std::cerr << "[complete] Found compilation entry" << std::endl;
|
||||
}
|
||||
sessions.push_back(MakeUnique<CompletionSession>(*entry, working_files));
|
||||
return sessions[sessions.size() - 1].get();
|
||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
||||
|
||||
if (view_sessions_.TryGetEntry(filename))
|
||||
return;
|
||||
|
||||
std::cerr << "[complete] Creating new edit code completion session for " << filename << std::endl;
|
||||
view_sessions_.InsertEntry(MakeUnique<CompletionSession>(
|
||||
project_->FindCompilationEntryForFile(filename), working_files_));
|
||||
parse_requests_.Enqueue(ParseRequest(filename));
|
||||
}
|
||||
|
||||
void CompletionManager::UpdateActiveSession(const std::string& filename) {
|
||||
// Drop all sessions except for |filename|.
|
||||
for (auto& session : sessions) {
|
||||
if (session->file.filename == filename)
|
||||
continue;
|
||||
void CompletionManager::NotifyEdit(const std::string& filename) {
|
||||
//
|
||||
// On edit, we reparse only if the file has not been parsed. The existence of
|
||||
// a CompletionSession instance implies the file is already parsed or will be
|
||||
// parsed soon.
|
||||
//
|
||||
|
||||
std::lock_guard<std::mutex> lock(session->usage_lock);
|
||||
session->active.reset();
|
||||
session->active_index.reset();
|
||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
||||
|
||||
if (edit_sessions_.TryGetEntry(filename))
|
||||
return;
|
||||
|
||||
std::cerr << "[complete] Creating new edit code completion session for " << filename << std::endl;
|
||||
edit_sessions_.InsertEntry(MakeUnique<CompletionSession>(
|
||||
project_->FindCompilationEntryForFile(filename), working_files_));
|
||||
parse_requests_.PriorityEnqueue(ParseRequest(filename));
|
||||
}
|
||||
|
||||
void CompletionManager::NotifySave(const std::string& filename) {
|
||||
//
|
||||
// On save, always reparse.
|
||||
//
|
||||
|
||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
||||
|
||||
if (!edit_sessions_.TryGetEntry(filename)) {
|
||||
std::cerr << "[complete] Creating new edit code completion session for " << filename << std::endl;
|
||||
edit_sessions_.InsertEntry(MakeUnique<CompletionSession>(
|
||||
project_->FindCompilationEntryForFile(filename), working_files_));
|
||||
}
|
||||
|
||||
// Reparse |filename|.
|
||||
// TODO: Instead of actually reparsing it, see if we can hook into the
|
||||
// indexer and steal the translation unit from there..
|
||||
reparse_request.Set(MakeUnique<std::string>(filename));
|
||||
parse_requests_.PriorityEnqueue(ParseRequest(filename));
|
||||
}
|
||||
|
||||
CompletionSession* CompletionManager::TryGetSession(const std::string& filename, bool create_if_needed) {
|
||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
||||
|
||||
CompletionSession* session = edit_sessions_.TryGetEntry(filename);
|
||||
|
||||
if (!session)
|
||||
session = view_sessions_.TryGetEntry(filename);
|
||||
|
||||
if (!session && create_if_needed) {
|
||||
// Create new session. Default to edited_sessions_ since invoking code
|
||||
// completion almost certainly implies an edit.
|
||||
edit_sessions_.InsertEntry(MakeUnique<CompletionSession>(
|
||||
project_->FindCompilationEntryForFile(filename), working_files_));
|
||||
session = edit_sessions_.TryGetEntry(filename);
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
@ -3,66 +3,101 @@
|
||||
#include "libclangmm/Index.h"
|
||||
#include "libclangmm/TranslationUnit.h"
|
||||
#include "project.h"
|
||||
#include "threaded_queue.h"
|
||||
#include "working_files.h"
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
// TODO: rename this file to clang_completion.h/cc
|
||||
|
||||
struct CompletionSession {
|
||||
Project::Entry file;
|
||||
WorkingFiles* working_files;
|
||||
clang::Index index;
|
||||
|
||||
// Acquired when the session is being used.
|
||||
std::mutex usage_lock;
|
||||
// When |tu| was last parsed.
|
||||
optional<std::chrono::time_point<std::chrono::high_resolution_clock>> tu_last_parsed_at;
|
||||
|
||||
// Acquired when |tu| is being used.
|
||||
std::mutex tu_lock;
|
||||
|
||||
// The active translation unit.
|
||||
std::unique_ptr<clang::TranslationUnit> active;
|
||||
std::unique_ptr<clang::Index> active_index;
|
||||
|
||||
// Updated translation unit. If |is_updated_ready| is true, then |updated|
|
||||
// contains more recent state than |active| and the two should be swapped.
|
||||
//
|
||||
// TODO: implement this. Needs changes in Refresh and CodeComplete.
|
||||
//bool is_updated_ready = false;
|
||||
//std::unique_ptr<clang::TranslationUnit> updated;
|
||||
//std::unique_ptr<clang::Index> updated_index;
|
||||
std::unique_ptr<clang::TranslationUnit> tu;
|
||||
|
||||
CompletionSession(const Project::Entry& file, WorkingFiles* working_files);
|
||||
~CompletionSession();
|
||||
};
|
||||
|
||||
struct CompletionManager {
|
||||
std::vector<std::unique_ptr<CompletionSession>> sessions;
|
||||
Config* config;
|
||||
Project* project;
|
||||
WorkingFiles* working_files;
|
||||
struct LruSessionCache {
|
||||
std::vector<std::unique_ptr<CompletionSession>> entries_;
|
||||
int max_entries_;
|
||||
|
||||
LruSessionCache(int max_entries);
|
||||
|
||||
// Fetches the entry for |filename| and updates it's usage so it is less
|
||||
// likely to be evicted.
|
||||
CompletionSession* TryGetEntry(const std::string& filename);
|
||||
// Inserts an entry. Evicts the oldest unused entry if there is no space.
|
||||
void InsertEntry(std::unique_ptr<CompletionSession> session);
|
||||
};
|
||||
|
||||
struct CompletionManager {
|
||||
using OnComplete = std::function<void(NonElidedVector<lsCompletionItem> results, NonElidedVector<lsDiagnostic> diagnostics)>;
|
||||
|
||||
struct ParseRequest {
|
||||
ParseRequest(const std::string& path);
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> request_time;
|
||||
std::string path;
|
||||
};
|
||||
struct CompletionRequest {
|
||||
lsTextDocumentPositionParams location;
|
||||
OnComplete on_complete;
|
||||
};
|
||||
|
||||
AtomicObject<CompletionRequest> completion_request;
|
||||
|
||||
// Request that the given path be reparsed.
|
||||
AtomicObject<std::string> reparse_request;
|
||||
|
||||
CompletionManager(Config* config, Project* project, WorkingFiles* working_files);
|
||||
|
||||
// Start a code completion at the given location. |on_complete| will run when
|
||||
// completion results are available. |on_complete| may run on any thread.
|
||||
void CodeComplete(const lsTextDocumentPositionParams& completion_location, const OnComplete& on_complete);
|
||||
void CodeComplete(const lsTextDocumentPositionParams& completion_location,
|
||||
const OnComplete& on_complete);
|
||||
|
||||
CompletionSession* GetOrOpenSession(const std::string& filename);
|
||||
// Notify the completion manager that |filename| has been viewed and we
|
||||
// should begin preloading completion data.
|
||||
void NotifyView(const std::string& filename);
|
||||
// Notify the completion manager that |filename| has been edited.
|
||||
void NotifyEdit(const std::string& filename);
|
||||
// Notify the completion manager that |filename| has been saved. This
|
||||
// triggers a reparse.
|
||||
void NotifySave(const std::string& filename);
|
||||
|
||||
// Set the new active session. We will drop clang state for all other
|
||||
// sessions and begin reparsing the session for |filename| to ensure
|
||||
// completions are always fast.
|
||||
void UpdateActiveSession(const std::string& filename);
|
||||
CompletionSession* TryGetSession(const std::string& filename, bool create_if_needed);
|
||||
|
||||
// TODO: make these configurable.
|
||||
const int kMaxViewSessions = 3;
|
||||
const int kMaxEditSessions = 10;
|
||||
|
||||
// Global state.
|
||||
Config* config_;
|
||||
Project* project_;
|
||||
WorkingFiles* working_files_;
|
||||
|
||||
// Sessions which have never had a real text-edit applied, but are preloaded
|
||||
// to give a fast initial experience.
|
||||
LruSessionCache view_sessions_;
|
||||
// Completion sessions which have been edited.
|
||||
LruSessionCache edit_sessions_;
|
||||
// Mutex which protects |view_sessions_| and |edit_sessions_|.
|
||||
std::mutex sessions_lock_;
|
||||
|
||||
// Request a code completion at the given location.
|
||||
AtomicObject<CompletionRequest> completion_request_;
|
||||
// Parse requests. The path may already be parsed, in which case it should be
|
||||
// reparsed.
|
||||
ThreadedQueue<ParseRequest> parse_requests_;
|
||||
};
|
@ -1869,27 +1869,31 @@ bool QueryDbMainLoop(
|
||||
|
||||
Timer time;
|
||||
auto msg = static_cast<Ipc_TextDocumentDidOpen*>(message.get());
|
||||
std::string path = msg->params.textDocument.uri.GetPath();
|
||||
WorkingFile* working_file = working_files->OnOpen(msg->params);
|
||||
optional<std::string> cached_file_contents = LoadCachedFileContents(config, msg->params.textDocument.uri.GetPath());
|
||||
optional<std::string> cached_file_contents = LoadCachedFileContents(config, path);
|
||||
if (cached_file_contents)
|
||||
working_file->SetIndexContent(*cached_file_contents);
|
||||
else
|
||||
working_file->SetIndexContent(working_file->buffer_content);
|
||||
|
||||
std::unique_ptr<IndexFile> cache = LoadCachedIndex(config, msg->params.textDocument.uri.GetPath());
|
||||
std::unique_ptr<IndexFile> cache = LoadCachedIndex(config, path);
|
||||
if (cache && !cache->skipped_by_preprocessor.empty())
|
||||
PublishInactiveLines(working_file, cache->skipped_by_preprocessor);
|
||||
|
||||
time.ResetAndPrint("[querydb] Loading cached index file for DidOpen (blocks CodeLens)");
|
||||
|
||||
include_completion->AddFile(working_file->filename);
|
||||
completion_manager->NotifyView(path);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IpcId::TextDocumentDidChange: {
|
||||
auto msg = static_cast<Ipc_TextDocumentDidChange*>(message.get());
|
||||
std::string path = msg->params.textDocument.uri.GetPath();
|
||||
working_files->OnChange(msg->params);
|
||||
completion_manager->NotifyEdit(path);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1927,7 +1931,8 @@ bool QueryDbMainLoop(
|
||||
|
||||
queue_do_index->PriorityEnqueue(Index_DoIndex(index_type, project->FindCompilationEntryForFile(path), working_file->buffer_content, true /*is_interactive*/));
|
||||
}
|
||||
completion_manager->UpdateActiveSession(path);
|
||||
|
||||
completion_manager->NotifySave(path);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -2416,8 +2421,11 @@ bool QueryDbMainLoop(
|
||||
response.id = msg->id;
|
||||
|
||||
lsDocumentUri file_as_uri = msg->params.textDocument.uri;
|
||||
std::string path = file_as_uri.GetPath();
|
||||
|
||||
QueryFile* file = FindFile(db, file_as_uri.GetPath());
|
||||
completion_manager->NotifyView(path);
|
||||
|
||||
QueryFile* file = FindFile(db, path);
|
||||
if (!file) {
|
||||
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
||||
break;
|
||||
|
@ -25,7 +25,9 @@ TranslationUnit::TranslationUnit(Index* index,
|
||||
|
||||
//std::cerr << "Parsing " << filepath << " with args " << StringJoin(args) << std::endl;
|
||||
|
||||
//CXErrorCode error_code = clang_parseTranslationUnit2FullArgv(
|
||||
//cx_tu = clang_createTranslationUnitFromSourceFile(
|
||||
// index->cx_index, filepath.c_str(), args.size(), args.data(), (unsigned)unsaved_files.size(), unsaved_files.data());
|
||||
|
||||
CXErrorCode error_code = clang_parseTranslationUnit2(
|
||||
index->cx_index, filepath.c_str(), args.data(), (int)args.size(),
|
||||
unsaved_files.data(), (unsigned)unsaved_files.size(), flags, &cx_tu);
|
||||
|
@ -50,7 +50,12 @@ struct MultiQueueWaiter {
|
||||
template <class T>
|
||||
struct ThreadedQueue : public BaseThreadQueue {
|
||||
public:
|
||||
ThreadedQueue(MultiQueueWaiter* waiter) : waiter_(waiter) {}
|
||||
ThreadedQueue() {
|
||||
owned_waiter_ = MakeUnique<MultiQueueWaiter>();
|
||||
waiter_ = owned_waiter_.get();
|
||||
}
|
||||
|
||||
explicit ThreadedQueue(MultiQueueWaiter* waiter) : waiter_(waiter) {}
|
||||
|
||||
// Add an element to the front of the queue.
|
||||
void PriorityEnqueue(T&& t) {
|
||||
@ -88,27 +93,23 @@ public:
|
||||
return priority_.empty() && queue_.empty();
|
||||
}
|
||||
|
||||
/*
|
||||
// Get the "front"-element.
|
||||
// If the queue is empty, wait untill an element is avaiable.
|
||||
// Get the first element from the queue. Blocks until one is available.
|
||||
T Dequeue() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
while (priority_.empty() && queue_.empty()) {
|
||||
// release lock as long as the wait and reaquire it afterwards.
|
||||
cv_.wait(lock);
|
||||
}
|
||||
waiter_->cv.wait(lock, [&]() {
|
||||
return !priority_.empty() || !queue_.empty();
|
||||
});
|
||||
|
||||
if (!priority_.empty()) {
|
||||
auto val = std::move(priority_.front());
|
||||
priority_.pop();
|
||||
return val;
|
||||
return std::move(val);
|
||||
}
|
||||
|
||||
auto val = std::move(queue_.front());
|
||||
queue_.pop();
|
||||
return val;
|
||||
return std::move(val);
|
||||
}
|
||||
*/
|
||||
|
||||
// Get the first element from the queue without blocking. Returns a null
|
||||
// value if the queue is empty.
|
||||
@ -133,4 +134,5 @@ public:
|
||||
mutable std::mutex mutex_;
|
||||
std::queue<T> queue_;
|
||||
MultiQueueWaiter* waiter_;
|
||||
std::unique_ptr<MultiQueueWaiter> owned_waiter_;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user