diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9b68e49..1133a33 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,11 +8,7 @@ stages: .script: &compile stage: test script: - - mkdir build - - cd build - - scan-build cmake .. - - scan-build --status-bugs make -j$(nproc) - - rm -r * + - mkdir build && cd build - cmake -DBUILD_TESTING=1 .. - make -j$(nproc) - broadwayd & CTEST_OUTPUT_ON_FAILURE=1 make test @@ -26,6 +22,20 @@ fedora: arch: image: cppit/jucipp:arch <<: *compile +static-analysis: + image: cppit/jucipp:arch + stage: test + script: + - mkdir build && cd build + - scan-build cmake .. + - scan-build --status-bugs make -j$(nproc) +thread-safety-analysis: + image: cppit/jucipp:arch + stage: test + script: + - mkdir build && cd build + - CXX=clang++ CXXFLAGS=-Werror cmake .. + - make -j$(nproc) debian-testing: image: cppit/jucipp:debian-testing <<: *compile diff --git a/CMakeLists.txt b/CMakeLists.txt index e5ae23a..96a945c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") add_compile_options(-Wno-cpp) elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options("-Wno-#warnings") + add_compile_options(-Wthread-safety) endif() if(APPLE) diff --git a/src/autocomplete.cc b/src/autocomplete.cc index 892b6e7..ce834f7 100644 --- a/src/autocomplete.cc +++ b/src/autocomplete.cc @@ -94,7 +94,12 @@ void Autocomplete::run() { } else { auto start_iter = view->get_buffer()->get_insert()->get_iter(); - if(prefix.size() > 0 && !start_iter.backward_chars(prefix.size())) { + std::size_t prefix_size; + { + LockGuard lock(prefix_mutex); + prefix_size = prefix.size(); + } + if(prefix_size > 0 && !start_iter.backward_chars(prefix_size)) { state = State::IDLE; reparse(); return; diff --git a/src/autocomplete.h b/src/autocomplete.h index a1d54cd..f96fdb7 100644 --- a/src/autocomplete.h +++ b/src/autocomplete.h @@ -1,5 +1,6 @@ #pragma once #include "dispatcher.h" +#include "mutex.h" #include "tooltips.h" #include #include @@ -16,8 +17,8 @@ class Autocomplete { public: enum class State { IDLE, STARTING, RESTARTING, CANCELED }; - Glib::ustring prefix; - std::mutex prefix_mutex; + Mutex prefix_mutex; + Glib::ustring prefix GUARDED_BY(prefix_mutex); std::vector rows; Tooltips tooltips; @@ -28,7 +29,7 @@ public: std::function is_processing = [] { return true; }; std::function reparse = [] {}; std::function cancel_reparse = [] {}; - std::function>()> get_parse_lock = [] { return nullptr; }; + std::function()> get_parse_lock = [] { return nullptr; }; std::function stop_parse = [] {}; std::function is_continue_key = [](guint) { return false; }; diff --git a/src/debug_lldb.cc b/src/debug_lldb.cc index 5d6ead0..925fe48 100644 --- a/src/debug_lldb.cc +++ b/src/debug_lldb.cc @@ -86,6 +86,8 @@ std::tuple, std::string, std::vector> Debu void Debug::LLDB::start(const std::string &command, const boost::filesystem::path &path, const std::vector> &breakpoints, const std::vector &startup_commands, const std::string &remote_host) { + LockGuard lock(mutex); + if(!debugger) { lldb::SBDebugger::Initialize(); debugger = std::make_unique(lldb::SBDebugger::Create(true, log, nullptr)); @@ -174,12 +176,14 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat process = std::make_unique(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error)); } + if(error.Fail()) { Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true); for(auto &handler : on_exit) handler(-1); return; } + if(debug_thread.joinable()) debug_thread.join(); for(auto &handler : on_start) @@ -193,7 +197,7 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat debug_thread = std::thread([this]() { lldb::SBEvent event; while(true) { - std::unique_lock lock(mutex); + LockGuard lock(mutex); if(listener->GetNextEvent(event)) { if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) { auto state = process->GetStateFromEvent(event); @@ -246,13 +250,13 @@ void Debug::LLDB::start(const std::string &command, const boost::filesystem::pat } void Debug::LLDB::continue_debug() { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::StateType::eStateStopped) process->Continue(); } void Debug::LLDB::stop() { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::StateType::eStateRunning) { auto error = process->Stop(); if(error.Fail()) @@ -261,7 +265,7 @@ void Debug::LLDB::stop() { } void Debug::LLDB::kill() { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(process) { auto error = process->Kill(); if(error.Fail()) @@ -270,29 +274,29 @@ void Debug::LLDB::kill() { } void Debug::LLDB::step_over() { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::StateType::eStateStopped) { process->GetSelectedThread().StepOver(); } } void Debug::LLDB::step_into() { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::StateType::eStateStopped) { process->GetSelectedThread().StepInto(); } } void Debug::LLDB::step_out() { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::StateType::eStateStopped) { process->GetSelectedThread().StepOut(); } } std::pair Debug::LLDB::run_command(const std::string &command) { + LockGuard lock(mutex); std::pair command_return; - std::lock_guard lock(mutex); if(state == lldb::StateType::eStateStopped || state == lldb::StateType::eStateRunning) { lldb::SBCommandReturnObject command_return_object; debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true); @@ -307,8 +311,8 @@ std::pair Debug::LLDB::run_command(const std::string & } std::vector Debug::LLDB::get_backtrace() { + LockGuard lock(mutex); std::vector backtrace; - std::lock_guard lock(mutex); if(state == lldb::StateType::eStateStopped) { auto thread = process->GetSelectedThread(); for(uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) { @@ -343,8 +347,8 @@ std::vector Debug::LLDB::get_backtrace() { } std::vector Debug::LLDB::get_variables() { + LockGuard lock(mutex); std::vector variables; - std::lock_guard lock(mutex); if(state == lldb::StateType::eStateStopped) { for(uint32_t c_t = 0; c_t < process->GetNumThreads(); c_t++) { auto thread = process->GetThreadAtIndex(c_t); @@ -398,7 +402,7 @@ std::vector Debug::LLDB::get_variables() { } void Debug::LLDB::select_frame(uint32_t frame_index, uint32_t thread_index_id) { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::StateType::eStateStopped) { if(thread_index_id != 0) process->SetSelectedThreadByIndexID(thread_index_id); @@ -414,7 +418,7 @@ void Debug::LLDB::cancel() { } std::string Debug::LLDB::get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::StateType::eStateStopped) { auto frame = process->GetSelectedThread().GetSelectedFrame(); @@ -449,8 +453,8 @@ std::string Debug::LLDB::get_value(const std::string &variable, const boost::fil } std::string Debug::LLDB::get_value(const std::string &expression) { + LockGuard lock(mutex); std::string variable_value; - std::lock_guard lock(mutex); if(state == lldb::StateType::eStateStopped) { auto frame = process->GetSelectedThread().GetSelectedFrame(); if(variable_value.empty()) { @@ -476,8 +480,8 @@ std::string Debug::LLDB::get_value(const std::string &expression) { } std::string Debug::LLDB::get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) { + LockGuard lock(mutex); std::string return_value; - std::lock_guard lock(mutex); if(state == lldb::StateType::eStateStopped) { auto thread = process->GetSelectedThread(); auto thread_return_value = thread.GetStopReturnValue(); @@ -499,22 +503,22 @@ std::string Debug::LLDB::get_return_value(const boost::filesystem::path &file_pa } bool Debug::LLDB::is_invalid() { - std::lock_guard lock(mutex); + LockGuard lock(mutex); return state == lldb::StateType::eStateInvalid; } bool Debug::LLDB::is_stopped() { - std::lock_guard lock(mutex); + LockGuard lock(mutex); return state == lldb::StateType::eStateStopped; } bool Debug::LLDB::is_running() { - std::lock_guard lock(mutex); + LockGuard lock(mutex); return state == lldb::StateType::eStateRunning; } void Debug::LLDB::add_breakpoint(const boost::filesystem::path &file_path, int line_nr) { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::eStateStopped || state == lldb::eStateRunning) { if(!(process->GetTarget().BreakpointCreateByLocation(file_path.string().c_str(), line_nr)).IsValid()) Terminal::get().async_print("Error (debug): Could not create breakpoint at: " + file_path.string() + ":" + std::to_string(line_nr) + '\n', true); @@ -522,7 +526,7 @@ void Debug::LLDB::add_breakpoint(const boost::filesystem::path &file_path, int l } void Debug::LLDB::remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::eStateStopped || state == lldb::eStateRunning) { auto target = process->GetTarget(); for(int line_nr_try = line_nr; line_nr_try < line_count; line_nr_try++) { @@ -547,7 +551,7 @@ void Debug::LLDB::remove_breakpoint(const boost::filesystem::path &file_path, in } void Debug::LLDB::write(const std::string &buffer) { - std::lock_guard lock(mutex); + LockGuard lock(mutex); if(state == lldb::StateType::eStateRunning) { process->PutSTDIN(buffer.c_str(), buffer.size()); } diff --git a/src/debug_lldb.h b/src/debug_lldb.h index 32947d8..43fa856 100644 --- a/src/debug_lldb.h +++ b/src/debug_lldb.h @@ -1,8 +1,8 @@ #pragma once +#include "mutex.h" #include #include #include -#include #include #include @@ -45,7 +45,7 @@ namespace Debug { /// The handlers are not run in the main loop. std::list> on_event; - std::mutex mutex; + Mutex mutex; void start(const std::string &command, const boost::filesystem::path &path = "", const std::vector> &breakpoints = {}, @@ -81,12 +81,12 @@ namespace Debug { private: std::tuple, std::string, std::vector> parse_run_arguments(const std::string &command); - std::unique_ptr debugger; - std::unique_ptr listener; - std::unique_ptr process; + std::unique_ptr debugger GUARDED_BY(mutex); + std::unique_ptr listener GUARDED_BY(mutex); + std::unique_ptr process GUARDED_BY(mutex); std::thread debug_thread; - lldb::StateType state; + lldb::StateType state GUARDED_BY(mutex); size_t buffer_size; }; diff --git a/src/directories.h b/src/directories.h index fb3fb48..691ad55 100644 --- a/src/directories.h +++ b/src/directories.h @@ -4,7 +4,6 @@ #include "git.h" #include #include -#include #include #include #include diff --git a/src/dispatcher.cc b/src/dispatcher.cc index 8382e43..c122576 100644 --- a/src/dispatcher.cc +++ b/src/dispatcher.cc @@ -9,7 +9,7 @@ void Dispatcher::connect() { connection = dispatcher.connect([this] { std::vector>::iterator> its; { - std::lock_guard lock(functions_mutex); + LockGuard lock(functions_mutex); if(functions.empty()) return; its.reserve(functions.size()); @@ -19,7 +19,7 @@ void Dispatcher::connect() { for(auto &it : its) (*it)(); { - std::lock_guard lock(functions_mutex); + LockGuard lock(functions_mutex); for(auto &it : its) functions.erase(it); } @@ -35,7 +35,7 @@ void Dispatcher::disconnect() { } void Dispatcher::reset() { - std::lock_guard lock(functions_mutex); + LockGuard lock(functions_mutex); disconnect(); functions.clear(); connect(); diff --git a/src/dispatcher.h b/src/dispatcher.h index 2a2d3e7..4765972 100644 --- a/src/dispatcher.h +++ b/src/dispatcher.h @@ -1,13 +1,13 @@ #pragma once +#include "mutex.h" #include #include #include -#include class Dispatcher { private: - std::list> functions; - std::mutex functions_mutex; + Mutex functions_mutex; + std::list> functions GUARDED_BY(functions_mutex); Glib::Dispatcher dispatcher; sigc::connection connection; @@ -22,7 +22,7 @@ public: /// Can be called from any thread. template void post(T &&function) { - std::lock_guard lock(functions_mutex); + LockGuard lock(functions_mutex); functions.emplace_back(std::forward(function)); dispatcher(); } diff --git a/src/git.cc b/src/git.cc index 5a157f7..5f8bb6f 100644 --- a/src/git.cc +++ b/src/git.cc @@ -3,7 +3,8 @@ #include bool Git::initialized = false; -std::mutex Git::mutex; +Mutex Git::mutex; +Git::Error Git::error; std::string Git::Error::message() noexcept { #if LIBGIT2_VER_MAJOR > 0 || (LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR >= 28) @@ -22,9 +23,8 @@ Git::Repository::Diff::Diff(const boost::filesystem::path &path, git_repository if(blob) git_blob_free(blob); }); - Error error; - std::lock_guard lock(mutex); auto spec = "HEAD:" + path.generic_string(); + LockGuard lock(mutex); error.code = git_revparse_single(reinterpret_cast(&blob), repository, spec.c_str()); if(error) throw std::runtime_error(error.message()); @@ -33,39 +33,35 @@ Git::Repository::Diff::Diff(const boost::filesystem::path &path, git_repository options.context_lines = 0; } -//Based on https://github.com/atom/git-diff/blob/master/lib/git-diff-view.coffee -int Git::Repository::Diff::hunk_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) noexcept { - auto lines = static_cast(payload); - auto start = hunk->new_start - 1; - auto end = hunk->new_start + hunk->new_lines - 1; - if(hunk->old_lines == 0 && hunk->new_lines > 0) - lines->added.emplace_back(start, end); - else if(hunk->new_lines == 0 && hunk->old_lines > 0) - lines->removed.emplace_back(start); - else - lines->modified.emplace_back(start, end); - - return 0; -} - Git::Repository::Diff::Lines Git::Repository::Diff::get_lines(const std::string &buffer) { Lines lines; - Error error; - std::lock_guard lock(mutex); - error.code = git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, hunk_cb, nullptr, &lines); + LockGuard lock(mutex); + error.code = git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, [](const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) { + //Based on https://github.com/atom/git-diff/blob/master/lib/git-diff-view.coffee + auto lines = static_cast(payload); + auto start = hunk->new_start - 1; + auto end = hunk->new_start + hunk->new_lines - 1; + if(hunk->old_lines == 0 && hunk->new_lines > 0) + lines->added.emplace_back(start, end); + else if(hunk->new_lines == 0 && hunk->old_lines > 0) + lines->removed.emplace_back(start); + else + lines->modified.emplace_back(start, end); + + return 0; + }, nullptr, &lines); if(error) throw std::runtime_error(error.message()); return lines; } std::vector Git::Repository::Diff::get_hunks(const std::string &old_buffer, const std::string &new_buffer) { - initialize(); std::vector hunks; - Error error; + LockGuard lock(mutex); + initialize(); git_diff_options options; git_diff_init_options(&options, GIT_DIFF_OPTIONS_VERSION); options.context_lines = 0; - std::lock_guard lock(mutex); error.code = git_diff_buffers(old_buffer.c_str(), old_buffer.size(), nullptr, new_buffer.c_str(), new_buffer.size(), nullptr, &options, nullptr, nullptr, [](const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) { auto hunks = static_cast *>(payload); hunks->emplace_back(hunk->old_start, hunk->old_lines, hunk->new_start, hunk->new_lines); @@ -76,25 +72,22 @@ std::vector Git::Repository::Diff::get_hunks(const return hunks; } -int Git::Repository::Diff::line_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) noexcept { - auto details = static_cast *>(payload); - auto line_nr = details->second; - auto start = hunk->new_start - 1; - auto end = hunk->new_start + hunk->new_lines - 1; - if(line_nr == start || (line_nr >= start && line_nr < end)) { - if(details->first.empty()) - details->first += std::string(hunk->header, hunk->header_len); - details->first += line->origin + std::string(line->content, line->content_len); - } - return 0; -} - std::string Git::Repository::Diff::get_details(const std::string &buffer, int line_nr) { std::pair details; details.second = line_nr; - Error error; - std::lock_guard lock(mutex); - error.code = git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, nullptr, line_cb, &details); + LockGuard lock(mutex); + error.code = git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, nullptr, [](const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { + auto details = static_cast *>(payload); + auto line_nr = details->second; + auto start = hunk->new_start - 1; + auto end = hunk->new_start + hunk->new_lines - 1; + if(line_nr == start || (line_nr >= start && line_nr < end)) { + if(details->first.empty()) + details->first += std::string(hunk->header, hunk->header_len); + details->first += line->origin + std::string(line->content, line->content_len); + } + return 0; + }, &details); if(error) throw std::runtime_error(error.message()); return details.first; @@ -103,8 +96,7 @@ std::string Git::Repository::Diff::get_details(const std::string &buffer, int li Git::Repository::Repository(const boost::filesystem::path &path) { git_repository *repository_ptr; { - Error error; - std::lock_guard lock(mutex); + LockGuard lock(mutex); error.code = git_repository_open_ext(&repository_ptr, path.generic_string().c_str(), 0, nullptr); if(error) throw std::runtime_error(error.message()); @@ -132,123 +124,88 @@ Git::Repository::~Repository() { monitor_changed_connection.disconnect(); } -std::string Git::Repository::status_string(STATUS status) noexcept { - switch(status) { - case STATUS::CURRENT: - return "current"; - case STATUS::NEW: - return "new"; - case STATUS::MODIFIED: - return "modified"; - case STATUS::DELETED: - return "deleted"; - case STATUS::RENAMED: - return "renamed"; - case STATUS::TYPECHANGE: - return "typechange"; - case STATUS::UNREADABLE: - return "unreadable"; - case STATUS::IGNORED: - return "ignored"; - case STATUS::CONFLICTED: - return "conflicted"; - default: - return ""; +Git::Repository::Status Git::Repository::get_status() { + { + LockGuard lock(saved_status_mutex); + if(has_saved_status) + return saved_status; } -} -int Git::Repository::status_callback(const char *path, unsigned int status_flags, void *data) noexcept { - auto callback = static_cast *>(data); + struct Data { + const boost::filesystem::path &work_path; + Status status = {}; + }; + Data data{work_path}; + { + LockGuard lock(mutex); + error.code = git_status_foreach(repository.get(), [](const char *path, unsigned int status_flags, void *payload) { + auto data = static_cast(payload); - STATUS status; - if((status_flags & (GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW)) > 0) - status = STATUS::NEW; - else if((status_flags & (GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED)) > 0) - status = STATUS::MODIFIED; - else if((status_flags & (GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED)) > 0) - status = STATUS::DELETED; - else if((status_flags & (GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED)) > 0) - status = STATUS::RENAMED; - else if((status_flags & (GIT_STATUS_INDEX_TYPECHANGE | GIT_STATUS_WT_TYPECHANGE)) > 0) - status = STATUS::TYPECHANGE; - else if((status_flags & (GIT_STATUS_WT_UNREADABLE)) > 0) - status = STATUS::UNREADABLE; - else if((status_flags & (GIT_STATUS_IGNORED)) > 0) - status = STATUS::IGNORED; - else if((status_flags & (GIT_STATUS_CONFLICTED)) > 0) - status = STATUS::CONFLICTED; - else - status = STATUS::CURRENT; + STATUS status; + if((status_flags & (GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW)) > 0) + status = STATUS::NEW; + else if((status_flags & (GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED)) > 0) + status = STATUS::MODIFIED; + else if((status_flags & (GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED)) > 0) + status = STATUS::DELETED; + else if((status_flags & (GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED)) > 0) + status = STATUS::RENAMED; + else if((status_flags & (GIT_STATUS_INDEX_TYPECHANGE | GIT_STATUS_WT_TYPECHANGE)) > 0) + status = STATUS::TYPECHANGE; + else if((status_flags & (GIT_STATUS_WT_UNREADABLE)) > 0) + status = STATUS::UNREADABLE; + else if((status_flags & (GIT_STATUS_IGNORED)) > 0) + status = STATUS::IGNORED; + else if((status_flags & (GIT_STATUS_CONFLICTED)) > 0) + status = STATUS::CONFLICTED; + else + status = STATUS::CURRENT; - if(*callback) - (*callback)(path, status); + boost::filesystem::path rel_path(path); + do { + if(status == STATUS::MODIFIED) + data->status.modified.emplace((data->work_path / rel_path).generic_string()); + if(status == STATUS::NEW) + data->status.added.emplace((data->work_path / rel_path).generic_string()); + rel_path = rel_path.parent_path(); + } while(!rel_path.empty()); - return 0; -} + return 0; + }, &data); -Git::Repository::Status Git::Repository::get_status() { - { - std::lock_guard lock(saved_status_mutex); - if(has_saved_status) - return saved_status; + if(error) + throw std::runtime_error(error.message()); } - Status status; - bool first = true; - std::unique_lock status_saved_lock(saved_status_mutex, std::defer_lock); - std::function callback = [this, &status, &first, &status_saved_lock](const char *path_cstr, STATUS status_enum) { - if(first) { - status_saved_lock.lock(); - first = false; - } - boost::filesystem::path rel_path(path_cstr); - do { - if(status_enum == STATUS::MODIFIED) - status.modified.emplace((work_path / rel_path).generic_string()); - if(status_enum == STATUS::NEW) - status.added.emplace((work_path / rel_path).generic_string()); - rel_path = rel_path.parent_path(); - } while(!rel_path.empty()); - }; - Error error; - std::lock_guard lock(mutex); - error.code = git_status_foreach(repository.get(), status_callback, &callback); - if(error) - throw std::runtime_error(error.message()); - saved_status = status; + LockGuard lock(saved_status_mutex); + saved_status = std::move(data.status); has_saved_status = true; - if(status_saved_lock) - status_saved_lock.unlock(); - return status; + return saved_status; } void Git::Repository::clear_saved_status() { - std::lock_guard lock(saved_status_mutex); - saved_status.added.clear(); - saved_status.modified.clear(); + LockGuard lock(saved_status_mutex); + saved_status = {}; has_saved_status = false; } boost::filesystem::path Git::Repository::get_work_path() noexcept { - std::lock_guard lock(mutex); + LockGuard lock(mutex); return Git::path(git_repository_workdir(repository.get())); } boost::filesystem::path Git::Repository::get_path() noexcept { - std::lock_guard lock(mutex); + LockGuard lock(mutex); return Git::path(git_repository_path(repository.get())); } boost::filesystem::path Git::Repository::get_root_path(const boost::filesystem::path &path) { - initialize(); git_buf root = {nullptr, 0, 0}; - { - Error error; - std::lock_guard lock(mutex); - error.code = git_repository_discover(&root, path.generic_string().c_str(), 0, nullptr); - if(error) - throw std::runtime_error(error.message()); - } + LockGuard lock(mutex); + initialize(); + error.code = git_repository_discover(&root, path.generic_string().c_str(), 0, nullptr); + if(error) + throw std::runtime_error(error.message()); auto root_path = Git::path(root.ptr, root.size); #if LIBGIT2_VER_MAJOR > 0 || (LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR >= 28) git_buf_dispose(&root); @@ -265,7 +222,9 @@ Git::Repository::Diff Git::Repository::get_diff(const boost::filesystem::path &p std::string Git::Repository::get_branch() noexcept { std::string branch; git_reference *reference; - if(git_repository_head(&reference, repository.get()) == 0) { + LockGuard lock(mutex); + error.code = git_repository_head(&reference, repository.get()); + if(!error) { if(auto reference_name_cstr = git_reference_name(reference)) { std::string reference_name(reference_name_cstr); size_t pos; @@ -284,7 +243,6 @@ std::string Git::Repository::get_branch() noexcept { } void Git::initialize() noexcept { - std::lock_guard lock(mutex); if(!initialized) { git_libgit2_init(); initialized = true; @@ -292,12 +250,12 @@ void Git::initialize() noexcept { } std::shared_ptr Git::get_repository(const boost::filesystem::path &path) { - initialize(); - static std::unordered_map> cache; - static std::mutex mutex; - - std::lock_guard lock(mutex); auto root_path = Repository::get_root_path(path).generic_string(); + + static Mutex mutex; + static std::unordered_map> cache GUARDED_BY(mutex); + + LockGuard lock(mutex); auto it = cache.find(root_path); if(it == cache.end()) it = cache.emplace(root_path, std::weak_ptr()).first; diff --git a/src/git.h b/src/git.h index 3945c7b..1392645 100644 --- a/src/git.h +++ b/src/git.h @@ -1,10 +1,10 @@ #pragma once +#include "mutex.h" #include #include #include #include #include -#include #include #include @@ -18,7 +18,7 @@ public: public: int code = 0; - operator bool() noexcept { return code < 0; } + operator bool() noexcept { return code != 0; } }; class Repository { @@ -45,8 +45,6 @@ public: Diff(const boost::filesystem::path &path, git_repository *repository); std::shared_ptr blob = nullptr; git_diff_options options; - static int hunk_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, void *payload) noexcept; - static int line_cb(const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) noexcept; public: Lines get_lines(const std::string &buffer); @@ -65,21 +63,17 @@ public: friend class Git; Repository(const boost::filesystem::path &path); - static int status_callback(const char *path, unsigned int status_flags, void *data) noexcept; - std::unique_ptr> repository; boost::filesystem::path work_path; sigc::connection monitor_changed_connection; - Status saved_status; - bool has_saved_status = false; - std::mutex saved_status_mutex; + Mutex saved_status_mutex; + Status saved_status GUARDED_BY(saved_status_mutex); + bool has_saved_status GUARDED_BY(saved_status_mutex) = false; public: ~Repository(); - static std::string status_string(STATUS status) noexcept; - Status get_status(); void clear_saved_status(); @@ -95,15 +89,17 @@ public: }; private: - static bool initialized; + static bool initialized GUARDED_BY(mutex); ///Mutex for thread safe operations - static std::mutex mutex; + static Mutex mutex; + + static Error error GUARDED_BY(mutex); ///Call initialize in public static methods - static void initialize() noexcept; + static void initialize() noexcept REQUIRES(mutex); - static boost::filesystem::path path(const char *cpath, size_t cpath_length = static_cast(-1)) noexcept; + static boost::filesystem::path path(const char *cpath, size_t cpath_length = static_cast(-1)) noexcept REQUIRES(mutex); public: static std::shared_ptr get_repository(const boost::filesystem::path &path); diff --git a/src/mutex.h b/src/mutex.h new file mode 100644 index 0000000..2720e7e --- /dev/null +++ b/src/mutex.h @@ -0,0 +1,112 @@ +// Based on https://clang.llvm.org/docs/ThreadSafetyAnalysis.html +#pragma once + +#include + +// Enable thread safety attributes only with clang. Exclude Apple Clang since it is too old. +#if defined(__clang__) && !defined(SWIG) && !defined(__apple_build_version__) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +#define CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) + +#define SCOPED_CAPABILITY \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +#define GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +#define PT_GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +#define ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +#define ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define REQUIRES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) + +#define REQUIRES_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) + +#define ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) + +#define ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) + +#define RELEASE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) + +#define RELEASE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) + +#define TRY_ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) + +#define TRY_ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) + +#define EXCLUDES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +#define ASSERT_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) + +#define ASSERT_SHARED_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) + +#define RETURN_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +/// Use this class instead of std::mutex +class CAPABILITY("mutex") Mutex { + std::mutex mutex; + +public: + void lock() ACQUIRE() { + mutex.lock(); + } + + bool try_lock() TRY_ACQUIRE(true) { + return mutex.try_lock(); + } + + void unlock() RELEASE() { + mutex.unlock(); + } + + const Mutex &operator!() const { return *this; } +}; + +/// Use this class instead of std::lock_guard and std::unique_lock +class SCOPED_CAPABILITY LockGuard { + Mutex &mutex; + bool locked; + +public: + LockGuard(Mutex &mutex_) ACQUIRE(mutex_) : mutex(mutex_) { + mutex.lock(); + locked = true; + } + void lock() ACQUIRE() { + mutex.lock(); + locked = true; + } + void unlock() RELEASE() { + mutex.unlock(); + locked = false; + } + ~LockGuard() RELEASE() { + if(locked) + mutex.unlock(); + } +}; diff --git a/src/notebook.cc b/src/notebook.cc index 21814ea..193f7ee 100644 --- a/src/notebook.cc +++ b/src/notebook.cc @@ -115,7 +115,12 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i if(ec) canonical_file_path = file_path; for(size_t c = 0; c < size(); c++) { - if(canonical_file_path == source_views[c]->canonical_file_path) { + bool equal; + { + LockGuard lock(source_views[c]->canonical_file_path_mutex); + equal = canonical_file_path == source_views[c]->canonical_file_path; + } + if(equal) { auto notebook_page = get_notebook_page(c); notebooks[notebook_page.first].set_current_page(notebook_page.second); focus_view(source_views[c]); diff --git a/src/project.cc b/src/project.cc index e314b90..c4427d6 100644 --- a/src/project.cc +++ b/src/project.cc @@ -3,6 +3,7 @@ #include "directories.h" #include "filesystem.h" #include "menu.h" +#include "mutex.h" #include "notebook.h" #include "selection_dialog.h" #include "terminal.h" @@ -409,7 +410,7 @@ void Project::LLDB::debug_start() { boost::filesystem::path stop_path; unsigned stop_line = 0, stop_column = 0; - std::lock_guard lock(Debug::LLDB::get().mutex); + LockGuard lock(Debug::LLDB::get().mutex); auto process = lldb::SBProcess::GetProcessFromEvent(event); auto state = lldb::SBProcess::GetStateFromEvent(event); lldb::SBStream stream; diff --git a/src/source_base.cc b/src/source_base.cc index 9a2a86d..e390bce 100644 --- a/src/source_base.cc +++ b/src/source_base.cc @@ -937,7 +937,7 @@ void Source::BaseView::search_occurrences_updated(GtkWidget *widget, GParamSpec } void Source::BaseView::set_snippets() { - std::lock_guard lock(snippets_mutex); + LockGuard lock(snippets_mutex); snippets = nullptr; diff --git a/src/source_base.h b/src/source_base.h index 225169f..2270d3f 100644 --- a/src/source_base.h +++ b/src/source_base.h @@ -1,10 +1,10 @@ #pragma once +#include "mutex.h" #include "snippets.h" #include #include #include -#include #include #include #include @@ -136,8 +136,8 @@ namespace Source { /// After inserting a snippet, one can use tab to select the next argument bool keep_snippet_marks = false; - std::vector *snippets = nullptr; - std::mutex snippets_mutex; + Mutex snippets_mutex; + std::vector *snippets GUARDED_BY(snippets_mutex) = nullptr; std::list, Glib::RefPtr>>> snippets_marks; Glib::RefPtr snippet_argument_tag; void insert_snippet(Gtk::TextIter iter, Glib::ustring snippet); diff --git a/src/source_clang.cc b/src/source_clang.cc index d25c2e9..cd2929c 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -128,7 +128,10 @@ void Source::ClangViewParse::parse_initialize() { clang_tokens_offsets.reserve(clang_tokens->size()); for(auto &token : *clang_tokens) clang_tokens_offsets.emplace_back(token.get_source_range().get_offsets()); - update_syntax(); + { + LockGuard lock(parse_mutex); + update_syntax(); + } status_state = "parsing..."; if(update_status_state) @@ -140,21 +143,19 @@ void Source::ClangViewParse::parse_initialize() { if(parse_state != ParseState::PROCESSING) break; auto expected = ParseProcessState::STARTING; - std::unique_lock parse_lock(parse_mutex, std::defer_lock); if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) { dispatcher.post([this] { auto expected = ParseProcessState::PREPROCESSING; - std::unique_lock parse_lock(parse_mutex, std::defer_lock); - if(parse_lock.try_lock()) { + if(parse_mutex.try_lock()) { if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING)) parse_thread_buffer = get_buffer()->get_text(); - parse_lock.unlock(); + parse_mutex.unlock(); } else parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING); }); } - else if(parse_process_state == ParseProcessState::PROCESSING && parse_lock.try_lock()) { + else if(parse_process_state == ParseProcessState::PROCESSING && parse_mutex.try_lock()) { auto &parse_thread_buffer_raw = const_cast(parse_thread_buffer.raw()); if(this->language && (this->language->get_id() == "chdr" || this->language->get_id() == "cpphdr")) clangmm::remove_include_guard(parse_thread_buffer_raw); @@ -168,10 +169,9 @@ void Source::ClangViewParse::parse_initialize() { for(auto &token : *clang_tokens) clang_tokens_offsets.emplace_back(token.get_source_range().get_offsets()); clang_diagnostics = clang_tu->get_diagnostics(); - parse_lock.unlock(); + parse_mutex.unlock(); dispatcher.post([this] { - std::unique_lock parse_lock(parse_mutex, std::defer_lock); - if(parse_lock.try_lock()) { + if(parse_mutex.try_lock()) { auto expected = ParseProcessState::POSTPROCESSING; if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) { update_syntax(); @@ -181,16 +181,16 @@ void Source::ClangViewParse::parse_initialize() { if(update_status_state) update_status_state(this); } - parse_lock.unlock(); + parse_mutex.unlock(); } }); } else - parse_lock.unlock(); + parse_mutex.unlock(); } else { parse_state = ParseState::STOP; - parse_lock.unlock(); + parse_mutex.unlock(); dispatcher.post([this] { Terminal::get().print("Error: failed to reparse " + this->file_path.string() + ".\n", true); status_state = ""; @@ -562,7 +562,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa }; autocomplete.get_parse_lock = [this]() { - return std::make_unique>(parse_mutex); + return std::make_unique(parse_mutex); }; autocomplete.stop_parse = [this]() { @@ -640,7 +640,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa std::smatch sm; if(std::regex_match(line, sm, regex)) { { - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); autocomplete.prefix = sm.length(2) ? sm[3].str() : sm.length(4) ? sm[5].str() : sm[6].str(); if(!sm.length(2) && !sm.length(4)) enable_snippets = true; @@ -649,7 +649,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa } else if(is_possible_argument()) { show_parameters = true; - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); autocomplete.prefix = ""; return true; } @@ -662,7 +662,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa iter.forward_char(); { - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); autocomplete.prefix = get_buffer()->get_text(iter, end_iter); } auto prev1 = iter; @@ -712,7 +712,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa if(autocomplete.state == Autocomplete::State::STARTING) { std::string prefix; { - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); prefix = autocomplete.prefix; } @@ -782,7 +782,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa } } if(!show_parameters && enable_snippets) { - std::lock_guard lock(snippets_mutex); + LockGuard lock(snippets_mutex); if(snippets) { for(auto &snippet : *snippets) { if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) { diff --git a/src/source_clang.h b/src/source_clang.h index 04e0651..541c4c9 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -2,11 +2,11 @@ #include "autocomplete.h" #include "clangmm.h" #include "dispatcher.h" +#include "mutex.h" #include "source.h" #include "terminal.h" #include #include -#include #include #include @@ -37,22 +37,22 @@ namespace Source { std::vector fix_its; + Mutex parse_mutex; std::thread parse_thread; - std::mutex parse_mutex; std::atomic parse_state; std::atomic parse_process_state; CXCompletionString selected_completion_string = nullptr; private: - Glib::ustring parse_thread_buffer; + Glib::ustring parse_thread_buffer GUARDED_BY(parse_mutex); static const std::map &clang_types(); - void update_syntax(); + void update_syntax() REQUIRES(parse_mutex); std::map> syntax_tags; - void update_diagnostics(); - std::vector clang_diagnostics; + void update_diagnostics() REQUIRES(parse_mutex); + std::vector clang_diagnostics GUARDED_BY(parse_mutex); }; class ClangViewAutocomplete : public virtual ClangViewParse { diff --git a/src/source_diff.cc b/src/source_diff.cc index ee7cc6f..517d2b9 100644 --- a/src/source_diff.cc +++ b/src/source_diff.cc @@ -80,8 +80,9 @@ void Source::DiffView::configure() { if(parse_thread.joinable()) parse_thread.join(); repository = nullptr; - diff = nullptr; + LockGuard lock(parse_mutex); + diff = nullptr; return; } else @@ -149,7 +150,7 @@ void Source::DiffView::configure() { delayed_monitor_changed_connection = Glib::signal_timeout().connect([this]() { monitor_changed = true; parse_state = ParseState::STARTING; - std::lock_guard lock(parse_mutex); + LockGuard lock(parse_mutex); diff = nullptr; return false; }, 500); @@ -159,7 +160,10 @@ void Source::DiffView::configure() { parse_thread = std::thread([this]() { std::string status_branch; try { - diff = get_diff(); + { + LockGuard lock(parse_mutex); + diff = get_diff(); + } status_branch = repository->get_branch(); } catch(const std::exception &) { @@ -177,22 +181,20 @@ void Source::DiffView::configure() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); if(parse_stop) break; - std::unique_lock parse_lock(parse_mutex, std::defer_lock); auto expected = ParseState::STARTING; if(parse_state.compare_exchange_strong(expected, ParseState::PREPROCESSING)) { dispatcher.post([this] { auto expected = ParseState::PREPROCESSING; - std::unique_lock parse_lock(parse_mutex, std::defer_lock); - if(parse_lock.try_lock()) { + if(parse_mutex.try_lock()) { if(parse_state.compare_exchange_strong(expected, ParseState::PROCESSING)) parse_buffer = get_buffer()->get_text(); - parse_lock.unlock(); + parse_mutex.unlock(); } else parse_state.compare_exchange_strong(expected, ParseState::STARTING); }); } - else if(parse_state == ParseState::PROCESSING && parse_lock.try_lock()) { + else if(parse_state == ParseState::PROCESSING && parse_mutex.try_lock()) { bool expected_monitor_changed = true; if(monitor_changed.compare_exchange_strong(expected_monitor_changed, false)) { try { @@ -226,16 +228,18 @@ void Source::DiffView::configure() { } auto expected = ParseState::PROCESSING; if(parse_state.compare_exchange_strong(expected, ParseState::POSTPROCESSING)) { - parse_lock.unlock(); + parse_mutex.unlock(); dispatcher.post([this] { - std::unique_lock parse_lock(parse_mutex, std::defer_lock); - if(parse_lock.try_lock()) { + if(parse_mutex.try_lock()) { auto expected = ParseState::POSTPROCESSING; if(parse_state.compare_exchange_strong(expected, ParseState::IDLE)) update_lines(); + parse_mutex.unlock(); } }); } + else + parse_mutex.unlock(); } } } @@ -256,7 +260,7 @@ void Source::DiffView::configure() { void Source::DiffView::rename(const boost::filesystem::path &path) { Source::BaseView::rename(path); - std::lock_guard lock(canonical_file_path_mutex); + LockGuard lock(canonical_file_path_mutex); boost::system::error_code ec; canonical_file_path = boost::filesystem::canonical(path, ec); if(ec) @@ -293,14 +297,16 @@ void Source::DiffView::git_goto_next_diff() { std::string Source::DiffView::git_get_diff_details() { std::string details; - if(diff) { - auto line_nr = get_buffer()->get_insert()->get_iter().get_line(); - auto iter = get_buffer()->get_iter_at_line(line_nr); - if(iter.has_tag(renderer->tag_removed_above)) - --line_nr; - std::lock_guard lock(parse_mutex); - parse_buffer = get_buffer()->get_text(); - details = diff->get_details(parse_buffer.raw(), line_nr); + { + LockGuard lock(parse_mutex); + if(diff) { + auto line_nr = get_buffer()->get_insert()->get_iter().get_line(); + auto iter = get_buffer()->get_iter_at_line(line_nr); + if(iter.has_tag(renderer->tag_removed_above)) + --line_nr; + parse_buffer = get_buffer()->get_text(); + details = diff->get_details(parse_buffer.raw(), line_nr); + } } if(details.empty()) Info::get().print("No changes found at current line"); @@ -312,7 +318,7 @@ std::unique_ptr Source::DiffView::get_diff() { auto work_path = filesystem::get_normal_path(repository->get_work_path()); boost::filesystem::path relative_path; { - std::lock_guard lock(canonical_file_path_mutex); + LockGuard lock(canonical_file_path_mutex); if(!filesystem::file_in_path(canonical_file_path, work_path)) throw std::runtime_error("not a relative path"); relative_path = filesystem::get_relative_path(canonical_file_path, work_path); diff --git a/src/source_diff.h b/src/source_diff.h index df961b4..4d29a49 100644 --- a/src/source_diff.h +++ b/src/source_diff.h @@ -1,11 +1,11 @@ #pragma once #include "dispatcher.h" #include "git.h" +#include "mutex.h" #include "source_base.h" #include #include #include -#include #include #include @@ -40,24 +40,25 @@ namespace Source { void git_goto_next_diff(); std::string git_get_diff_details(); + Mutex canonical_file_path_mutex; /// Use canonical path to follow symbolic links - boost::filesystem::path canonical_file_path; + boost::filesystem::path canonical_file_path GUARDED_BY(canonical_file_path_mutex); private: - std::mutex canonical_file_path_mutex; - std::unique_ptr renderer; Dispatcher dispatcher; + + Mutex parse_mutex; + std::shared_ptr repository; - std::unique_ptr diff; + std::unique_ptr diff GUARDED_BY(parse_mutex); std::unique_ptr get_diff(); std::thread parse_thread; std::atomic parse_state; - std::mutex parse_mutex; std::atomic parse_stop; - Glib::ustring parse_buffer; + Glib::ustring parse_buffer GUARDED_BY(parse_mutex); sigc::connection buffer_insert_connection; sigc::connection buffer_erase_connection; sigc::connection monitor_changed_connection; @@ -65,7 +66,7 @@ namespace Source { sigc::connection delayed_monitor_changed_connection; std::atomic monitor_changed; - Git::Repository::Diff::Lines lines; - void update_lines(); + Git::Repository::Diff::Lines lines GUARDED_BY(parse_mutex); + void update_lines() REQUIRES(parse_mutex); }; } // namespace Source diff --git a/src/source_generic.cc b/src/source_generic.cc index ca0809a..7d07161 100644 --- a/src/source_generic.cc +++ b/src/source_generic.cc @@ -122,7 +122,7 @@ std::vector> Source::GenericView::get_wo void Source::GenericView::setup_buffer_words() { { auto words = get_words(get_buffer()->begin(), get_buffer()->end()); - std::lock_guard lock(buffer_words_mutex); + LockGuard lock(buffer_words_mutex); for(auto &word : words) { auto result = buffer_words.emplace(get_buffer()->get_text(word.first, word.second), 1); if(!result.second) @@ -139,7 +139,7 @@ void Source::GenericView::setup_buffer_words() { if(is_word_iter(iter)) { auto word = get_word(iter); if(word.second.get_offset() - word.first.get_offset() >= 3) { - std::lock_guard lock(buffer_words_mutex); + LockGuard lock(buffer_words_mutex); auto it = buffer_words.find(get_buffer()->get_text(word.first, word.second)); if(it != buffer_words.end()) { if(it->second > 1) @@ -161,7 +161,7 @@ void Source::GenericView::setup_buffer_words() { end.forward_char(); auto words = get_words(start, end); - std::lock_guard lock(buffer_words_mutex); + LockGuard lock(buffer_words_mutex); for(auto &word : words) { auto result = buffer_words.emplace(get_buffer()->get_text(word.first, word.second), 1); if(!result.second) @@ -177,7 +177,7 @@ void Source::GenericView::setup_buffer_words() { start.backward_char(); end.forward_char(); auto words = get_words(start, end); - std::lock_guard lock(buffer_words_mutex); + LockGuard lock(buffer_words_mutex); for(auto &word : words) { auto it = buffer_words.find(get_buffer()->get_text(word.first, word.second)); if(it != buffer_words.end()) { @@ -197,7 +197,7 @@ void Source::GenericView::setup_buffer_words() { if(is_word_iter(start)) { auto word = get_word(start); if(word.second.get_offset() - word.first.get_offset() >= 3) { - std::lock_guard lock(buffer_words_mutex); + LockGuard lock(buffer_words_mutex); auto result = buffer_words.emplace(get_buffer()->get_text(word.first, word.second), 1); if(!result.second) ++(result.first->second); @@ -236,9 +236,8 @@ void Source::GenericView::setup_autocomplete() { while(start.backward_char() && ((*start >= '0' && *start <= '9') || (*start >= 'a' && *start <= 'z') || (*start >= 'A' && *start <= 'Z') || *start == '_' || *start >= 0x00C0)) ++count; if((start.is_start() || start.forward_char()) && count >= 3 && !(*start >= '0' && *start <= '9')) { - std::lock(autocomplete.prefix_mutex, buffer_words_mutex); - std::lock_guard lock1(autocomplete.prefix_mutex, std::adopt_lock); - std::lock_guard lock2(buffer_words_mutex, std::adopt_lock); + LockGuard lock1(autocomplete.prefix_mutex); + LockGuard lock2(buffer_words_mutex); autocomplete.prefix = get_buffer()->get_text(start, end); show_prefix_buffer_word = buffer_words.find(autocomplete.prefix) != buffer_words.end(); return true; @@ -250,9 +249,8 @@ void Source::GenericView::setup_autocomplete() { } if(iter != end_iter) iter.forward_char(); - std::lock(autocomplete.prefix_mutex, buffer_words_mutex); - std::lock_guard lock1(autocomplete.prefix_mutex, std::adopt_lock); - std::lock_guard lock2(buffer_words_mutex, std::adopt_lock); + LockGuard lock1(autocomplete.prefix_mutex); + LockGuard lock2(buffer_words_mutex); autocomplete.prefix = get_buffer()->get_text(iter, end_iter); show_prefix_buffer_word = buffer_words.find(autocomplete.prefix) != buffer_words.end(); return true; @@ -285,7 +283,7 @@ void Source::GenericView::setup_autocomplete() { std::string prefix; { - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); prefix = autocomplete.prefix; } for(auto &keyword : keywords) { @@ -295,9 +293,8 @@ void Source::GenericView::setup_autocomplete() { autocomplete_comment.emplace_back(""); } } - bool show_prefix_buffer_word = this->show_prefix_buffer_word; { - std::lock_guard lock(buffer_words_mutex); + LockGuard lock(buffer_words_mutex); for(auto &buffer_word : buffer_words) { if((show_prefix_buffer_word || buffer_word.first.size() > prefix.size()) && prefix.compare(0, prefix.size(), buffer_word.first, 0, prefix.size()) == 0 && keywords.find(buffer_word.first) == keywords.end()) { @@ -307,7 +304,7 @@ void Source::GenericView::setup_autocomplete() { } } } - std::lock_guard lock(snippets_mutex); + LockGuard lock(snippets_mutex); if(snippets) { for(auto &snippet : *snippets) { if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) { diff --git a/src/source_generic.h b/src/source_generic.h index 63caf4e..3df7209 100644 --- a/src/source_generic.h +++ b/src/source_generic.h @@ -1,6 +1,7 @@ #pragma once #include "autocomplete.h" +#include "mutex.h" #include "source.h" #include @@ -19,10 +20,10 @@ namespace Source { std::pair get_word(Gtk::TextIter iter); std::vector> get_words(const Gtk::TextIter &start, const Gtk::TextIter &end); - std::mutex buffer_words_mutex; - std::map buffer_words; + Mutex buffer_words_mutex ACQUIRED_AFTER(autocomplete.prefix_mutex); + std::map buffer_words GUARDED_BY(buffer_words_mutex); + bool show_prefix_buffer_word GUARDED_BY(buffer_words_mutex) = false; /// To avoid showing the current word if it is unique in document void setup_buffer_words(); - std::atomic show_prefix_buffer_word = {false}; /// To avoid showing the current word if it is unique in document Autocomplete autocomplete; std::vector autocomplete_comment; diff --git a/src/source_language_protocol.cc b/src/source_language_protocol.cc index 0798df1..614f0a2 100644 --- a/src/source_language_protocol.cc +++ b/src/source_language_protocol.cc @@ -58,16 +58,17 @@ std::shared_ptr LanguageProtocol::Client::get(const bo auto cache_id = root_path.string() + '|' + language_id; - static std::unordered_map> cache; - static std::mutex mutex; - std::lock_guard lock(mutex); + static Mutex mutex; + static std::unordered_map> cache GUARDED_BY(mutex); + + LockGuard lock(mutex); auto it = cache.find(cache_id); if(it == cache.end()) it = cache.emplace(cache_id, std::weak_ptr()).first; auto instance = it->second.lock(); if(!instance) it->second = instance = std::shared_ptr(new Client(root_path, language_id), [](Client *client_ptr) { - std::thread delete_thread([client_ptr] { + std::thread delete_thread([client_ptr] { // Delete client in the background delete client_ptr; }); delete_thread.detach(); @@ -84,7 +85,7 @@ LanguageProtocol::Client::~Client() { }); result_processed.get_future().get(); - std::lock_guard lock(timeout_threads_mutex); + LockGuard lock(timeout_threads_mutex); for(auto &thread : timeout_threads) thread.join(); @@ -102,17 +103,22 @@ LanguageProtocol::Client::~Client() { LanguageProtocol::Capabilities LanguageProtocol::Client::initialize(Source::LanguageProtocolView *view) { if(view) { - std::lock_guard lock(views_mutex); + LockGuard lock(views_mutex); views.emplace(view); } - std::lock_guard lock(initialize_mutex); + LockGuard lock(initialize_mutex); if(initialized) return capabilities; std::promise result_processed; - write_request(nullptr, "initialize", "\"processId\":" + std::to_string(process->get_id()) + R"(,"rootUri":")" + filesystem::get_uri_from_path(root_path) + R"(","capabilities":{"workspace":{"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true},"executeCommand":{"dynamicRegistration":true}},"textDocument":{"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"completionItem":{"snippetSupport":true}},"hover":{"dynamicRegistration":true},"signatureHelp":{"dynamicRegistration":true},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true},"codeAction":{"dynamicRegistration":true},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true},"documentLink":{"dynamicRegistration":true}}},"initializationOptions":{"omitInitBuild":true},"trace":"off")", [this, &result_processed](const boost::property_tree::ptree &result, bool error) { + TinyProcessLib::Process::id_type process_id; + { + LockGuard lock(read_write_mutex); + process_id = process->get_id(); + } + write_request(nullptr, "initialize", "\"processId\":" + std::to_string(process_id) + R"(,"rootUri":")" + filesystem::get_uri_from_path(root_path) + R"(","capabilities":{"workspace":{"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true},"executeCommand":{"dynamicRegistration":true}},"textDocument":{"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"completionItem":{"snippetSupport":true}},"hover":{"dynamicRegistration":true},"signatureHelp":{"dynamicRegistration":true},"definition":{"dynamicRegistration":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true},"codeAction":{"dynamicRegistration":true},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true},"documentLink":{"dynamicRegistration":true}}},"initializationOptions":{"omitInitBuild":true},"trace":"off")", [this, &result_processed](const boost::property_tree::ptree &result, bool error) { if(!error) { auto capabilities_pt = result.find("capabilities"); if(capabilities_pt != result.not_found()) { @@ -150,12 +156,12 @@ LanguageProtocol::Capabilities LanguageProtocol::Client::initialize(Source::Lang void LanguageProtocol::Client::close(Source::LanguageProtocolView *view) { { - std::lock_guard lock(views_mutex); + LockGuard lock(views_mutex); auto it = views.find(view); if(it != views.end()) views.erase(it); } - std::lock_guard lock(read_write_mutex); + LockGuard lock(read_write_mutex); for(auto it = handlers.begin(); it != handlers.end();) { if(it->second.first == view) it = handlers.erase(it); @@ -217,7 +223,7 @@ void LanguageProtocol::Client::parse_server_message() { auto result_it = pt.find("result"); auto error_it = pt.find("error"); { - std::unique_lock lock(read_write_mutex); + LockGuard lock(read_write_mutex); if(result_it != pt.not_found()) { if(message_id) { auto id_it = handlers.find(message_id); @@ -272,21 +278,21 @@ void LanguageProtocol::Client::parse_server_message() { } void LanguageProtocol::Client::write_request(Source::LanguageProtocolView *view, const std::string &method, const std::string ¶ms, std::function &&function) { - std::unique_lock lock(read_write_mutex); + LockGuard lock(read_write_mutex); if(function) { handlers.emplace(message_id, std::make_pair(view, std::move(function))); auto message_id = this->message_id; - std::lock_guard lock(timeout_threads_mutex); + LockGuard lock(timeout_threads_mutex); timeout_threads.emplace_back([this, message_id] { for(size_t c = 0; c < 20; ++c) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); - std::lock_guard lock(read_write_mutex); + LockGuard lock(read_write_mutex); auto id_it = handlers.find(message_id); if(id_it == handlers.end()) return; } - std::unique_lock lock(read_write_mutex); + LockGuard lock(read_write_mutex); auto id_it = handlers.find(message_id); if(id_it != handlers.end()) { Terminal::get().async_print("Request to language server timed out. If you suspect the server has crashed, please close and reopen all project source files.\n", true); @@ -316,7 +322,7 @@ void LanguageProtocol::Client::write_request(Source::LanguageProtocolView *view, } void LanguageProtocol::Client::write_notification(const std::string &method, const std::string ¶ms) { - std::lock_guard lock(read_write_mutex); + LockGuard lock(read_write_mutex); std::string content(R"({"jsonrpc":"2.0","method":")" + method + R"(","params":{)" + params + "}}"); auto message = "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n" + content; if(Config::get().log.language_server) @@ -337,7 +343,7 @@ void LanguageProtocol::Client::handle_server_request(const std::string &method, catch(...) { } } - std::lock_guard lock(views_mutex); + LockGuard lock(views_mutex); for(auto view : views) { if(file == view->file_path) { view->update_diagnostics(std::move(diagnostics)); @@ -1267,7 +1273,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { std::smatch sm; if(std::regex_match(line, sm, regex)) { { - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); autocomplete.prefix = sm.length(2) ? sm[3].str() : sm.length(4) ? sm[5].str() : sm[6].str(); if(!sm.length(2) && !sm.length(4)) autocomplete_enable_snippets = true; @@ -1276,7 +1282,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { } else if(is_possible_argument()) { autocomplete_show_parameters = true; - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); autocomplete.prefix = ""; return true; } @@ -1289,7 +1295,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { iter.forward_char(); { - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); autocomplete.prefix = get_buffer()->get_text(iter, end_iter); } auto prev1 = iter; @@ -1397,7 +1403,7 @@ void Source::LanguageProtocolView::setup_autocomplete() { if(!label.empty()) { std::string prefix; { - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); prefix = autocomplete.prefix; } if(prefix.compare(0, prefix.size(), label, 0, prefix.size()) == 0) { @@ -1416,10 +1422,10 @@ void Source::LanguageProtocolView::setup_autocomplete() { if(autocomplete_enable_snippets) { std::string prefix; { - std::lock_guard lock(autocomplete.prefix_mutex); + LockGuard lock(autocomplete.prefix_mutex); prefix = autocomplete.prefix; } - std::lock_guard lock(snippets_mutex); + LockGuard lock(snippets_mutex); if(snippets) { for(auto &snippet : *snippets) { if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) { diff --git a/src/source_language_protocol.h b/src/source_language_protocol.h index 4467c8c..4668608 100644 --- a/src/source_language_protocol.h +++ b/src/source_language_protocol.h @@ -1,12 +1,12 @@ #pragma once #include "autocomplete.h" +#include "mutex.h" #include "process.hpp" #include "source.h" #include #include #include #include -#include #include #include @@ -105,31 +105,32 @@ namespace LanguageProtocol { Capabilities capabilities; - std::set views; - std::mutex views_mutex; + Mutex views_mutex; + std::set views GUARDED_BY(views_mutex); - std::mutex initialize_mutex; + Mutex initialize_mutex; + bool initialized GUARDED_BY(initialize_mutex) = false; - std::unique_ptr process; - std::mutex read_write_mutex; + Mutex read_write_mutex; + std::unique_ptr process GUARDED_BY(read_write_mutex); std::stringstream server_message_stream; size_t server_message_size = static_cast(-1); size_t server_message_content_pos; bool header_read = false; - size_t message_id = 1; + size_t message_id GUARDED_BY(read_write_mutex) = 1; - std::map>> handlers; - std::vector timeout_threads; - std::mutex timeout_threads_mutex; + std::map>> handlers GUARDED_BY(read_write_mutex); + + Mutex timeout_threads_mutex; + std::vector timeout_threads GUARDED_BY(timeout_threads_mutex); public: static std::shared_ptr get(const boost::filesystem::path &file_path, const std::string &language_id); ~Client(); - bool initialized = false; Capabilities initialize(Source::LanguageProtocolView *view); void close(Source::LanguageProtocolView *view); diff --git a/src/terminal.cc b/src/terminal.cc index 6662eed..939e40d 100644 --- a/src/terminal.cc +++ b/src/terminal.cc @@ -79,7 +79,7 @@ int Terminal::process(std::istream &stdin_stream, std::ostream &stdout_stream, c void Terminal::async_process(const std::string &command, const boost::filesystem::path &path, const std::function &callback, bool quiet) { std::thread async_execute_thread([this, command, path, callback, quiet]() { - std::unique_lock processes_lock(processes_mutex); + LockGuard lock(processes_mutex); stdin_buffer.clear(); auto process = std::make_shared(command, path.string(), [this, quiet](const char *bytes, size_t n) { if(!quiet) @@ -90,7 +90,7 @@ void Terminal::async_process(const std::string &command, const boost::filesystem }, true); auto pid = process->get_id(); if(pid <= 0) { - processes_lock.unlock(); + lock.unlock(); async_print("Error: failed to run command: " + command + "\n", true); if(callback) callback(-1); @@ -98,12 +98,12 @@ void Terminal::async_process(const std::string &command, const boost::filesystem } else { processes.emplace_back(process); - processes_lock.unlock(); + lock.unlock(); } auto exit_status = process->get_exit_status(); - processes_lock.lock(); + lock.lock(); for(auto it = processes.begin(); it != processes.end(); it++) { if((*it)->get_id() == pid) { processes.erase(it); @@ -111,7 +111,7 @@ void Terminal::async_process(const std::string &command, const boost::filesystem } } stdin_buffer.clear(); - processes_lock.unlock(); + lock.unlock(); if(callback) callback(exit_status); @@ -120,7 +120,7 @@ void Terminal::async_process(const std::string &command, const boost::filesystem } void Terminal::kill_last_async_process(bool force) { - std::lock_guard lock(processes_mutex); + LockGuard lock(processes_mutex); if(processes.empty()) Info::get().print("No running processes"); else @@ -128,7 +128,7 @@ void Terminal::kill_last_async_process(bool force) { } void Terminal::kill_async_processes(bool force) { - std::lock_guard lock(processes_mutex); + LockGuard lock(processes_mutex); for(auto &process : processes) process->kill(force); } @@ -393,7 +393,7 @@ bool Terminal::on_button_press_event(GdkEventButton *button_event) { } bool Terminal::on_key_press_event(GdkEventKey *event) { - std::lock_guard lock(processes_mutex); + LockGuard lock(processes_mutex); bool debug_is_running = false; #ifdef JUCI_ENABLE_DEBUG debug_is_running = Project::current ? Project::current->debug_is_running() : false; diff --git a/src/terminal.h b/src/terminal.h index bfc779a..5712f0c 100644 --- a/src/terminal.h +++ b/src/terminal.h @@ -1,11 +1,11 @@ #pragma once #include "dispatcher.h" #include "gtkmm.h" +#include "mutex.h" #include "process.hpp" #include #include #include -#include #include class Terminal : public Gtk::TextView { @@ -47,7 +47,7 @@ private: std::tuple find_link(const std::string &line); void apply_link_tags(const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter); - std::vector> processes; - std::mutex processes_mutex; + Mutex processes_mutex; + std::vector> processes GUARDED_BY(processes_mutex); Glib::ustring stdin_buffer; }; diff --git a/src/usages_clang.cc b/src/usages_clang.cc index a1bf45d..592d096 100644 --- a/src/usages_clang.cc +++ b/src/usages_clang.cc @@ -3,6 +3,7 @@ #include "config.h" #include "dialogs.h" #include "filesystem.h" +#include "utility.h" #include #include #include @@ -22,7 +23,7 @@ pid_t get_current_process_id() { const boost::filesystem::path Usages::Clang::cache_folder = ".usages_clang"; std::map Usages::Clang::caches; -std::mutex Usages::Clang::caches_mutex; +Mutex Usages::Clang::caches_mutex; std::atomic Usages::Clang::cache_in_progress_count(0); bool Usages::Clang::Cache::Cursor::operator==(const Cursor &o) { @@ -170,7 +171,7 @@ std::vector Usages::Clang::get_usages(const boost::filesy // Use cache for(auto it = potential_paths.begin(); it != potential_paths.end();) { - std::lock_guard lock(caches_mutex); + LockGuard lock(caches_mutex); auto caches_it = caches.find(*it); // Load cache from file if not found in memory and if cache file exists @@ -221,21 +222,14 @@ std::vector Usages::Clang::get_usages(const boost::filesy while(true) { boost::filesystem::path path; { - static std::mutex mutex; - std::lock_guard lock(mutex); + static Mutex mutex; + LockGuard lock(mutex); if(it == potential_paths.end()) return; path = *it; ++it; } - // { - // static std::mutex mutex; - // std::lock_guard lock(mutex); - // std::cout << "parsing: " << path << std::endl; - // } - // auto before_time = std::chrono::system_clock::now(); - std::ifstream stream(path.string(), std::ifstream::binary); std::string buffer; buffer.assign(std::istreambuf_iterator(stream), std::istreambuf_iterator()); @@ -256,8 +250,8 @@ std::vector Usages::Clang::get_usages(const boost::filesy clangmm::TranslationUnit translation_unit(std::make_shared(0, 0), path.string(), arguments, &buffer, flags); { - static std::mutex mutex; - std::lock_guard lock(mutex); + static Mutex mutex; + LockGuard lock(mutex); add_usages(project_path, build_path, path, usages, visited, spelling, cursor, &translation_unit, true); add_usages_from_includes(project_path, build_path, usages, visited, spelling, cursor, &translation_unit, true); } @@ -279,22 +273,13 @@ std::vector Usages::Clang::get_usages(const boost::filesy void Usages::Clang::cache(const boost::filesystem::path &project_path, const boost::filesystem::path &build_path, const boost::filesystem::path &path, std::time_t before_parse_time, const PathSet &project_paths_in_use, clangmm::TranslationUnit *translation_unit, clangmm::Tokens *tokens) { - class ScopeExit { - public: - std::function f; - ~ScopeExit() { - f(); - } - }; - ScopeExit scope_exit{[] { - --cache_in_progress_count; - }}; + ScopeGuard guard{[] { --cache_in_progress_count; }}; if(project_path.empty()) return; { - std::lock_guard lock(caches_mutex); + LockGuard lock(caches_mutex); if(project_paths_in_use.count(project_path)) { caches.erase(path); caches.emplace(path, Cache(project_path, build_path, path, before_parse_time, translation_unit, tokens)); @@ -329,7 +314,7 @@ void Usages::Clang::cache(const boost::filesystem::path &project_path, const boo if(file_size == static_cast(-1) || ec) continue; auto tokens = translation_unit->get_tokens(path.string(), 0, file_size - 1); - std::lock_guard lock(caches_mutex); + LockGuard lock(caches_mutex); if(project_paths_in_use.count(project_path)) { caches.erase(path); caches.emplace(path, Cache(project_path, build_path, path, before_parse_time, translation_unit, tokens.get())); @@ -340,7 +325,7 @@ void Usages::Clang::cache(const boost::filesystem::path &project_path, const boo } void Usages::Clang::erase_unused_caches(const PathSet &project_paths_in_use) { - std::lock_guard lock(caches_mutex); + LockGuard lock(caches_mutex); for(auto it = caches.begin(); it != caches.end();) { bool found = false; for(auto &project_path : project_paths_in_use) { @@ -359,7 +344,7 @@ void Usages::Clang::erase_unused_caches(const PathSet &project_paths_in_use) { } void Usages::Clang::erase_cache(const boost::filesystem::path &path) { - std::lock_guard lock(caches_mutex); + LockGuard lock(caches_mutex); auto it = caches.find(path); if(it == caches.end()) @@ -377,7 +362,7 @@ void Usages::Clang::erase_all_caches_for_project(const boost::filesystem::path & if(cache_in_progress_count != 0) std::this_thread::sleep_for(std::chrono::milliseconds(10)); - std::lock_guard lock(caches_mutex); + LockGuard lock(caches_mutex); boost::system::error_code ec; auto usages_clang_path = build_path / cache_folder; if(boost::filesystem::exists(usages_clang_path, ec) && boost::filesystem::is_directory(usages_clang_path, ec)) { @@ -440,7 +425,7 @@ void Usages::Clang::add_usages(const boost::filesystem::path &project_path, cons } if(store_in_cache && filesystem::file_in_path(path, project_path)) { - std::lock_guard lock(caches_mutex); + LockGuard lock(caches_mutex); caches.erase(path); caches.emplace(path, Cache(project_path, build_path, path, before_parse_time, translation_unit, tokens.get())); } diff --git a/src/usages_clang.h b/src/usages_clang.h index 56ae3f2..7fe60db 100644 --- a/src/usages_clang.h +++ b/src/usages_clang.h @@ -1,5 +1,6 @@ #pragma once #include "clangmm.h" +#include "mutex.h" #include #include #include @@ -8,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -102,8 +102,8 @@ namespace Usages { private: const static boost::filesystem::path cache_folder; - static std::map caches; - static std::mutex caches_mutex; + static Mutex caches_mutex; + static std::map caches GUARDED_BY(caches_mutex); static std::atomic cache_in_progress_count; @@ -142,7 +142,7 @@ namespace Usages { static std::pair find_potential_paths(const PathSet &paths, const boost::filesystem::path &project_path, const std::map &paths_includes, const PathSet &paths_with_spelling); - static void write_cache(const boost::filesystem::path &path, const Cache &cache); - static Cache read_cache(const boost::filesystem::path &project_path, const boost::filesystem::path &build_path, const boost::filesystem::path &path); + static void write_cache(const boost::filesystem::path &path, const Cache &cache) REQUIRES(caches_mutex); + static Cache read_cache(const boost::filesystem::path &project_path, const boost::filesystem::path &build_path, const boost::filesystem::path &path) REQUIRES(caches_mutex); }; } // namespace Usages diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 833ef76..420bb5f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,8 @@ add_compile_options(-fno-access-control) +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wno-thread-safety) +endif() + add_definitions(-DJUCI_BUILD_PATH="${CMAKE_BINARY_DIR}" -DJUCI_TESTS_PATH="${CMAKE_CURRENT_SOURCE_DIR}") include_directories( diff --git a/tests/lldb_test.cc b/tests/lldb_test.cc index 1c99d3f..9ab9d6b 100644 --- a/tests/lldb_test.cc +++ b/tests/lldb_test.cc @@ -85,7 +85,7 @@ int main() { exited = true; }); Debug::LLDB::get().on_event.emplace_back([&](const lldb::SBEvent &event) { - std::unique_lock lock(Debug::LLDB::get().mutex); + LockGuard lock(Debug::LLDB::get().mutex); auto process = lldb::SBProcess::GetProcessFromEvent(event); auto state = lldb::SBProcess::GetStateFromEvent(event); if(state == lldb::StateType::eStateStopped) {