mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 00:55:08 +00:00
wip
This commit is contained in:
parent
56135f615c
commit
243630ca2e
@ -94,7 +94,6 @@ REGISTER_IPC_MESSAGE(Ipc_CancelRequest);
|
|||||||
|
|
||||||
bool QueryDbMainLoop(Config* config,
|
bool QueryDbMainLoop(Config* config,
|
||||||
QueryDatabase* db,
|
QueryDatabase* db,
|
||||||
bool* exit_when_idle,
|
|
||||||
MultiQueueWaiter* waiter,
|
MultiQueueWaiter* waiter,
|
||||||
Project* project,
|
Project* project,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
@ -142,7 +141,6 @@ bool QueryDbMainLoop(Config* config,
|
|||||||
void RunQueryDbThread(const std::string& bin_name,
|
void RunQueryDbThread(const std::string& bin_name,
|
||||||
Config* config,
|
Config* config,
|
||||||
MultiQueueWaiter* waiter) {
|
MultiQueueWaiter* waiter) {
|
||||||
bool exit_when_idle = false;
|
|
||||||
Project project;
|
Project project;
|
||||||
SemanticHighlightSymbolCache semantic_cache;
|
SemanticHighlightSymbolCache semantic_cache;
|
||||||
WorkingFiles working_files;
|
WorkingFiles working_files;
|
||||||
@ -161,6 +159,7 @@ void RunQueryDbThread(const std::string& bin_name,
|
|||||||
auto non_global_code_complete_cache = MakeUnique<CodeCompleteCache>();
|
auto non_global_code_complete_cache = MakeUnique<CodeCompleteCache>();
|
||||||
auto signature_cache = MakeUnique<CodeCompleteCache>();
|
auto signature_cache = MakeUnique<CodeCompleteCache>();
|
||||||
ImportManager import_manager;
|
ImportManager import_manager;
|
||||||
|
ImportPipelineStatus import_pipeline_status;
|
||||||
TimestampManager timestamp_manager;
|
TimestampManager timestamp_manager;
|
||||||
QueryDatabase db;
|
QueryDatabase db;
|
||||||
|
|
||||||
@ -168,11 +167,11 @@ void RunQueryDbThread(const std::string& bin_name,
|
|||||||
for (MessageHandler* handler : *MessageHandler::message_handlers) {
|
for (MessageHandler* handler : *MessageHandler::message_handlers) {
|
||||||
handler->config = config;
|
handler->config = config;
|
||||||
handler->db = &db;
|
handler->db = &db;
|
||||||
handler->exit_when_idle = &exit_when_idle;
|
|
||||||
handler->waiter = waiter;
|
handler->waiter = waiter;
|
||||||
handler->project = &project;
|
handler->project = &project;
|
||||||
handler->file_consumer_shared = &file_consumer_shared;
|
handler->file_consumer_shared = &file_consumer_shared;
|
||||||
handler->import_manager = &import_manager;
|
handler->import_manager = &import_manager;
|
||||||
|
handler->import_pipeline_status = &import_pipeline_status;
|
||||||
handler->timestamp_manager = ×tamp_manager;
|
handler->timestamp_manager = ×tamp_manager;
|
||||||
handler->semantic_cache = &semantic_cache;
|
handler->semantic_cache = &semantic_cache;
|
||||||
handler->working_files = &working_files;
|
handler->working_files = &working_files;
|
||||||
@ -188,17 +187,11 @@ void RunQueryDbThread(const std::string& bin_name,
|
|||||||
SetCurrentThreadName("querydb");
|
SetCurrentThreadName("querydb");
|
||||||
while (true) {
|
while (true) {
|
||||||
bool did_work = QueryDbMainLoop(
|
bool did_work = QueryDbMainLoop(
|
||||||
config, &db, &exit_when_idle, waiter, &project, &file_consumer_shared,
|
config, &db, waiter, &project, &file_consumer_shared,
|
||||||
&import_manager, ×tamp_manager, &semantic_cache, &working_files,
|
&import_manager, ×tamp_manager, &semantic_cache, &working_files,
|
||||||
&clang_complete, &include_complete, global_code_complete_cache.get(),
|
&clang_complete, &include_complete, global_code_complete_cache.get(),
|
||||||
non_global_code_complete_cache.get(), signature_cache.get());
|
non_global_code_complete_cache.get(), signature_cache.get());
|
||||||
|
|
||||||
// No more work left and exit request. Exit.
|
|
||||||
if (!did_work && exit_when_idle && WorkThread::num_active_threads == 0) {
|
|
||||||
LOG_S(INFO) << "Exiting; exit_when_idle is set and there is no more work";
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup and free any unused memory.
|
// Cleanup and free any unused memory.
|
||||||
FreeUnusedMemory();
|
FreeUnusedMemory();
|
||||||
|
|
||||||
@ -249,9 +242,12 @@ void LaunchStdinLoop(Config* config,
|
|||||||
if (!message)
|
if (!message)
|
||||||
return WorkThread::Result::MoreWork;
|
return WorkThread::Result::MoreWork;
|
||||||
|
|
||||||
|
// Cache |method_id| so we can access it after moving |message|.
|
||||||
|
IpcId method_id = message->method_id;
|
||||||
|
|
||||||
(*request_times)[message->method_id] = Timer();
|
(*request_times)[message->method_id] = Timer();
|
||||||
|
|
||||||
switch (message->method_id) {
|
switch (method_id) {
|
||||||
case IpcId::Initialized: {
|
case IpcId::Initialized: {
|
||||||
// TODO: don't send output until we get this notification
|
// TODO: don't send output until we get this notification
|
||||||
break;
|
break;
|
||||||
@ -262,21 +258,7 @@ void LaunchStdinLoop(Config* config,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case IpcId::Exit: {
|
case IpcId::Exit:
|
||||||
LOG_S(INFO) << "Exiting";
|
|
||||||
exit(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case IpcId::CqueryExitWhenIdle: {
|
|
||||||
// querydb needs to know to exit when idle. We return out of the stdin
|
|
||||||
// loop to exit the thread. If we keep parsing input stdin is likely
|
|
||||||
// closed so cquery will exit.
|
|
||||||
LOG_S(INFO) << "cquery will exit when all threads are idle";
|
|
||||||
queue->for_querydb.Enqueue(std::move(message));
|
|
||||||
return WorkThread::Result::ExitThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
case IpcId::Initialize:
|
case IpcId::Initialize:
|
||||||
case IpcId::TextDocumentDidOpen:
|
case IpcId::TextDocumentDidOpen:
|
||||||
case IpcId::CqueryTextDocumentDidView:
|
case IpcId::CqueryTextDocumentDidView:
|
||||||
@ -304,18 +286,23 @@ void LaunchStdinLoop(Config* config,
|
|||||||
case IpcId::CqueryBase:
|
case IpcId::CqueryBase:
|
||||||
case IpcId::CqueryDerived:
|
case IpcId::CqueryDerived:
|
||||||
case IpcId::CqueryIndexFile:
|
case IpcId::CqueryIndexFile:
|
||||||
case IpcId::CqueryQueryDbWaitForIdleIndexer: {
|
case IpcId::CqueryWait: {
|
||||||
queue->for_querydb.Enqueue(std::move(message));
|
queue->for_querydb.Enqueue(std::move(message));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
LOG_S(ERROR) << "Unhandled IPC message "
|
LOG_S(ERROR) << "Unhandled IPC message "
|
||||||
<< IpcIdToString(message->method_id);
|
<< IpcIdToString(method_id);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the message was to exit then querydb will take care of the actual
|
||||||
|
// exit. Stop reading from stdin since it might be detached.
|
||||||
|
if (method_id == IpcId::Exit)
|
||||||
|
return WorkThread::Result::ExitThread;
|
||||||
|
|
||||||
return WorkThread::Result::MoreWork;
|
return WorkThread::Result::MoreWork;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "file_consumer.h"
|
#include "file_consumer.h"
|
||||||
#include "import_manager.h"
|
#include "import_manager.h"
|
||||||
|
#include "import_pipeline.h"
|
||||||
#include "queue_manager.h"
|
#include "queue_manager.h"
|
||||||
#include "project.h"
|
#include "project.h"
|
||||||
#include "semantic_highlight_symbol_cache.h"
|
#include "semantic_highlight_symbol_cache.h"
|
||||||
@ -23,6 +24,7 @@ WorkThread::Result IndexMain(Config* config,
|
|||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
ImportManager* import_manager,
|
ImportManager* import_manager,
|
||||||
|
ImportPipelineStatus* status,
|
||||||
Project* project,
|
Project* project,
|
||||||
WorkingFiles* working_files,
|
WorkingFiles* working_files,
|
||||||
MultiQueueWaiter* waiter);
|
MultiQueueWaiter* waiter);
|
@ -78,6 +78,7 @@ std::vector<Index_DoIdMap> DoParseFile(
|
|||||||
// Always run this block, even if we are interactive, so we can check
|
// Always run this block, even if we are interactive, so we can check
|
||||||
// dependencies and reset files in |file_consumer_shared|.
|
// dependencies and reset files in |file_consumer_shared|.
|
||||||
IndexFile* previous_index = cache_loader->TryLoad(path);
|
IndexFile* previous_index = cache_loader->TryLoad(path);
|
||||||
|
LOG_S(ERROR) << "!! DoParseFile " << path << ", previous_index=" << previous_index;
|
||||||
if (previous_index) {
|
if (previous_index) {
|
||||||
// If none of the dependencies have changed and the index is not
|
// If none of the dependencies have changed and the index is not
|
||||||
// interactive (ie, requested by a file save), skip parsing and just load
|
// interactive (ie, requested by a file save), skip parsing and just load
|
||||||
@ -312,12 +313,14 @@ bool IndexMain_DoParse(Config* config,
|
|||||||
if (!request)
|
if (!request)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
LOG_S(INFO) << "IndexMain_DoParse request->path=" << request->path;
|
||||||
Project::Entry entry;
|
Project::Entry entry;
|
||||||
entry.filename = request->path;
|
entry.filename = request->path;
|
||||||
entry.args = request->args;
|
entry.args = request->args;
|
||||||
std::vector<Index_DoIdMap> responses = ParseFile(
|
std::vector<Index_DoIdMap> responses = ParseFile(
|
||||||
config, working_files, index, file_consumer_shared, timestamp_manager,
|
config, working_files, index, file_consumer_shared, timestamp_manager,
|
||||||
import_manager, request->is_interactive, entry, request->contents);
|
import_manager, request->is_interactive, entry, request->contents);
|
||||||
|
LOG_S(INFO) << "IndexMain_DoParse request->path=" << request->path << " responses.size()=" << responses.size();
|
||||||
|
|
||||||
// Don't bother sending an IdMap request if there are no responses.
|
// Don't bother sending an IdMap request if there are no responses.
|
||||||
if (responses.empty())
|
if (responses.empty())
|
||||||
@ -438,9 +441,12 @@ WorkThread::Result IndexMain(Config* config,
|
|||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
TimestampManager* timestamp_manager,
|
TimestampManager* timestamp_manager,
|
||||||
ImportManager* import_manager,
|
ImportManager* import_manager,
|
||||||
|
ImportPipelineStatus* status,
|
||||||
Project* project,
|
Project* project,
|
||||||
WorkingFiles* working_files,
|
WorkingFiles* working_files,
|
||||||
MultiQueueWaiter* waiter) {
|
MultiQueueWaiter* waiter) {
|
||||||
|
status->num_active_threads++;
|
||||||
|
|
||||||
EmitProgress(config);
|
EmitProgress(config);
|
||||||
|
|
||||||
// Build one index per-indexer, as building the index acquires a global lock.
|
// Build one index per-indexer, as building the index acquires a global lock.
|
||||||
@ -469,6 +475,8 @@ WorkThread::Result IndexMain(Config* config,
|
|||||||
if (!did_parse && !did_create_update && !did_load_previous)
|
if (!did_parse && !did_create_update && !did_load_previous)
|
||||||
did_merge = IndexMergeIndexUpdates();
|
did_merge = IndexMergeIndexUpdates();
|
||||||
|
|
||||||
|
status->num_active_threads--;
|
||||||
|
|
||||||
auto* queue = QueueManager::instance();
|
auto* queue = QueueManager::instance();
|
||||||
|
|
||||||
// We didn't do any work, so wait for a notification.
|
// We didn't do any work, so wait for a notification.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "file_consumer.h"
|
#include "file_consumer.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -12,6 +13,10 @@ struct QueryDatabase;
|
|||||||
struct SemanticHighlightSymbolCache;
|
struct SemanticHighlightSymbolCache;
|
||||||
struct WorkingFiles;
|
struct WorkingFiles;
|
||||||
|
|
||||||
|
struct ImportPipelineStatus {
|
||||||
|
std::atomic<int> num_active_threads;
|
||||||
|
};
|
||||||
|
|
||||||
void IndexWithTuFromCodeCompletion(
|
void IndexWithTuFromCodeCompletion(
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
ClangTranslationUnit* tu,
|
ClangTranslationUnit* tu,
|
||||||
|
@ -78,10 +78,8 @@ const char* IpcIdToString(IpcId id) {
|
|||||||
|
|
||||||
case IpcId::CqueryIndexFile:
|
case IpcId::CqueryIndexFile:
|
||||||
return "$cquery/indexFile";
|
return "$cquery/indexFile";
|
||||||
case IpcId::CqueryQueryDbWaitForIdleIndexer:
|
case IpcId::CqueryWait:
|
||||||
return "$cquery/queryDbWaitForIdleIndexer";
|
return "$cquery/wait";
|
||||||
case IpcId::CqueryExitWhenIdle:
|
|
||||||
return "$cquery/exitWhenIdle";
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(false && "missing IpcId string name");
|
assert(false && "missing IpcId string name");
|
||||||
|
@ -54,10 +54,8 @@ enum class IpcId : int {
|
|||||||
|
|
||||||
// Index the given file contents. Used in tests.
|
// Index the given file contents. Used in tests.
|
||||||
CqueryIndexFile,
|
CqueryIndexFile,
|
||||||
// Make querydb wait for the indexer to be idle. Used in tests.
|
// Wait until all cquery threads are idle. Used in tests.
|
||||||
CqueryQueryDbWaitForIdleIndexer,
|
CqueryWait,
|
||||||
// Exit after all messages have been read/processes. Used in tests.
|
|
||||||
CqueryExitWhenIdle
|
|
||||||
};
|
};
|
||||||
MAKE_ENUM_HASHABLE(IpcId)
|
MAKE_ENUM_HASHABLE(IpcId)
|
||||||
MAKE_REFLECT_TYPE_PROXY(IpcId, int)
|
MAKE_REFLECT_TYPE_PROXY(IpcId, int)
|
||||||
|
@ -38,6 +38,11 @@ bool FindFileOrFail(QueryDatabase* db,
|
|||||||
*out_file_id = QueryFileId((size_t)-1);
|
*out_file_id = QueryFileId((size_t)-1);
|
||||||
|
|
||||||
LOG_S(INFO) << "Unable to find file \"" << absolute_path << "\"";
|
LOG_S(INFO) << "Unable to find file \"" << absolute_path << "\"";
|
||||||
|
LOG_S(INFO) << "Files (size=" << db->usr_to_file.size() << "): "
|
||||||
|
<< StringJoinMap(db->usr_to_file,
|
||||||
|
[](const std::pair<Usr, QueryFileId>& entry) {
|
||||||
|
return entry.first;
|
||||||
|
});
|
||||||
|
|
||||||
if (id) {
|
if (id) {
|
||||||
Out_Error out;
|
Out_Error out;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "code_complete_cache.h"
|
#include "code_complete_cache.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "import_manager.h"
|
#include "import_manager.h"
|
||||||
|
#include "import_pipeline.h"
|
||||||
#include "include_complete.h"
|
#include "include_complete.h"
|
||||||
#include "queue_manager.h"
|
#include "queue_manager.h"
|
||||||
#include "project.h"
|
#include "project.h"
|
||||||
@ -30,11 +31,11 @@
|
|||||||
struct MessageHandler {
|
struct MessageHandler {
|
||||||
Config* config = nullptr;
|
Config* config = nullptr;
|
||||||
QueryDatabase* db = nullptr;
|
QueryDatabase* db = nullptr;
|
||||||
bool* exit_when_idle = nullptr;
|
|
||||||
MultiQueueWaiter* waiter = nullptr;
|
MultiQueueWaiter* waiter = nullptr;
|
||||||
Project* project = nullptr;
|
Project* project = nullptr;
|
||||||
FileConsumer::SharedState* file_consumer_shared = nullptr;
|
FileConsumer::SharedState* file_consumer_shared = nullptr;
|
||||||
ImportManager* import_manager = nullptr;
|
ImportManager* import_manager = nullptr;
|
||||||
|
ImportPipelineStatus* import_pipeline_status = nullptr;
|
||||||
TimestampManager* timestamp_manager = nullptr;
|
TimestampManager* timestamp_manager = nullptr;
|
||||||
SemanticHighlightSymbolCache* semantic_cache = nullptr;
|
SemanticHighlightSymbolCache* semantic_cache = nullptr;
|
||||||
WorkingFiles* working_files = nullptr;
|
WorkingFiles* working_files = nullptr;
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
#include "entry_points.h"
|
|
||||||
#include "message_handler.h"
|
|
||||||
|
|
||||||
#include <loguru.hpp>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
struct Ipc_CqueryExitWhenIdle : public IpcMessage<Ipc_CqueryExitWhenIdle> {
|
|
||||||
static constexpr IpcId kIpcId = IpcId::CqueryExitWhenIdle;
|
|
||||||
};
|
|
||||||
MAKE_REFLECT_EMPTY_STRUCT(Ipc_CqueryExitWhenIdle);
|
|
||||||
REGISTER_IPC_MESSAGE(Ipc_CqueryExitWhenIdle);
|
|
||||||
|
|
||||||
struct CqueryExitWhenIdleHandler : MessageHandler {
|
|
||||||
IpcId GetId() const override { return IpcId::CqueryExitWhenIdle; }
|
|
||||||
void Run(std::unique_ptr<BaseIpcMessage> request) override {
|
|
||||||
*exit_when_idle = true;
|
|
||||||
WorkThread::request_exit_on_idle = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
REGISTER_MESSAGE_HANDLER(CqueryExitWhenIdleHandler);
|
|
||||||
} // namespace
|
|
@ -1,6 +1,8 @@
|
|||||||
#include "message_handler.h"
|
#include "message_handler.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include <loguru/loguru.hpp>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct Ipc_CqueryIndexFile : public IpcMessage<Ipc_CqueryIndexFile> {
|
struct Ipc_CqueryIndexFile : public IpcMessage<Ipc_CqueryIndexFile> {
|
||||||
static constexpr IpcId kIpcId = IpcId::CqueryIndexFile;
|
static constexpr IpcId kIpcId = IpcId::CqueryIndexFile;
|
||||||
@ -23,6 +25,7 @@ REGISTER_IPC_MESSAGE(Ipc_CqueryIndexFile);
|
|||||||
|
|
||||||
struct CqueryIndexFileHandler : BaseMessageHandler<Ipc_CqueryIndexFile> {
|
struct CqueryIndexFileHandler : BaseMessageHandler<Ipc_CqueryIndexFile> {
|
||||||
void Run(Ipc_CqueryIndexFile* request) override {
|
void Run(Ipc_CqueryIndexFile* request) override {
|
||||||
|
LOG_S(INFO) << "Indexing file " << request->params.path;
|
||||||
QueueManager::instance()->index_request.Enqueue(Index_Request(
|
QueueManager::instance()->index_request.Enqueue(Index_Request(
|
||||||
NormalizePath(request->params.path), request->params.args,
|
NormalizePath(request->params.path), request->params.args,
|
||||||
request->params.is_interactive, request->params.contents));
|
request->params.is_interactive, request->params.contents));
|
||||||
|
@ -4,22 +4,26 @@
|
|||||||
#include <loguru.hpp>
|
#include <loguru.hpp>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct Ipc_CqueryQueryDbWaitForIdleIndexer
|
struct Ipc_CqueryWait
|
||||||
: public IpcMessage<Ipc_CqueryQueryDbWaitForIdleIndexer> {
|
: public IpcMessage<Ipc_CqueryWait> {
|
||||||
static constexpr IpcId kIpcId = IpcId::CqueryQueryDbWaitForIdleIndexer;
|
static constexpr IpcId kIpcId = IpcId::CqueryWait;
|
||||||
};
|
};
|
||||||
MAKE_REFLECT_EMPTY_STRUCT(Ipc_CqueryQueryDbWaitForIdleIndexer);
|
MAKE_REFLECT_EMPTY_STRUCT(Ipc_CqueryWait);
|
||||||
REGISTER_IPC_MESSAGE(Ipc_CqueryQueryDbWaitForIdleIndexer);
|
REGISTER_IPC_MESSAGE(Ipc_CqueryWait);
|
||||||
|
|
||||||
struct CqueryQueryDbWaitForIdleIndexerHandler : MessageHandler {
|
struct CqueryWaitHandler : MessageHandler {
|
||||||
IpcId GetId() const override {
|
IpcId GetId() const override {
|
||||||
return IpcId::CqueryQueryDbWaitForIdleIndexer;
|
return IpcId::CqueryWait;
|
||||||
}
|
}
|
||||||
void Run(std::unique_ptr<BaseIpcMessage> request) override {
|
void Run(std::unique_ptr<BaseIpcMessage> request) override {
|
||||||
|
// TODO: use status message system here, then run querydb as normal? Maybe
|
||||||
|
// this cannot be a normal message, ie, it needs to be re-entrant.
|
||||||
|
|
||||||
LOG_S(INFO) << "Waiting for idle";
|
LOG_S(INFO) << "Waiting for idle";
|
||||||
int idle_count = 0;
|
int idle_count = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
bool has_work = false;
|
bool has_work = false;
|
||||||
|
has_work |= import_pipeline_status->num_active_threads != 0;
|
||||||
has_work |= import_manager->HasActiveQuerydbImports();
|
has_work |= import_manager->HasActiveQuerydbImports();
|
||||||
has_work |= QueueManager::instance()->HasWork();
|
has_work |= QueueManager::instance()->HasWork();
|
||||||
has_work |= QueryDb_ImportMain(config, db, import_manager, semantic_cache,
|
has_work |= QueryDb_ImportMain(config, db, import_manager, semantic_cache,
|
||||||
@ -37,5 +41,5 @@ struct CqueryQueryDbWaitForIdleIndexerHandler : MessageHandler {
|
|||||||
LOG_S(INFO) << "Done waiting for idle";
|
LOG_S(INFO) << "Done waiting for idle";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
REGISTER_MESSAGE_HANDLER(CqueryQueryDbWaitForIdleIndexerHandler);
|
REGISTER_MESSAGE_HANDLER(CqueryWaitHandler);
|
||||||
} // namespace
|
} // namespace
|
@ -174,7 +174,8 @@ struct InitializeHandler : BaseMessageHandler<Ipc_InitializeRequest> {
|
|||||||
for (int i = 0; i < config->indexerCount; ++i) {
|
for (int i = 0; i < config->indexerCount; ++i) {
|
||||||
WorkThread::StartThread("indexer" + std::to_string(i), [=]() {
|
WorkThread::StartThread("indexer" + std::to_string(i), [=]() {
|
||||||
return IndexMain(config, file_consumer_shared, timestamp_manager,
|
return IndexMain(config, file_consumer_shared, timestamp_manager,
|
||||||
import_manager, project, working_files, waiter);
|
import_manager, import_pipeline_status, project,
|
||||||
|
working_files, waiter);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional.h>
|
#include "utils.h"
|
||||||
#include "work_thread.h"
|
#include "work_thread.h"
|
||||||
|
|
||||||
|
#include <optional.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@ -43,11 +45,8 @@ struct MultiQueueWaiter {
|
|||||||
// HasState() is called data gets posted but before we begin waiting for
|
// HasState() is called data gets posted but before we begin waiting for
|
||||||
// the condition variable, we will miss the notification. The timeout of 5
|
// the condition variable, we will miss the notification. The timeout of 5
|
||||||
// means that if this happens we will delay operation for 5 seconds.
|
// means that if this happens we will delay operation for 5 seconds.
|
||||||
//
|
|
||||||
// If we're trying to exit (WorkThread::request_exit_on_idle), do not
|
|
||||||
// bother waiting.
|
|
||||||
|
|
||||||
while (!HasState(queues) && !WorkThread::request_exit_on_idle) {
|
while (!HasState(queues)) {
|
||||||
std::unique_lock<std::mutex> l(m);
|
std::unique_lock<std::mutex> l(m);
|
||||||
cv.wait_for(l, std::chrono::seconds(5));
|
cv.wait_for(l, std::chrono::seconds(5));
|
||||||
}
|
}
|
||||||
|
@ -2,26 +2,17 @@
|
|||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
std::atomic<int> WorkThread::num_active_threads;
|
|
||||||
std::atomic<bool> WorkThread::request_exit_on_idle;
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void WorkThread::StartThread(const std::string& thread_name,
|
void WorkThread::StartThread(const std::string& thread_name,
|
||||||
const std::function<Result()>& entry_point) {
|
const std::function<Result()>& entry_point) {
|
||||||
new std::thread([thread_name, entry_point]() {
|
new std::thread([thread_name, entry_point]() {
|
||||||
SetCurrentThreadName(thread_name);
|
SetCurrentThreadName(thread_name);
|
||||||
|
|
||||||
++num_active_threads;
|
|
||||||
|
|
||||||
// Main loop.
|
// Main loop.
|
||||||
while (true) {
|
while (true) {
|
||||||
Result result = entry_point();
|
Result result = entry_point();
|
||||||
if (result == Result::ExitThread)
|
if (result == Result::ExitThread)
|
||||||
break;
|
break;
|
||||||
if (request_exit_on_idle && result == Result::NoWork)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--num_active_threads;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -9,13 +9,9 @@
|
|||||||
// Helper methods for starting threads that do some work. Enables test code to
|
// Helper methods for starting threads that do some work. Enables test code to
|
||||||
// wait for all work to complete.
|
// wait for all work to complete.
|
||||||
struct WorkThread {
|
struct WorkThread {
|
||||||
|
// FIXME: remove result, have entry_point run a while(true) loop.
|
||||||
enum class Result { MoreWork, NoWork, ExitThread };
|
enum class Result { MoreWork, NoWork, ExitThread };
|
||||||
|
|
||||||
// The number of active worker threads.
|
|
||||||
static std::atomic<int> num_active_threads;
|
|
||||||
// Set to true to request all work thread instances to exit.
|
|
||||||
static std::atomic<bool> request_exit_on_idle;
|
|
||||||
|
|
||||||
// Launch a new thread. |entry_point| will be called continously. It should
|
// Launch a new thread. |entry_point| will be called continously. It should
|
||||||
// return true if it there is still known work to be done.
|
// return true if it there is still known work to be done.
|
||||||
static void StartThread(const std::string& thread_name,
|
static void StartThread(const std::string& thread_name,
|
||||||
|
231
test_runner_e2e.py
Normal file → Executable file
231
test_runner_e2e.py
Normal file → Executable file
@ -1,22 +1,29 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
|
import shutil
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
|
|
||||||
|
|
||||||
CQUERY_PATH = 'x64/Debug/cquery.exe'
|
# FIXME: instead of $cquery/exitWhenIdle, just send $cquery/wait and the normal
|
||||||
|
# lsp exit. This requires renaming $cquery/queryDbWaitForIdle to just
|
||||||
|
# $cquery/wait.
|
||||||
|
|
||||||
|
CQUERY_PATH = 'build/asan/bin/cquery'
|
||||||
CACHE_DIR = 'e2e_CACHE'
|
CACHE_DIR = 'e2e_CACHE'
|
||||||
|
|
||||||
# Content-Length: ...\r\n
|
# Content-Length: ...\r\n
|
||||||
# \r\n
|
# \r\n
|
||||||
# {
|
# {
|
||||||
# "jsonrpc": "2.0",
|
# "jsonrpc": "2.0",
|
||||||
# "id": 1,
|
# "id": 1,
|
||||||
# "method": "textDocument/didOpen",
|
# "method": "textDocument/didOpen",
|
||||||
# "params": {
|
# "params": {
|
||||||
# ...
|
# ...
|
||||||
# }
|
# }
|
||||||
# }
|
# }
|
||||||
|
|
||||||
# We write test files in python. The test runner collects all python files in
|
# We write test files in python. The test runner collects all python files in
|
||||||
# the directory and executes them. The test function just creates a test object
|
# the directory and executes them. The test function just creates a test object
|
||||||
@ -25,6 +32,7 @@ CACHE_DIR = 'e2e_CACHE'
|
|||||||
# Test functions are automatically discovered; they just need to be in the
|
# Test functions are automatically discovered; they just need to be in the
|
||||||
# global environment and start with `Test_`.
|
# global environment and start with `Test_`.
|
||||||
|
|
||||||
|
|
||||||
class TestBuilder:
|
class TestBuilder:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.sent = []
|
self.sent = []
|
||||||
@ -32,28 +40,26 @@ class TestBuilder:
|
|||||||
|
|
||||||
def IndexFile(self, path, contents):
|
def IndexFile(self, path, contents):
|
||||||
"""
|
"""
|
||||||
Writes the file contents to disk so that the language server can access it.
|
Indexes the given file with contents.
|
||||||
"""
|
"""
|
||||||
self.Send({
|
self.Send({
|
||||||
'method': '$cquery/indexFile',
|
'method': '$cquery/indexFile',
|
||||||
'params': {
|
'params': {
|
||||||
'path': path,
|
'path': path,
|
||||||
'contents': contents,
|
'contents': contents,
|
||||||
'args': [
|
'args': [
|
||||||
'-xc++',
|
'-xc++',
|
||||||
'-std=c++11',
|
'-std=c++11'
|
||||||
'-isystemC:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.10.25017/include',
|
]
|
||||||
'-isystemC:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt'
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def WaitForIdle(self):
|
def WaitForIdle(self):
|
||||||
"""
|
"""
|
||||||
Blocks the querydb thread until any active imports are complete.
|
cquery will pause processing messages until it is idle.
|
||||||
"""
|
"""
|
||||||
self.Send({'method': '$cquery/queryDbWaitForIdleIndexer'})
|
self.Send({'method': '$cquery/wait'})
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def Send(self, stdin):
|
def Send(self, stdin):
|
||||||
@ -77,64 +83,69 @@ class TestBuilder:
|
|||||||
Add initialize/initialized messages.
|
Add initialize/initialized messages.
|
||||||
"""
|
"""
|
||||||
self.Send({
|
self.Send({
|
||||||
'id': 0,
|
'id': 0,
|
||||||
'method': 'initialize',
|
'method': 'initialize',
|
||||||
'params': {
|
'params': {
|
||||||
'processId': 123,
|
'processId': 123,
|
||||||
'rootUri': 'cquery',
|
'rootUri': 'cquery',
|
||||||
'capabilities': {},
|
'capabilities': {},
|
||||||
'trace': 'off',
|
'trace': 'off',
|
||||||
'initializationOptions': {
|
'initializationOptions': {
|
||||||
'cacheDirectory': CACHE_DIR,
|
'cacheDirectory': CACHE_DIR
|
||||||
'clientVersion': -1 # Disables the check
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
self.Expect({
|
self.Expect({
|
||||||
'id': 0,
|
'id': 0,
|
||||||
'result': {
|
'result': {
|
||||||
'capabilities': {
|
'capabilities': {
|
||||||
'textDocumentSync': 2,
|
'textDocumentSync': 2,
|
||||||
'hoverProvider': True,
|
'hoverProvider': True,
|
||||||
'completionProvider': {
|
'completionProvider': {
|
||||||
'resolveProvider': False,
|
'resolveProvider': False,
|
||||||
'triggerCharacters': [ '.', ':', '>', '#' ]
|
'triggerCharacters': ['.', ':', '>', '#']
|
||||||
},
|
},
|
||||||
'signatureHelpProvider': {
|
'signatureHelpProvider': {
|
||||||
'triggerCharacters': [ '(', ',' ]
|
'triggerCharacters': ['(', ',']
|
||||||
},
|
},
|
||||||
'definitionProvider': True,
|
'definitionProvider': True,
|
||||||
'referencesProvider': True,
|
'referencesProvider': True,
|
||||||
'documentHighlightProvider': True,
|
'documentHighlightProvider': True,
|
||||||
'documentSymbolProvider': True,
|
'documentSymbolProvider': True,
|
||||||
'workspaceSymbolProvider': True,
|
'workspaceSymbolProvider': True,
|
||||||
'codeActionProvider': True,
|
'codeActionProvider': True,
|
||||||
'codeLensProvider': {
|
'codeLensProvider': {
|
||||||
'resolveProvider': False
|
'resolveProvider': False
|
||||||
},
|
},
|
||||||
'documentFormattingProvider': False,
|
'documentFormattingProvider': False,
|
||||||
'documentRangeFormattingProvider': False,
|
'documentRangeFormattingProvider': False,
|
||||||
'renameProvider': True,
|
'renameProvider': True,
|
||||||
'documentLinkProvider': {
|
'documentLinkProvider': {
|
||||||
'resolveProvider': False
|
'resolveProvider': False
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
def _ExecuteTest(name, func):
|
def _ExecuteTest(name, func):
|
||||||
"""
|
"""
|
||||||
Executes a specific test.
|
Executes a specific test.
|
||||||
|
|
||||||
|func| must return a TestBuilder object.
|
|func| must return a TestBuilder object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Delete cache directory.
|
||||||
|
shutil.rmtree(CACHE_DIR, ignore_errors=True)
|
||||||
|
|
||||||
test_builder = func()
|
test_builder = func()
|
||||||
if not isinstance(test_builder, TestBuilder):
|
if not isinstance(test_builder, TestBuilder):
|
||||||
raise Exception('%s does not return a TestBuilder instance' % name)
|
raise Exception('%s does not return a TestBuilder instance' % name)
|
||||||
|
|
||||||
# Add a final exit message.
|
# Add a final exit message.
|
||||||
test_builder.Send({ 'method': '$cquery/exitWhenIdle' })
|
test_builder.Send({'method': '$cquery/wait'})
|
||||||
|
test_builder.Send({'method': 'exit'})
|
||||||
|
|
||||||
# Convert messages to a stdin byte array.
|
# Convert messages to a stdin byte array.
|
||||||
stdin = ''
|
stdin = ''
|
||||||
@ -151,7 +162,15 @@ def _ExecuteTest(name, func):
|
|||||||
start = match.span()[1]
|
start = match.span()[1]
|
||||||
length = int(match.groups()[0])
|
length = int(match.groups()[0])
|
||||||
message = string[start:start + length]
|
message = string[start:start + length]
|
||||||
messages.append(json.loads(message))
|
decoded = json.loads(message)
|
||||||
|
# Do not report '$cquery/progress' messages.
|
||||||
|
if 'method' in decoded and decoded['method'] == '$cquery/progress':
|
||||||
|
continue
|
||||||
|
# Do not report 'textDocument/publishDiagnostic' messages.
|
||||||
|
if 'method' in decoded and decoded['method'] == 'textDocument/publishDiagnostics':
|
||||||
|
continue
|
||||||
|
|
||||||
|
messages.append(decoded)
|
||||||
return messages
|
return messages
|
||||||
|
|
||||||
# Utility method to print a byte array.
|
# Utility method to print a byte array.
|
||||||
@ -160,10 +179,10 @@ def _ExecuteTest(name, func):
|
|||||||
print(line.decode('utf8'))
|
print(line.decode('utf8'))
|
||||||
|
|
||||||
# Execute program.
|
# Execute program.
|
||||||
cmd = "%s --language-server" % CQUERY_PATH
|
cmd = "%s --language-server --log-all-to-stderr" % CQUERY_PATH
|
||||||
process = Popen(shlex.split(cmd), stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
process = Popen(shlex.split(cmd), stdin=PIPE, stdout=PIPE, stderr=PIPE)
|
||||||
(stdout, stderr) = process.communicate(stdin_bytes)
|
(stdout, stderr) = process.communicate(stdin_bytes)
|
||||||
exit_code = process.wait();
|
exit_code = process.wait()
|
||||||
|
|
||||||
# Check if test succeeded.
|
# Check if test succeeded.
|
||||||
actual = GetMessages(stdout.decode('utf8'))
|
actual = GetMessages(stdout.decode('utf8'))
|
||||||
@ -215,6 +234,7 @@ def _DiscoverTests():
|
|||||||
continue
|
continue
|
||||||
yield (name, value)
|
yield (name, value)
|
||||||
|
|
||||||
|
|
||||||
def _RunTests():
|
def _RunTests():
|
||||||
"""
|
"""
|
||||||
Executes all tests.
|
Executes all tests.
|
||||||
@ -223,55 +243,62 @@ def _RunTests():
|
|||||||
_ExecuteTest(name, func)
|
_ExecuteTest(name, func)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### EXAMPLE TESTS ####
|
#### EXAMPLE TESTS ####
|
||||||
|
|
||||||
|
|
||||||
class lsSymbolKind:
|
class lsSymbolKind:
|
||||||
Function = 1
|
Function = 1
|
||||||
|
|
||||||
|
|
||||||
def lsSymbolInfo(name, position, kind):
|
def lsSymbolInfo(name, position, kind):
|
||||||
return {
|
return {
|
||||||
'name': name,
|
'name': name,
|
||||||
'position': position,
|
'position': position,
|
||||||
'kind': kind
|
'kind': kind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def DISABLED_Test_Init():
|
def DISABLED_Test_Init():
|
||||||
return (TestBuilder()
|
return (TestBuilder()
|
||||||
.SetupCommonInit()
|
.SetupCommonInit()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def Test_Outline():
|
def Test_Outline():
|
||||||
return (TestBuilder()
|
return (TestBuilder()
|
||||||
.SetupCommonInit()
|
.SetupCommonInit()
|
||||||
# .IndexFile("file:///C%3A/Users/jacob/Desktop/cquery/foo.cc",
|
.IndexFile("foo.cc",
|
||||||
.IndexFile("foo.cc",
|
"""void foobar();""")
|
||||||
"""void foobar();""")
|
.WaitForIdle()
|
||||||
.WaitForIdle()
|
.Send({
|
||||||
.Send({
|
'id': 1,
|
||||||
'id': 1,
|
'method': 'textDocument/documentSymbol',
|
||||||
'method': 'textDocument/documentSymbol',
|
'params': {
|
||||||
'params': {
|
'textDocument': {
|
||||||
'textDocument': {
|
'uri': 'foo.cc'
|
||||||
'uri': 'C:/Users/jacob/Desktop/cquery/foo.cc'
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
.Expect({
|
||||||
# .Expect({
|
'jsonrpc': '2.0',
|
||||||
# 'jsonrpc': '2.0',
|
'id': 1, 'result': [
|
||||||
# 'id': 1,
|
{
|
||||||
# 'error': {'code': -32603, 'message': 'Unable to find file '}
|
'containerName': 'void foobar()',
|
||||||
# }))
|
'kind': 12,
|
||||||
.Expect({
|
'name': 'foobar',
|
||||||
'id': 1,
|
'location': {
|
||||||
'result': [
|
'range': {
|
||||||
lsSymbolInfo('void main()', (1, 1), lsSymbolKind.Function)
|
'start': {
|
||||||
]
|
'line': 0,
|
||||||
}))
|
'character': 5},
|
||||||
|
'end': {
|
||||||
|
'line': 0,
|
||||||
|
'character': 11
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'uri': 'file://foo.cc'
|
||||||
|
}
|
||||||
|
}]}))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
Loading…
Reference in New Issue
Block a user