From 36f7775a670da00735843e208b12d8f8705303ef Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 24 Dec 2018 22:20:00 -0800 Subject: [PATCH] Delay requests if the document has not not indexed (#176) This fixes a plethora of "not indexed" errors when the document has not been indexed. * Message handler throws NotIndexed if not overdue * The message is put into backlog and tagged with backlog_path * path2backlog[path] tracks backlog associated with document `path` * The backlog is cleared when the index is merged * backlog[0] is forced to run if it becomes overdue --- src/config.hh | 10 ++++- src/lsp.hh | 4 +- src/message_handler.cc | 38 ++++++++++++----- src/message_handler.hh | 12 +++++- src/messages/ccls_call.cc | 3 +- src/messages/ccls_inheritance.cc | 6 +-- src/messages/ccls_member.cc | 7 +--- src/messages/ccls_navigate.cc | 4 +- src/messages/ccls_vars.cc | 4 +- src/messages/initialize.cc | 2 +- src/messages/textDocument_code.cc | 17 +++----- src/messages/textDocument_completion.cc | 2 +- src/messages/textDocument_definition.cc | 21 +++------- src/messages/textDocument_document.cc | 15 ++----- src/messages/textDocument_foldingRange.cc | 7 +--- src/messages/textDocument_formatting.cc | 15 ++----- src/messages/textDocument_hover.cc | 7 +--- src/messages/textDocument_references.cc | 7 +--- src/messages/textDocument_rename.cc | 4 +- src/messages/textDocument_signatureHelp.cc | 5 +-- src/pipeline.cc | 48 ++++++++++++++++++++-- src/threaded_queue.hh | 9 ++++ 22 files changed, 142 insertions(+), 105 deletions(-) diff --git a/src/config.hh b/src/config.hh index 055da31c..073071db 100644 --- a/src/config.hh +++ b/src/config.hh @@ -230,6 +230,12 @@ struct Config { std::vector whitelist; } index; + struct Request { + // If the document of a request has not been indexed, wait up to this many + // milleseconds before reporting error. + int64_t timeout = 5000; + } request; + struct Session { int maxNum = 10; } session; @@ -266,12 +272,14 @@ REFLECT_STRUCT(Config::Index, blacklist, comments, initialBlacklist, initialWhitelist, multiVersion, multiVersionBlacklist, multiVersionWhitelist, onChange, threads, trackDependency, whitelist); +REFLECT_STRUCT(Config::Request, timeout); REFLECT_STRUCT(Config::Session, maxNum); REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort); REFLECT_STRUCT(Config::Xref, maxNum); REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory, cacheDirectory, cacheFormat, clang, client, codeLens, completion, - diagnostics, highlight, index, session, workspaceSymbol, xref); + diagnostics, highlight, index, request, session, workspaceSymbol, + xref); extern Config *g_config; diff --git a/src/lsp.hh b/src/lsp.hh index 212d49e0..ad77f993 100644 --- a/src/lsp.hh +++ b/src/lsp.hh @@ -9,8 +9,8 @@ #include +#include #include -#include namespace ccls { struct RequestId { @@ -31,6 +31,8 @@ struct InMessage { std::string method; std::unique_ptr message; std::unique_ptr document; + std::chrono::steady_clock::time_point deadline; + std::string backlog_path; }; enum class ErrorCode { diff --git a/src/message_handler.cc b/src/message_handler.cc index 08d5d1b2..7179931d 100644 --- a/src/message_handler.cc +++ b/src/message_handler.cc @@ -97,11 +97,8 @@ struct ScanLineEvent { }; } // namespace -void ReplyOnce::NotReady(bool file) { - if (file) - Error(ErrorCode::InvalidRequest, "not opened"); - else - Error(ErrorCode::InternalError, "not indexed"); +void ReplyOnce::NotOpened(std::string_view path) { + Error(ErrorCode::InvalidRequest, std::string(path) + " is not opened"); } void ReplyOnce::ReplyLocationLink(std::vector &result) { @@ -203,13 +200,11 @@ MessageHandler::MessageHandler() { void MessageHandler::Run(InMessage &msg) { rapidjson::Document &doc = *msg.document; - rapidjson::Value param; + rapidjson::Value null; auto it = doc.FindMember("params"); - if (it != doc.MemberEnd()) - param = it->value; - JsonReader reader(¶m); + JsonReader reader(it != doc.MemberEnd() ? &it->value : &null); if (msg.id.Valid()) { - ReplyOnce reply{msg.id}; + ReplyOnce reply{*this, msg.id}; auto it = method2request.find(msg.method); if (it != method2request.end()) { try { @@ -218,6 +213,8 @@ void MessageHandler::Run(InMessage &msg) { reply.Error(ErrorCode::InvalidParams, "invalid params of " + msg.method + ": expected " + ex.what() + " for " + reader.GetPath()); + } catch (NotIndexed &) { + throw; } catch (...) { reply.Error(ErrorCode::InternalError, "failed to process " + msg.method); } @@ -237,7 +234,8 @@ void MessageHandler::Run(InMessage &msg) { } } -QueryFile *MessageHandler::FindFile(const std::string &path, int *out_file_id) { +QueryFile *MessageHandler::FindFile(const std::string &path, + int *out_file_id) { QueryFile *ret = nullptr; auto it = db->name2file_id.find(LowerPathIfInsensitive(path)); if (it != db->name2file_id.end()) { @@ -254,6 +252,24 @@ QueryFile *MessageHandler::FindFile(const std::string &path, int *out_file_id) { return ret; } +std::pair +MessageHandler::FindOrFail(const std::string &path, ReplyOnce &reply, + int *out_file_id) { + WorkingFile *wf = wfiles->GetFile(path); + if (!wf) { + reply.NotOpened(path); + return {nullptr, nullptr}; + } + QueryFile *file = FindFile(path, out_file_id); + if (!file) { + if (!overdue) + throw NotIndexed{path}; + reply.Error(ErrorCode::InvalidRequest, "not indexed"); + return {nullptr, nullptr}; + } + return {file, wf}; +} + void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) { CclsSetSkippedRanges params; params.uri = DocumentUri::FromPath(wfile->filename); diff --git a/src/message_handler.hh b/src/message_handler.hh index b0a3747c..949fc242 100644 --- a/src/message_handler.hh +++ b/src/message_handler.hh @@ -199,7 +199,13 @@ REFLECT_STRUCT(Diagnostic, range, severity, code, source, message); REFLECT_STRUCT(ShowMessageParam, type, message); REFLECT_UNDERLYING_B(LanguageId); +struct NotIndexed { + std::string path; +}; +struct MessageHandler; + struct ReplyOnce { + MessageHandler &handler; RequestId id; template void operator()(Res &&result) const { if (id.Valid()) @@ -210,7 +216,7 @@ struct ReplyOnce { if (id.Valid()) pipeline::ReplyError(id, [&](JsonWriter &w) { Reflect(w, err); }); } - void NotReady(bool file); + void NotOpened(std::string_view path); void ReplyLocationLink(std::vector &result); }; @@ -225,10 +231,14 @@ struct MessageHandler { llvm::StringMap> method2notification; llvm::StringMap> method2request; + bool overdue = false; MessageHandler(); void Run(InMessage &msg); QueryFile *FindFile(const std::string &path, int *out_file_id = nullptr); + std::pair FindOrFail(const std::string &path, + ReplyOnce &reply, + int *out_file_id = nullptr); private: void Bind(const char *method, void (MessageHandler::*handler)(JsonReader &)); diff --git a/src/messages/ccls_call.cc b/src/messages/ccls_call.cc index e0ed636c..b487d36b 100644 --- a/src/messages/ccls_call.cc +++ b/src/messages/ccls_call.cc @@ -188,8 +188,7 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) { Expand(this, &*result, param.callee, param.callType, param.qualified, param.levels); } else { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) return; for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { diff --git a/src/messages/ccls_inheritance.cc b/src/messages/ccls_inheritance.cc index f3b11b09..0a55f734 100644 --- a/src/messages/ccls_inheritance.cc +++ b/src/messages/ccls_inheritance.cc @@ -134,10 +134,10 @@ void Inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) { Expand(m, &*result, param.derived, param.qualified, param.levels))) result.reset(); } else { - QueryFile *file = m->FindFile(param.textDocument.uri.GetPath()); - if (!file) + auto [file, wf] = m->FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) { return; - WorkingFile *wf = m->wfiles->GetFile(file->def->path); + } for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) if (sym.kind == Kind::Func || sym.kind == Kind::Type) { result = BuildInitial(m, sym, param.derived, param.qualified, diff --git a/src/messages/ccls_member.cc b/src/messages/ccls_member.cc index de09a75e..7f2dfe49 100644 --- a/src/messages/ccls_member.cc +++ b/src/messages/ccls_member.cc @@ -269,12 +269,9 @@ void MessageHandler::ccls_member(JsonReader &reader, ReplyOnce &reply) { param.levels, param.kind))) result.reset(); } else { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { switch (sym.kind) { case Kind::Func: diff --git a/src/messages/ccls_navigate.cc b/src/messages/ccls_navigate.cc index d09d57a8..2afef719 100644 --- a/src/messages/ccls_navigate.cc +++ b/src/messages/ccls_navigate.cc @@ -41,10 +41,8 @@ Maybe FindParent(QueryFile *file, Pos pos) { void MessageHandler::ccls_navigate(JsonReader &reader, ReplyOnce &reply) { Param param; Reflect(reader, param); - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } Position ls_pos = param.position; diff --git a/src/messages/ccls_vars.cc b/src/messages/ccls_vars.cc index 01c8f403..020508cd 100644 --- a/src/messages/ccls_vars.cc +++ b/src/messages/ccls_vars.cc @@ -19,10 +19,8 @@ REFLECT_STRUCT(Param, textDocument, position, kind); void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) { Param param; Reflect(reader, param); - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index f87264ee..7cccd1b8 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -377,7 +377,7 @@ void MessageHandler::initialize(JsonReader &reader, ReplyOnce &reply) { void StandaloneInitialize(MessageHandler &handler, const std::string &root) { InitializeParam param; param.rootUri = DocumentUri::FromPath(root); - ReplyOnce reply; + ReplyOnce reply{handler}; Initialize(&handler, param, reply); } diff --git a/src/messages/textDocument_code.cc b/src/messages/textDocument_code.cc index 4d288f03..b3aa83df 100644 --- a/src/messages/textDocument_code.cc +++ b/src/messages/textDocument_code.cc @@ -35,11 +35,9 @@ REFLECT_STRUCT(CodeAction, title, kind, edit); } void MessageHandler::textDocument_codeAction(CodeActionParam ¶m, ReplyOnce &reply) { - WorkingFile *wf = wfiles->GetFile(param.textDocument.uri.GetPath()); - if (!wf) { - reply.NotReady(true); + WorkingFile *wf = FindOrFail(param.textDocument.uri.GetPath(), reply).second; + if (!wf) return; - } std::vector result; std::vector diagnostics; wfiles->WithLock([&]() { diagnostics = wf->diagnostics; }); @@ -96,16 +94,13 @@ struct CommonCodeLensParams { void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } std::vector result; - auto Add = [&](const char *singular, Cmd_xref show, Range range, int num, - bool force_display = false) { + auto Add = [&, wf = wf](const char *singular, Cmd_xref show, Range range, + int num, bool force_display = false) { if (!num && !force_display) return; std::optional ls_range = GetLsRange(wf, range); diff --git a/src/messages/textDocument_completion.cc b/src/messages/textDocument_completion.cc index 7a95be03..e3d29966 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -447,7 +447,7 @@ void MessageHandler::textDocument_completion(CompletionParam ¶m, std::string path = param.textDocument.uri.GetPath(); WorkingFile *wf = wfiles->GetFile(path); if (!wf) { - reply.NotReady(true); + reply.NotOpened(path); return; } diff --git a/src/messages/textDocument_definition.cc b/src/messages/textDocument_definition.cc index b643b93d..b63cac40 100644 --- a/src/messages/textDocument_definition.cc +++ b/src/messages/textDocument_definition.cc @@ -36,12 +36,9 @@ std::vector GetNonDefDeclarationTargets(DB *db, SymbolRef sym) { void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m, ReplyOnce &reply) { int file_id; - QueryFile *file = FindFile(param.textDocument.uri.GetPath(), &file_id); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); + if (!wf) return; - } std::vector result; Position &ls_pos = param.position; @@ -57,12 +54,9 @@ void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m, void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m, ReplyOnce &reply) { int file_id; - QueryFile *file = FindFile(param.textDocument.uri.GetPath(), &file_id); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); + if (!wf) return; - } std::vector result; Maybe on_def; @@ -178,12 +172,9 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m, void MessageHandler::textDocument_typeDefinition( TextDocumentPositionParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!file) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!file) return; - } std::vector result; auto Add = [&](const QueryType &type) { diff --git a/src/messages/textDocument_document.cc b/src/messages/textDocument_document.cc index 5aa33afa..2a345276 100644 --- a/src/messages/textDocument_document.cc +++ b/src/messages/textDocument_document.cc @@ -32,12 +32,9 @@ REFLECT_STRUCT(DocumentHighlight, range, kind, role); void MessageHandler::textDocument_documentHighlight( TextDocumentPositionParam ¶m, ReplyOnce &reply) { int file_id; - QueryFile *file = FindFile(param.textDocument.uri.GetPath(), &file_id); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); + if (!wf) return; - } std::vector result; std::vector syms = @@ -78,10 +75,8 @@ REFLECT_STRUCT(DocumentLink, range, target); void MessageHandler::textDocument_documentLink(TextDocumentParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } @@ -153,10 +148,8 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader, Reflect(reader, param); int file_id; - QueryFile *file = FindFile(param.textDocument.uri.GetPath(), &file_id); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply, &file_id); if (!wf) { - reply.NotReady(file); return; } diff --git a/src/messages/textDocument_foldingRange.cc b/src/messages/textDocument_foldingRange.cc index 67fb0c0a..a3f1981c 100644 --- a/src/messages/textDocument_foldingRange.cc +++ b/src/messages/textDocument_foldingRange.cc @@ -31,12 +31,9 @@ REFLECT_STRUCT(FoldingRange, startLine, startCharacter, endLine, endCharacter, void MessageHandler::textDocument_foldingRange(TextDocumentParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } std::vector result; std::optional ls_range; diff --git a/src/messages/textDocument_formatting.cc b/src/messages/textDocument_formatting.cc index 37bd7c46..ba46313d 100644 --- a/src/messages/textDocument_formatting.cc +++ b/src/messages/textDocument_formatting.cc @@ -80,21 +80,16 @@ void Format(ReplyOnce &reply, WorkingFile *wfile, tooling::Range range) { void MessageHandler::textDocument_formatting(DocumentFormattingParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } Format(reply, wf, {0, (unsigned)wf->buffer_content.size()}); } void MessageHandler::textDocument_onTypeFormatting( DocumentOnTypeFormattingParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } std::string_view code = wf->buffer_content; @@ -107,10 +102,8 @@ void MessageHandler::textDocument_onTypeFormatting( void MessageHandler::textDocument_rangeFormatting( DocumentRangeFormattingParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } std::string_view code = wf->buffer_content; diff --git a/src/messages/textDocument_hover.cc b/src/messages/textDocument_hover.cc index 8964bfda..50cbf620 100644 --- a/src/messages/textDocument_hover.cc +++ b/src/messages/textDocument_hover.cc @@ -81,12 +81,9 @@ GetHover(DB *db, LanguageId lang, SymbolRef sym, int file_id) { void MessageHandler::textDocument_hover(TextDocumentPositionParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } Hover result; for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { diff --git a/src/messages/textDocument_references.cc b/src/messages/textDocument_references.cc index d13d07af..8f10e206 100644 --- a/src/messages/textDocument_references.cc +++ b/src/messages/textDocument_references.cc @@ -33,12 +33,9 @@ void MessageHandler::textDocument_references(JsonReader &reader, ReplyOnce &reply) { ReferenceParam param; Reflect(reader, param); - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; - if (!wf) { - reply.NotReady(file); + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); + if (!wf) return; - } for (auto &folder : param.folders) EnsureEndsInSlash(folder); diff --git a/src/messages/textDocument_rename.cc b/src/messages/textDocument_rename.cc index 64bb9d36..a2fa9cf2 100644 --- a/src/messages/textDocument_rename.cc +++ b/src/messages/textDocument_rename.cc @@ -44,10 +44,8 @@ WorkspaceEdit BuildWorkspaceEdit(DB *db, WorkingFiles *wfiles, SymbolRef sym, } // namespace void MessageHandler::textDocument_rename(RenameParam ¶m, ReplyOnce &reply) { - QueryFile *file = FindFile(param.textDocument.uri.GetPath()); - WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + auto [file, wf] = FindOrFail(param.textDocument.uri.GetPath(), reply); if (!wf) { - reply.NotReady(file); return; } diff --git a/src/messages/textDocument_signatureHelp.cc b/src/messages/textDocument_signatureHelp.cc index 64e6ad80..b0b7955c 100644 --- a/src/messages/textDocument_signatureHelp.cc +++ b/src/messages/textDocument_signatureHelp.cc @@ -140,12 +140,11 @@ public: void MessageHandler::textDocument_signatureHelp( TextDocumentPositionParam ¶m, ReplyOnce &reply) { static CompleteConsumerCache cache; - - std::string path = param.textDocument.uri.GetPath(); Position begin_pos = param.position; + std::string path = param.textDocument.uri.GetPath(); WorkingFile *wf = wfiles->GetFile(path); if (!wf) { - reply.NotReady(true); + reply.NotOpened(path); return; } { diff --git a/src/pipeline.cc b/src/pipeline.cc index d4a3f788..481b09f7 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -20,7 +20,6 @@ #include #include #include -using namespace llvm; #include #include @@ -29,6 +28,8 @@ using namespace llvm; #ifndef _WIN32 #include #endif +using namespace llvm; +namespace chrono = std::chrono; namespace ccls { namespace { @@ -515,8 +516,11 @@ void LaunchStdin() { if (method.empty()) continue; bool should_exit = method == "exit"; + // g_config is not available before "initialize". Use 0 in that case. on_request->PushBack( - {id, std::move(method), std::move(message), std::move(document)}); + {id, std::move(method), std::move(message), std::move(document), + chrono::steady_clock::now() + + chrono::milliseconds(g_config ? g_config->request.timeout : 0)}); if (should_exit) break; @@ -578,11 +582,34 @@ void MainLoop() { handler.include_complete = &include_complete; bool has_indexed = false; + std::deque backlog; + StringMap> path2backlog; while (true) { + if (backlog.size()) { + auto now = chrono::steady_clock::now(); + handler.overdue = true; + while (backlog.size()) { + if (backlog[0].backlog_path.size()) { + if (now < backlog[0].deadline) + break; + handler.Run(backlog[0]); + path2backlog[backlog[0].backlog_path].pop_front(); + } + backlog.pop_front(); + } + handler.overdue = false; + } + std::vector messages = on_request->DequeueAll(); bool did_work = messages.size(); for (InMessage &message : messages) - handler.Run(message); + try { + handler.Run(message); + } catch (NotIndexed &ex) { + backlog.push_back(std::move(message)); + backlog.back().backlog_path = ex.path; + path2backlog[ex.path].push_back(&backlog.back()); + } bool indexed = false; for (int i = 20; i--;) { @@ -592,6 +619,16 @@ void MainLoop() { did_work = true; indexed = true; Main_OnIndexed(&db, &wfiles, &*update); + if (update->files_def_update) { + auto it = path2backlog.find(update->files_def_update->first.path); + if (it != path2backlog.end()) { + for (auto &message : it->second) { + handler.Run(*message); + message->backlog_path.clear(); + } + path2backlog.erase(it); + } + } } if (did_work) { @@ -603,7 +640,10 @@ void MainLoop() { FreeUnusedMemory(); has_indexed = false; } - main_waiter->Wait(quit, on_indexed, on_request); + if (backlog.empty()) + main_waiter->Wait(quit, on_indexed, on_request); + else + main_waiter->WaitUntil(backlog[0].deadline, on_indexed, on_request); } } diff --git a/src/threaded_queue.hh b/src/threaded_queue.hh index e81638c9..2d55dbfe 100644 --- a/src/threaded_queue.hh +++ b/src/threaded_queue.hh @@ -6,6 +6,7 @@ #include "utils.hh" #include +#include #include #include #include @@ -64,6 +65,14 @@ struct MultiQueueWaiter { } return true; } + + template + void WaitUntil(std::chrono::steady_clock::time_point t, + BaseThreadQueue... queues) { + MultiQueueLock l(queues...); + if (!HasState({queues...})) + cv.wait_until(l, t); + } }; // A threadsafe-queue. http://stackoverflow.com/a/16075550