Browse Source

Made use of clang's Thread Safety Analysis. Also added two new ci-jobs: static-analysis and thread-safety-analysis using the arch linux image. Static analysis tasks were removed from the other jobs.

merge-requests/398/head
eidheim 7 years ago
parent
commit
9e0a25e9ac
  1. 20
      .gitlab-ci.yml
  2. 1
      CMakeLists.txt
  3. 7
      src/autocomplete.cc
  4. 7
      src/autocomplete.h
  5. 44
      src/debug_lldb.cc
  6. 12
      src/debug_lldb.h
  7. 1
      src/directories.h
  8. 6
      src/dispatcher.cc
  9. 8
      src/dispatcher.h
  10. 240
      src/git.cc
  11. 26
      src/git.h
  12. 112
      src/mutex.h
  13. 7
      src/notebook.cc
  14. 3
      src/project.cc
  15. 2
      src/source_base.cc
  16. 6
      src/source_base.h
  17. 36
      src/source_clang.cc
  18. 12
      src/source_clang.h
  19. 48
      src/source_diff.cc
  20. 19
      src/source_diff.h
  21. 27
      src/source_generic.cc
  22. 7
      src/source_generic.h
  23. 52
      src/source_language_protocol.cc
  24. 23
      src/source_language_protocol.h
  25. 16
      src/terminal.cc
  26. 6
      src/terminal.h
  27. 43
      src/usages_clang.cc
  28. 10
      src/usages_clang.h
  29. 4
      tests/CMakeLists.txt
  30. 2
      tests/lldb_test.cc

20
.gitlab-ci.yml

@ -8,11 +8,7 @@ stages:
.script: &compile .script: &compile
stage: test stage: test
script: script:
- mkdir build - mkdir build && cd build
- cd build
- scan-build cmake ..
- scan-build --status-bugs make -j$(nproc)
- rm -r *
- cmake -DBUILD_TESTING=1 .. - cmake -DBUILD_TESTING=1 ..
- make -j$(nproc) - make -j$(nproc)
- broadwayd & CTEST_OUTPUT_ON_FAILURE=1 make test - broadwayd & CTEST_OUTPUT_ON_FAILURE=1 make test
@ -26,6 +22,20 @@ fedora:
arch: arch:
image: cppit/jucipp:arch image: cppit/jucipp:arch
<<: *compile <<: *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: debian-testing:
image: cppit/jucipp:debian-testing image: cppit/jucipp:debian-testing
<<: *compile <<: *compile

1
CMakeLists.txt

@ -27,6 +27,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
add_compile_options(-Wno-cpp) add_compile_options(-Wno-cpp)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options("-Wno-#warnings") add_compile_options("-Wno-#warnings")
add_compile_options(-Wthread-safety)
endif() endif()
if(APPLE) if(APPLE)

7
src/autocomplete.cc

@ -94,7 +94,12 @@ void Autocomplete::run() {
} }
else { else {
auto start_iter = view->get_buffer()->get_insert()->get_iter(); 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; state = State::IDLE;
reparse(); reparse();
return; return;

7
src/autocomplete.h

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "dispatcher.h" #include "dispatcher.h"
#include "mutex.h"
#include "tooltips.h" #include "tooltips.h"
#include <atomic> #include <atomic>
#include <thread> #include <thread>
@ -16,8 +17,8 @@ class Autocomplete {
public: public:
enum class State { IDLE, STARTING, RESTARTING, CANCELED }; enum class State { IDLE, STARTING, RESTARTING, CANCELED };
Glib::ustring prefix; Mutex prefix_mutex;
std::mutex prefix_mutex; Glib::ustring prefix GUARDED_BY(prefix_mutex);
std::vector<std::string> rows; std::vector<std::string> rows;
Tooltips tooltips; Tooltips tooltips;
@ -28,7 +29,7 @@ public:
std::function<bool()> is_processing = [] { return true; }; std::function<bool()> is_processing = [] { return true; };
std::function<void()> reparse = [] {}; std::function<void()> reparse = [] {};
std::function<void()> cancel_reparse = [] {}; std::function<void()> cancel_reparse = [] {};
std::function<std::unique_ptr<std::lock_guard<std::mutex>>()> get_parse_lock = [] { return nullptr; }; std::function<std::unique_ptr<LockGuard>()> get_parse_lock = [] { return nullptr; };
std::function<void()> stop_parse = [] {}; std::function<void()> stop_parse = [] {};
std::function<bool(guint last_keyval)> is_continue_key = [](guint) { return false; }; std::function<bool(guint last_keyval)> is_continue_key = [](guint) { return false; };

44
src/debug_lldb.cc

@ -86,6 +86,8 @@ std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> Debu
void Debug::LLDB::start(const std::string &command, const boost::filesystem::path &path, void Debug::LLDB::start(const std::string &command, const boost::filesystem::path &path,
const std::vector<std::pair<boost::filesystem::path, int>> &breakpoints, const std::vector<std::pair<boost::filesystem::path, int>> &breakpoints,
const std::vector<std::string> &startup_commands, const std::string &remote_host) { const std::vector<std::string> &startup_commands, const std::string &remote_host) {
LockGuard lock(mutex);
if(!debugger) { if(!debugger) {
lldb::SBDebugger::Initialize(); lldb::SBDebugger::Initialize();
debugger = std::make_unique<lldb::SBDebugger>(lldb::SBDebugger::Create(true, log, nullptr)); debugger = std::make_unique<lldb::SBDebugger>(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<lldb::SBProcess>(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error)); process = std::make_unique<lldb::SBProcess>(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error));
} }
if(error.Fail()) { if(error.Fail()) {
Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true); Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
for(auto &handler : on_exit) for(auto &handler : on_exit)
handler(-1); handler(-1);
return; return;
} }
if(debug_thread.joinable()) if(debug_thread.joinable())
debug_thread.join(); debug_thread.join();
for(auto &handler : on_start) 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]() { debug_thread = std::thread([this]() {
lldb::SBEvent event; lldb::SBEvent event;
while(true) { while(true) {
std::unique_lock<std::mutex> lock(mutex); LockGuard lock(mutex);
if(listener->GetNextEvent(event)) { if(listener->GetNextEvent(event)) {
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) { if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) {
auto state = process->GetStateFromEvent(event); 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() { void Debug::LLDB::continue_debug() {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::StateType::eStateStopped) if(state == lldb::StateType::eStateStopped)
process->Continue(); process->Continue();
} }
void Debug::LLDB::stop() { void Debug::LLDB::stop() {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::StateType::eStateRunning) { if(state == lldb::StateType::eStateRunning) {
auto error = process->Stop(); auto error = process->Stop();
if(error.Fail()) if(error.Fail())
@ -261,7 +265,7 @@ void Debug::LLDB::stop() {
} }
void Debug::LLDB::kill() { void Debug::LLDB::kill() {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(process) { if(process) {
auto error = process->Kill(); auto error = process->Kill();
if(error.Fail()) if(error.Fail())
@ -270,29 +274,29 @@ void Debug::LLDB::kill() {
} }
void Debug::LLDB::step_over() { void Debug::LLDB::step_over() {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
process->GetSelectedThread().StepOver(); process->GetSelectedThread().StepOver();
} }
} }
void Debug::LLDB::step_into() { void Debug::LLDB::step_into() {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
process->GetSelectedThread().StepInto(); process->GetSelectedThread().StepInto();
} }
} }
void Debug::LLDB::step_out() { void Debug::LLDB::step_out() {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
process->GetSelectedThread().StepOut(); process->GetSelectedThread().StepOut();
} }
} }
std::pair<std::string, std::string> Debug::LLDB::run_command(const std::string &command) { std::pair<std::string, std::string> Debug::LLDB::run_command(const std::string &command) {
LockGuard lock(mutex);
std::pair<std::string, std::string> command_return; std::pair<std::string, std::string> command_return;
std::lock_guard<std::mutex> lock(mutex);
if(state == lldb::StateType::eStateStopped || state == lldb::StateType::eStateRunning) { if(state == lldb::StateType::eStateStopped || state == lldb::StateType::eStateRunning) {
lldb::SBCommandReturnObject command_return_object; lldb::SBCommandReturnObject command_return_object;
debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true); debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true);
@ -307,8 +311,8 @@ std::pair<std::string, std::string> Debug::LLDB::run_command(const std::string &
} }
std::vector<Debug::LLDB::Frame> Debug::LLDB::get_backtrace() { std::vector<Debug::LLDB::Frame> Debug::LLDB::get_backtrace() {
LockGuard lock(mutex);
std::vector<Frame> backtrace; std::vector<Frame> backtrace;
std::lock_guard<std::mutex> lock(mutex);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
auto thread = process->GetSelectedThread(); auto thread = process->GetSelectedThread();
for(uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) { for(uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) {
@ -343,8 +347,8 @@ std::vector<Debug::LLDB::Frame> Debug::LLDB::get_backtrace() {
} }
std::vector<Debug::LLDB::Variable> Debug::LLDB::get_variables() { std::vector<Debug::LLDB::Variable> Debug::LLDB::get_variables() {
LockGuard lock(mutex);
std::vector<Debug::LLDB::Variable> variables; std::vector<Debug::LLDB::Variable> variables;
std::lock_guard<std::mutex> lock(mutex);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
for(uint32_t c_t = 0; c_t < process->GetNumThreads(); c_t++) { for(uint32_t c_t = 0; c_t < process->GetNumThreads(); c_t++) {
auto thread = process->GetThreadAtIndex(c_t); auto thread = process->GetThreadAtIndex(c_t);
@ -398,7 +402,7 @@ std::vector<Debug::LLDB::Variable> Debug::LLDB::get_variables() {
} }
void Debug::LLDB::select_frame(uint32_t frame_index, uint32_t thread_index_id) { void Debug::LLDB::select_frame(uint32_t frame_index, uint32_t thread_index_id) {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
if(thread_index_id != 0) if(thread_index_id != 0)
process->SetSelectedThreadByIndexID(thread_index_id); 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::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<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
auto frame = process->GetSelectedThread().GetSelectedFrame(); 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) { std::string Debug::LLDB::get_value(const std::string &expression) {
LockGuard lock(mutex);
std::string variable_value; std::string variable_value;
std::lock_guard<std::mutex> lock(mutex);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
auto frame = process->GetSelectedThread().GetSelectedFrame(); auto frame = process->GetSelectedThread().GetSelectedFrame();
if(variable_value.empty()) { 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) { 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::string return_value;
std::lock_guard<std::mutex> lock(mutex);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {
auto thread = process->GetSelectedThread(); auto thread = process->GetSelectedThread();
auto thread_return_value = thread.GetStopReturnValue(); 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() { bool Debug::LLDB::is_invalid() {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
return state == lldb::StateType::eStateInvalid; return state == lldb::StateType::eStateInvalid;
} }
bool Debug::LLDB::is_stopped() { bool Debug::LLDB::is_stopped() {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
return state == lldb::StateType::eStateStopped; return state == lldb::StateType::eStateStopped;
} }
bool Debug::LLDB::is_running() { bool Debug::LLDB::is_running() {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
return state == lldb::StateType::eStateRunning; return state == lldb::StateType::eStateRunning;
} }
void Debug::LLDB::add_breakpoint(const boost::filesystem::path &file_path, int line_nr) { void Debug::LLDB::add_breakpoint(const boost::filesystem::path &file_path, int line_nr) {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::eStateStopped || state == lldb::eStateRunning) { if(state == lldb::eStateStopped || state == lldb::eStateRunning) {
if(!(process->GetTarget().BreakpointCreateByLocation(file_path.string().c_str(), line_nr)).IsValid()) 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); 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) { void Debug::LLDB::remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::eStateStopped || state == lldb::eStateRunning) { if(state == lldb::eStateStopped || state == lldb::eStateRunning) {
auto target = process->GetTarget(); auto target = process->GetTarget();
for(int line_nr_try = line_nr; line_nr_try < line_count; line_nr_try++) { 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) { void Debug::LLDB::write(const std::string &buffer) {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(state == lldb::StateType::eStateRunning) { if(state == lldb::StateType::eStateRunning) {
process->PutSTDIN(buffer.c_str(), buffer.size()); process->PutSTDIN(buffer.c_str(), buffer.size());
} }

12
src/debug_lldb.h

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "mutex.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <list> #include <list>
#include <lldb/API/LLDB.h> #include <lldb/API/LLDB.h>
#include <mutex>
#include <thread> #include <thread>
#include <tuple> #include <tuple>
@ -45,7 +45,7 @@ namespace Debug {
/// The handlers are not run in the main loop. /// The handlers are not run in the main loop.
std::list<std::function<void(const lldb::SBEvent &)>> on_event; std::list<std::function<void(const lldb::SBEvent &)>> on_event;
std::mutex mutex; Mutex mutex;
void start(const std::string &command, const boost::filesystem::path &path = "", void start(const std::string &command, const boost::filesystem::path &path = "",
const std::vector<std::pair<boost::filesystem::path, int>> &breakpoints = {}, const std::vector<std::pair<boost::filesystem::path, int>> &breakpoints = {},
@ -81,12 +81,12 @@ namespace Debug {
private: private:
std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> parse_run_arguments(const std::string &command); std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> parse_run_arguments(const std::string &command);
std::unique_ptr<lldb::SBDebugger> debugger; std::unique_ptr<lldb::SBDebugger> debugger GUARDED_BY(mutex);
std::unique_ptr<lldb::SBListener> listener; std::unique_ptr<lldb::SBListener> listener GUARDED_BY(mutex);
std::unique_ptr<lldb::SBProcess> process; std::unique_ptr<lldb::SBProcess> process GUARDED_BY(mutex);
std::thread debug_thread; std::thread debug_thread;
lldb::StateType state; lldb::StateType state GUARDED_BY(mutex);
size_t buffer_size; size_t buffer_size;
}; };

1
src/directories.h

@ -4,7 +4,6 @@
#include "git.h" #include "git.h"
#include <atomic> #include <atomic>
#include <gtkmm.h> #include <gtkmm.h>
#include <mutex>
#include <string> #include <string>
#include <thread> #include <thread>
#include <unordered_map> #include <unordered_map>

6
src/dispatcher.cc

@ -9,7 +9,7 @@ void Dispatcher::connect() {
connection = dispatcher.connect([this] { connection = dispatcher.connect([this] {
std::vector<std::list<std::function<void()>>::iterator> its; std::vector<std::list<std::function<void()>>::iterator> its;
{ {
std::lock_guard<std::mutex> lock(functions_mutex); LockGuard lock(functions_mutex);
if(functions.empty()) if(functions.empty())
return; return;
its.reserve(functions.size()); its.reserve(functions.size());
@ -19,7 +19,7 @@ void Dispatcher::connect() {
for(auto &it : its) for(auto &it : its)
(*it)(); (*it)();
{ {
std::lock_guard<std::mutex> lock(functions_mutex); LockGuard lock(functions_mutex);
for(auto &it : its) for(auto &it : its)
functions.erase(it); functions.erase(it);
} }
@ -35,7 +35,7 @@ void Dispatcher::disconnect() {
} }
void Dispatcher::reset() { void Dispatcher::reset() {
std::lock_guard<std::mutex> lock(functions_mutex); LockGuard lock(functions_mutex);
disconnect(); disconnect();
functions.clear(); functions.clear();
connect(); connect();

8
src/dispatcher.h

@ -1,13 +1,13 @@
#pragma once #pragma once
#include "mutex.h"
#include <functional> #include <functional>
#include <gtkmm.h> #include <gtkmm.h>
#include <list> #include <list>
#include <mutex>
class Dispatcher { class Dispatcher {
private: private:
std::list<std::function<void()>> functions; Mutex functions_mutex;
std::mutex functions_mutex; std::list<std::function<void()>> functions GUARDED_BY(functions_mutex);
Glib::Dispatcher dispatcher; Glib::Dispatcher dispatcher;
sigc::connection connection; sigc::connection connection;
@ -22,7 +22,7 @@ public:
/// Can be called from any thread. /// Can be called from any thread.
template <typename T> template <typename T>
void post(T &&function) { void post(T &&function) {
std::lock_guard<std::mutex> lock(functions_mutex); LockGuard lock(functions_mutex);
functions.emplace_back(std::forward<T>(function)); functions.emplace_back(std::forward<T>(function));
dispatcher(); dispatcher();
} }

240
src/git.cc

@ -3,7 +3,8 @@
#include <unordered_map> #include <unordered_map>
bool Git::initialized = false; bool Git::initialized = false;
std::mutex Git::mutex; Mutex Git::mutex;
Git::Error Git::error;
std::string Git::Error::message() noexcept { std::string Git::Error::message() noexcept {
#if LIBGIT2_VER_MAJOR > 0 || (LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR >= 28) #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) if(blob)
git_blob_free(blob); git_blob_free(blob);
}); });
Error error;
std::lock_guard<std::mutex> lock(mutex);
auto spec = "HEAD:" + path.generic_string(); auto spec = "HEAD:" + path.generic_string();
LockGuard lock(mutex);
error.code = git_revparse_single(reinterpret_cast<git_object **>(&blob), repository, spec.c_str()); error.code = git_revparse_single(reinterpret_cast<git_object **>(&blob), repository, spec.c_str());
if(error) if(error)
throw std::runtime_error(error.message()); 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; 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<Lines *>(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) { Git::Repository::Diff::Lines Git::Repository::Diff::get_lines(const std::string &buffer) {
Lines lines; Lines lines;
Error error; LockGuard lock(mutex);
std::lock_guard<std::mutex> 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) {
error.code = git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, hunk_cb, nullptr, &lines); //Based on https://github.com/atom/git-diff/blob/master/lib/git-diff-view.coffee
auto lines = static_cast<Lines *>(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) if(error)
throw std::runtime_error(error.message()); throw std::runtime_error(error.message());
return lines; return lines;
} }
std::vector<Git::Repository::Diff::Hunk> Git::Repository::Diff::get_hunks(const std::string &old_buffer, const std::string &new_buffer) { std::vector<Git::Repository::Diff::Hunk> Git::Repository::Diff::get_hunks(const std::string &old_buffer, const std::string &new_buffer) {
initialize();
std::vector<Git::Repository::Diff::Hunk> hunks; std::vector<Git::Repository::Diff::Hunk> hunks;
Error error; LockGuard lock(mutex);
initialize();
git_diff_options options; git_diff_options options;
git_diff_init_options(&options, GIT_DIFF_OPTIONS_VERSION); git_diff_init_options(&options, GIT_DIFF_OPTIONS_VERSION);
options.context_lines = 0; options.context_lines = 0;
std::lock_guard<std::mutex> 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) { 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<std::vector<Git::Repository::Diff::Hunk> *>(payload); auto hunks = static_cast<std::vector<Git::Repository::Diff::Hunk> *>(payload);
hunks->emplace_back(hunk->old_start, hunk->old_lines, hunk->new_start, hunk->new_lines); hunks->emplace_back(hunk->old_start, hunk->old_lines, hunk->new_start, hunk->new_lines);
@ -76,25 +72,22 @@ std::vector<Git::Repository::Diff::Hunk> Git::Repository::Diff::get_hunks(const
return hunks; 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<std::pair<std::string, int> *>(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::string Git::Repository::Diff::get_details(const std::string &buffer, int line_nr) {
std::pair<std::string, int> details; std::pair<std::string, int> details;
details.second = line_nr; details.second = line_nr;
Error error; LockGuard lock(mutex);
std::lock_guard<std::mutex> 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) {
error.code = git_diff_blob_to_buffer(blob.get(), nullptr, buffer.c_str(), buffer.size(), nullptr, &options, nullptr, nullptr, nullptr, line_cb, &details); auto details = static_cast<std::pair<std::string, int> *>(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) if(error)
throw std::runtime_error(error.message()); throw std::runtime_error(error.message());
return details.first; 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(const boost::filesystem::path &path) {
git_repository *repository_ptr; git_repository *repository_ptr;
{ {
Error error; LockGuard lock(mutex);
std::lock_guard<std::mutex> lock(mutex);
error.code = git_repository_open_ext(&repository_ptr, path.generic_string().c_str(), 0, nullptr); error.code = git_repository_open_ext(&repository_ptr, path.generic_string().c_str(), 0, nullptr);
if(error) if(error)
throw std::runtime_error(error.message()); throw std::runtime_error(error.message());
@ -132,123 +124,88 @@ Git::Repository::~Repository() {
monitor_changed_connection.disconnect(); monitor_changed_connection.disconnect();
} }
std::string Git::Repository::status_string(STATUS status) noexcept { Git::Repository::Status Git::Repository::get_status() {
switch(status) { {
case STATUS::CURRENT: LockGuard lock(saved_status_mutex);
return "current"; if(has_saved_status)
case STATUS::NEW: return saved_status;
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 "";
} }
}
int Git::Repository::status_callback(const char *path, unsigned int status_flags, void *data) noexcept { struct Data {
auto callback = static_cast<std::function<void(const char *path, STATUS status)> *>(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<Data *>(payload);
STATUS status; STATUS status;
if((status_flags & (GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW)) > 0) if((status_flags & (GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_NEW)) > 0)
status = STATUS::NEW; status = STATUS::NEW;
else if((status_flags & (GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED)) > 0) else if((status_flags & (GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED)) > 0)
status = STATUS::MODIFIED; status = STATUS::MODIFIED;
else if((status_flags & (GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED)) > 0) else if((status_flags & (GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_DELETED)) > 0)
status = STATUS::DELETED; status = STATUS::DELETED;
else if((status_flags & (GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED)) > 0) else if((status_flags & (GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED)) > 0)
status = STATUS::RENAMED; status = STATUS::RENAMED;
else if((status_flags & (GIT_STATUS_INDEX_TYPECHANGE | GIT_STATUS_WT_TYPECHANGE)) > 0) else if((status_flags & (GIT_STATUS_INDEX_TYPECHANGE | GIT_STATUS_WT_TYPECHANGE)) > 0)
status = STATUS::TYPECHANGE; status = STATUS::TYPECHANGE;
else if((status_flags & (GIT_STATUS_WT_UNREADABLE)) > 0) else if((status_flags & (GIT_STATUS_WT_UNREADABLE)) > 0)
status = STATUS::UNREADABLE; status = STATUS::UNREADABLE;
else if((status_flags & (GIT_STATUS_IGNORED)) > 0) else if((status_flags & (GIT_STATUS_IGNORED)) > 0)
status = STATUS::IGNORED; status = STATUS::IGNORED;
else if((status_flags & (GIT_STATUS_CONFLICTED)) > 0) else if((status_flags & (GIT_STATUS_CONFLICTED)) > 0)
status = STATUS::CONFLICTED; status = STATUS::CONFLICTED;
else else
status = STATUS::CURRENT; status = STATUS::CURRENT;
if(*callback) boost::filesystem::path rel_path(path);
(*callback)(path, status); 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() { if(error)
{ throw std::runtime_error(error.message());
std::lock_guard<std::mutex> lock(saved_status_mutex);
if(has_saved_status)
return saved_status;
} }
Status status; LockGuard lock(saved_status_mutex);
bool first = true; saved_status = std::move(data.status);
std::unique_lock<std::mutex> status_saved_lock(saved_status_mutex, std::defer_lock);
std::function<void(const char *path, STATUS status)> 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<std::mutex> lock(mutex);
error.code = git_status_foreach(repository.get(), status_callback, &callback);
if(error)
throw std::runtime_error(error.message());
saved_status = status;
has_saved_status = true; has_saved_status = true;
if(status_saved_lock) return saved_status;
status_saved_lock.unlock();
return status;
} }
void Git::Repository::clear_saved_status() { void Git::Repository::clear_saved_status() {
std::lock_guard<std::mutex> lock(saved_status_mutex); LockGuard lock(saved_status_mutex);
saved_status.added.clear(); saved_status = {};
saved_status.modified.clear();
has_saved_status = false; has_saved_status = false;
} }
boost::filesystem::path Git::Repository::get_work_path() noexcept { boost::filesystem::path Git::Repository::get_work_path() noexcept {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
return Git::path(git_repository_workdir(repository.get())); return Git::path(git_repository_workdir(repository.get()));
} }
boost::filesystem::path Git::Repository::get_path() noexcept { boost::filesystem::path Git::Repository::get_path() noexcept {
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
return Git::path(git_repository_path(repository.get())); return Git::path(git_repository_path(repository.get()));
} }
boost::filesystem::path Git::Repository::get_root_path(const boost::filesystem::path &path) { boost::filesystem::path Git::Repository::get_root_path(const boost::filesystem::path &path) {
initialize();
git_buf root = {nullptr, 0, 0}; git_buf root = {nullptr, 0, 0};
{ LockGuard lock(mutex);
Error error; initialize();
std::lock_guard<std::mutex> lock(mutex); error.code = git_repository_discover(&root, path.generic_string().c_str(), 0, nullptr);
error.code = git_repository_discover(&root, path.generic_string().c_str(), 0, nullptr); if(error)
if(error) throw std::runtime_error(error.message());
throw std::runtime_error(error.message());
}
auto root_path = Git::path(root.ptr, root.size); auto root_path = Git::path(root.ptr, root.size);
#if LIBGIT2_VER_MAJOR > 0 || (LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR >= 28) #if LIBGIT2_VER_MAJOR > 0 || (LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR >= 28)
git_buf_dispose(&root); 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 Git::Repository::get_branch() noexcept {
std::string branch; std::string branch;
git_reference *reference; 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)) { if(auto reference_name_cstr = git_reference_name(reference)) {
std::string reference_name(reference_name_cstr); std::string reference_name(reference_name_cstr);
size_t pos; size_t pos;
@ -284,7 +243,6 @@ std::string Git::Repository::get_branch() noexcept {
} }
void Git::initialize() noexcept { void Git::initialize() noexcept {
std::lock_guard<std::mutex> lock(mutex);
if(!initialized) { if(!initialized) {
git_libgit2_init(); git_libgit2_init();
initialized = true; initialized = true;
@ -292,12 +250,12 @@ void Git::initialize() noexcept {
} }
std::shared_ptr<Git::Repository> Git::get_repository(const boost::filesystem::path &path) { std::shared_ptr<Git::Repository> Git::get_repository(const boost::filesystem::path &path) {
initialize();
static std::unordered_map<std::string, std::weak_ptr<Git::Repository>> cache;
static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);
auto root_path = Repository::get_root_path(path).generic_string(); auto root_path = Repository::get_root_path(path).generic_string();
static Mutex mutex;
static std::unordered_map<std::string, std::weak_ptr<Git::Repository>> cache GUARDED_BY(mutex);
LockGuard lock(mutex);
auto it = cache.find(root_path); auto it = cache.find(root_path);
if(it == cache.end()) if(it == cache.end())
it = cache.emplace(root_path, std::weak_ptr<Git::Repository>()).first; it = cache.emplace(root_path, std::weak_ptr<Git::Repository>()).first;

26
src/git.h

@ -1,10 +1,10 @@
#pragma once #pragma once
#include "mutex.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <giomm.h> #include <giomm.h>
#include <git2.h> #include <git2.h>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <mutex>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@ -18,7 +18,7 @@ public:
public: public:
int code = 0; int code = 0;
operator bool() noexcept { return code < 0; } operator bool() noexcept { return code != 0; }
}; };
class Repository { class Repository {
@ -45,8 +45,6 @@ public:
Diff(const boost::filesystem::path &path, git_repository *repository); Diff(const boost::filesystem::path &path, git_repository *repository);
std::shared_ptr<git_blob> blob = nullptr; std::shared_ptr<git_blob> blob = nullptr;
git_diff_options options; 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: public:
Lines get_lines(const std::string &buffer); Lines get_lines(const std::string &buffer);
@ -65,21 +63,17 @@ public:
friend class Git; friend class Git;
Repository(const boost::filesystem::path &path); Repository(const boost::filesystem::path &path);
static int status_callback(const char *path, unsigned int status_flags, void *data) noexcept;
std::unique_ptr<git_repository, std::function<void(git_repository *)>> repository; std::unique_ptr<git_repository, std::function<void(git_repository *)>> repository;
boost::filesystem::path work_path; boost::filesystem::path work_path;
sigc::connection monitor_changed_connection; sigc::connection monitor_changed_connection;
Status saved_status; Mutex saved_status_mutex;
bool has_saved_status = false; Status saved_status GUARDED_BY(saved_status_mutex);
std::mutex saved_status_mutex; bool has_saved_status GUARDED_BY(saved_status_mutex) = false;
public: public:
~Repository(); ~Repository();
static std::string status_string(STATUS status) noexcept;
Status get_status(); Status get_status();
void clear_saved_status(); void clear_saved_status();
@ -95,15 +89,17 @@ public:
}; };
private: private:
static bool initialized; static bool initialized GUARDED_BY(mutex);
///Mutex for thread safe operations ///Mutex for thread safe operations
static std::mutex mutex; static Mutex mutex;
static Error error GUARDED_BY(mutex);
///Call initialize in public static methods ///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<size_t>(-1)) noexcept; static boost::filesystem::path path(const char *cpath, size_t cpath_length = static_cast<size_t>(-1)) noexcept REQUIRES(mutex);
public: public:
static std::shared_ptr<Repository> get_repository(const boost::filesystem::path &path); static std::shared_ptr<Repository> get_repository(const boost::filesystem::path &path);

112
src/mutex.h

@ -0,0 +1,112 @@
// Based on https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
#pragma once
#include <mutex>
// 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();
}
};

7
src/notebook.cc

@ -115,7 +115,12 @@ void Notebook::open(const boost::filesystem::path &file_path_, size_t notebook_i
if(ec) if(ec)
canonical_file_path = file_path; canonical_file_path = file_path;
for(size_t c = 0; c < size(); c++) { 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); auto notebook_page = get_notebook_page(c);
notebooks[notebook_page.first].set_current_page(notebook_page.second); notebooks[notebook_page.first].set_current_page(notebook_page.second);
focus_view(source_views[c]); focus_view(source_views[c]);

3
src/project.cc

@ -3,6 +3,7 @@
#include "directories.h" #include "directories.h"
#include "filesystem.h" #include "filesystem.h"
#include "menu.h" #include "menu.h"
#include "mutex.h"
#include "notebook.h" #include "notebook.h"
#include "selection_dialog.h" #include "selection_dialog.h"
#include "terminal.h" #include "terminal.h"
@ -409,7 +410,7 @@ void Project::LLDB::debug_start() {
boost::filesystem::path stop_path; boost::filesystem::path stop_path;
unsigned stop_line = 0, stop_column = 0; unsigned stop_line = 0, stop_column = 0;
std::lock_guard<std::mutex> lock(Debug::LLDB::get().mutex); LockGuard lock(Debug::LLDB::get().mutex);
auto process = lldb::SBProcess::GetProcessFromEvent(event); auto process = lldb::SBProcess::GetProcessFromEvent(event);
auto state = lldb::SBProcess::GetStateFromEvent(event); auto state = lldb::SBProcess::GetStateFromEvent(event);
lldb::SBStream stream; lldb::SBStream stream;

2
src/source_base.cc

@ -937,7 +937,7 @@ void Source::BaseView::search_occurrences_updated(GtkWidget *widget, GParamSpec
} }
void Source::BaseView::set_snippets() { void Source::BaseView::set_snippets() {
std::lock_guard<std::mutex> lock(snippets_mutex); LockGuard lock(snippets_mutex);
snippets = nullptr; snippets = nullptr;

6
src/source_base.h

@ -1,10 +1,10 @@
#pragma once #pragma once
#include "mutex.h"
#include "snippets.h" #include "snippets.h"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <gtksourceviewmm.h> #include <gtksourceviewmm.h>
#include <list> #include <list>
#include <mutex>
#include <regex> #include <regex>
#include <set> #include <set>
#include <vector> #include <vector>
@ -136,8 +136,8 @@ namespace Source {
/// After inserting a snippet, one can use tab to select the next argument /// After inserting a snippet, one can use tab to select the next argument
bool keep_snippet_marks = false; bool keep_snippet_marks = false;
std::vector<Snippets::Snippet> *snippets = nullptr; Mutex snippets_mutex;
std::mutex snippets_mutex; std::vector<Snippets::Snippet> *snippets GUARDED_BY(snippets_mutex) = nullptr;
std::list<std::vector<std::pair<Glib::RefPtr<Gtk::TextBuffer::Mark>, Glib::RefPtr<Gtk::TextBuffer::Mark>>>> snippets_marks; std::list<std::vector<std::pair<Glib::RefPtr<Gtk::TextBuffer::Mark>, Glib::RefPtr<Gtk::TextBuffer::Mark>>>> snippets_marks;
Glib::RefPtr<Gtk::TextTag> snippet_argument_tag; Glib::RefPtr<Gtk::TextTag> snippet_argument_tag;
void insert_snippet(Gtk::TextIter iter, Glib::ustring snippet); void insert_snippet(Gtk::TextIter iter, Glib::ustring snippet);

36
src/source_clang.cc

@ -128,7 +128,10 @@ void Source::ClangViewParse::parse_initialize() {
clang_tokens_offsets.reserve(clang_tokens->size()); clang_tokens_offsets.reserve(clang_tokens->size());
for(auto &token : *clang_tokens) for(auto &token : *clang_tokens)
clang_tokens_offsets.emplace_back(token.get_source_range().get_offsets()); clang_tokens_offsets.emplace_back(token.get_source_range().get_offsets());
update_syntax(); {
LockGuard lock(parse_mutex);
update_syntax();
}
status_state = "parsing..."; status_state = "parsing...";
if(update_status_state) if(update_status_state)
@ -140,21 +143,19 @@ void Source::ClangViewParse::parse_initialize() {
if(parse_state != ParseState::PROCESSING) if(parse_state != ParseState::PROCESSING)
break; break;
auto expected = ParseProcessState::STARTING; auto expected = ParseProcessState::STARTING;
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock);
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) { if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) {
dispatcher.post([this] { dispatcher.post([this] {
auto expected = ParseProcessState::PREPROCESSING; auto expected = ParseProcessState::PREPROCESSING;
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock); if(parse_mutex.try_lock()) {
if(parse_lock.try_lock()) {
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING)) if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING))
parse_thread_buffer = get_buffer()->get_text(); parse_thread_buffer = get_buffer()->get_text();
parse_lock.unlock(); parse_mutex.unlock();
} }
else else
parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING); 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<std::string &>(parse_thread_buffer.raw()); auto &parse_thread_buffer_raw = const_cast<std::string &>(parse_thread_buffer.raw());
if(this->language && (this->language->get_id() == "chdr" || this->language->get_id() == "cpphdr")) if(this->language && (this->language->get_id() == "chdr" || this->language->get_id() == "cpphdr"))
clangmm::remove_include_guard(parse_thread_buffer_raw); clangmm::remove_include_guard(parse_thread_buffer_raw);
@ -168,10 +169,9 @@ void Source::ClangViewParse::parse_initialize() {
for(auto &token : *clang_tokens) for(auto &token : *clang_tokens)
clang_tokens_offsets.emplace_back(token.get_source_range().get_offsets()); clang_tokens_offsets.emplace_back(token.get_source_range().get_offsets());
clang_diagnostics = clang_tu->get_diagnostics(); clang_diagnostics = clang_tu->get_diagnostics();
parse_lock.unlock(); parse_mutex.unlock();
dispatcher.post([this] { dispatcher.post([this] {
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock); if(parse_mutex.try_lock()) {
if(parse_lock.try_lock()) {
auto expected = ParseProcessState::POSTPROCESSING; auto expected = ParseProcessState::POSTPROCESSING;
if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) { if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) {
update_syntax(); update_syntax();
@ -181,16 +181,16 @@ void Source::ClangViewParse::parse_initialize() {
if(update_status_state) if(update_status_state)
update_status_state(this); update_status_state(this);
} }
parse_lock.unlock(); parse_mutex.unlock();
} }
}); });
} }
else else
parse_lock.unlock(); parse_mutex.unlock();
} }
else { else {
parse_state = ParseState::STOP; parse_state = ParseState::STOP;
parse_lock.unlock(); parse_mutex.unlock();
dispatcher.post([this] { dispatcher.post([this] {
Terminal::get().print("Error: failed to reparse " + this->file_path.string() + ".\n", true); Terminal::get().print("Error: failed to reparse " + this->file_path.string() + ".\n", true);
status_state = ""; status_state = "";
@ -562,7 +562,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
}; };
autocomplete.get_parse_lock = [this]() { autocomplete.get_parse_lock = [this]() {
return std::make_unique<std::lock_guard<std::mutex>>(parse_mutex); return std::make_unique<LockGuard>(parse_mutex);
}; };
autocomplete.stop_parse = [this]() { autocomplete.stop_parse = [this]() {
@ -640,7 +640,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, regex)) { if(std::regex_match(line, sm, regex)) {
{ {
std::lock_guard<std::mutex> 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(); autocomplete.prefix = sm.length(2) ? sm[3].str() : sm.length(4) ? sm[5].str() : sm[6].str();
if(!sm.length(2) && !sm.length(4)) if(!sm.length(2) && !sm.length(4))
enable_snippets = true; enable_snippets = true;
@ -649,7 +649,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
} }
else if(is_possible_argument()) { else if(is_possible_argument()) {
show_parameters = true; show_parameters = true;
std::lock_guard<std::mutex> lock(autocomplete.prefix_mutex); LockGuard lock(autocomplete.prefix_mutex);
autocomplete.prefix = ""; autocomplete.prefix = "";
return true; return true;
} }
@ -662,7 +662,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
iter.forward_char(); iter.forward_char();
{ {
std::lock_guard<std::mutex> lock(autocomplete.prefix_mutex); LockGuard lock(autocomplete.prefix_mutex);
autocomplete.prefix = get_buffer()->get_text(iter, end_iter); autocomplete.prefix = get_buffer()->get_text(iter, end_iter);
} }
auto prev1 = iter; auto prev1 = iter;
@ -712,7 +712,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
if(autocomplete.state == Autocomplete::State::STARTING) { if(autocomplete.state == Autocomplete::State::STARTING) {
std::string prefix; std::string prefix;
{ {
std::lock_guard<std::mutex> lock(autocomplete.prefix_mutex); LockGuard lock(autocomplete.prefix_mutex);
prefix = autocomplete.prefix; prefix = autocomplete.prefix;
} }
@ -782,7 +782,7 @@ Source::ClangViewAutocomplete::ClangViewAutocomplete(const boost::filesystem::pa
} }
} }
if(!show_parameters && enable_snippets) { if(!show_parameters && enable_snippets) {
std::lock_guard<std::mutex> lock(snippets_mutex); LockGuard lock(snippets_mutex);
if(snippets) { if(snippets) {
for(auto &snippet : *snippets) { for(auto &snippet : *snippets) {
if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) { if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) {

12
src/source_clang.h

@ -2,11 +2,11 @@
#include "autocomplete.h" #include "autocomplete.h"
#include "clangmm.h" #include "clangmm.h"
#include "dispatcher.h" #include "dispatcher.h"
#include "mutex.h"
#include "source.h" #include "source.h"
#include "terminal.h" #include "terminal.h"
#include <atomic> #include <atomic>
#include <map> #include <map>
#include <mutex>
#include <set> #include <set>
#include <thread> #include <thread>
@ -37,22 +37,22 @@ namespace Source {
std::vector<FixIt> fix_its; std::vector<FixIt> fix_its;
Mutex parse_mutex;
std::thread parse_thread; std::thread parse_thread;
std::mutex parse_mutex;
std::atomic<ParseState> parse_state; std::atomic<ParseState> parse_state;
std::atomic<ParseProcessState> parse_process_state; std::atomic<ParseProcessState> parse_process_state;
CXCompletionString selected_completion_string = nullptr; CXCompletionString selected_completion_string = nullptr;
private: private:
Glib::ustring parse_thread_buffer; Glib::ustring parse_thread_buffer GUARDED_BY(parse_mutex);
static const std::map<int, std::string> &clang_types(); static const std::map<int, std::string> &clang_types();
void update_syntax(); void update_syntax() REQUIRES(parse_mutex);
std::map<int, Glib::RefPtr<Gtk::TextTag>> syntax_tags; std::map<int, Glib::RefPtr<Gtk::TextTag>> syntax_tags;
void update_diagnostics(); void update_diagnostics() REQUIRES(parse_mutex);
std::vector<clangmm::Diagnostic> clang_diagnostics; std::vector<clangmm::Diagnostic> clang_diagnostics GUARDED_BY(parse_mutex);
}; };
class ClangViewAutocomplete : public virtual ClangViewParse { class ClangViewAutocomplete : public virtual ClangViewParse {

48
src/source_diff.cc

@ -80,8 +80,9 @@ void Source::DiffView::configure() {
if(parse_thread.joinable()) if(parse_thread.joinable())
parse_thread.join(); parse_thread.join();
repository = nullptr; repository = nullptr;
diff = nullptr;
LockGuard lock(parse_mutex);
diff = nullptr;
return; return;
} }
else else
@ -149,7 +150,7 @@ void Source::DiffView::configure() {
delayed_monitor_changed_connection = Glib::signal_timeout().connect([this]() { delayed_monitor_changed_connection = Glib::signal_timeout().connect([this]() {
monitor_changed = true; monitor_changed = true;
parse_state = ParseState::STARTING; parse_state = ParseState::STARTING;
std::lock_guard<std::mutex> lock(parse_mutex); LockGuard lock(parse_mutex);
diff = nullptr; diff = nullptr;
return false; return false;
}, 500); }, 500);
@ -159,7 +160,10 @@ void Source::DiffView::configure() {
parse_thread = std::thread([this]() { parse_thread = std::thread([this]() {
std::string status_branch; std::string status_branch;
try { try {
diff = get_diff(); {
LockGuard lock(parse_mutex);
diff = get_diff();
}
status_branch = repository->get_branch(); status_branch = repository->get_branch();
} }
catch(const std::exception &) { catch(const std::exception &) {
@ -177,22 +181,20 @@ void Source::DiffView::configure() {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
if(parse_stop) if(parse_stop)
break; break;
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock);
auto expected = ParseState::STARTING; auto expected = ParseState::STARTING;
if(parse_state.compare_exchange_strong(expected, ParseState::PREPROCESSING)) { if(parse_state.compare_exchange_strong(expected, ParseState::PREPROCESSING)) {
dispatcher.post([this] { dispatcher.post([this] {
auto expected = ParseState::PREPROCESSING; auto expected = ParseState::PREPROCESSING;
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock); if(parse_mutex.try_lock()) {
if(parse_lock.try_lock()) {
if(parse_state.compare_exchange_strong(expected, ParseState::PROCESSING)) if(parse_state.compare_exchange_strong(expected, ParseState::PROCESSING))
parse_buffer = get_buffer()->get_text(); parse_buffer = get_buffer()->get_text();
parse_lock.unlock(); parse_mutex.unlock();
} }
else else
parse_state.compare_exchange_strong(expected, ParseState::STARTING); 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; bool expected_monitor_changed = true;
if(monitor_changed.compare_exchange_strong(expected_monitor_changed, false)) { if(monitor_changed.compare_exchange_strong(expected_monitor_changed, false)) {
try { try {
@ -226,16 +228,18 @@ void Source::DiffView::configure() {
} }
auto expected = ParseState::PROCESSING; auto expected = ParseState::PROCESSING;
if(parse_state.compare_exchange_strong(expected, ParseState::POSTPROCESSING)) { if(parse_state.compare_exchange_strong(expected, ParseState::POSTPROCESSING)) {
parse_lock.unlock(); parse_mutex.unlock();
dispatcher.post([this] { dispatcher.post([this] {
std::unique_lock<std::mutex> parse_lock(parse_mutex, std::defer_lock); if(parse_mutex.try_lock()) {
if(parse_lock.try_lock()) {
auto expected = ParseState::POSTPROCESSING; auto expected = ParseState::POSTPROCESSING;
if(parse_state.compare_exchange_strong(expected, ParseState::IDLE)) if(parse_state.compare_exchange_strong(expected, ParseState::IDLE))
update_lines(); 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) { void Source::DiffView::rename(const boost::filesystem::path &path) {
Source::BaseView::rename(path); Source::BaseView::rename(path);
std::lock_guard<std::mutex> lock(canonical_file_path_mutex); LockGuard lock(canonical_file_path_mutex);
boost::system::error_code ec; boost::system::error_code ec;
canonical_file_path = boost::filesystem::canonical(path, ec); canonical_file_path = boost::filesystem::canonical(path, ec);
if(ec) if(ec)
@ -293,14 +297,16 @@ void Source::DiffView::git_goto_next_diff() {
std::string Source::DiffView::git_get_diff_details() { std::string Source::DiffView::git_get_diff_details() {
std::string details; std::string details;
if(diff) { {
auto line_nr = get_buffer()->get_insert()->get_iter().get_line(); LockGuard lock(parse_mutex);
auto iter = get_buffer()->get_iter_at_line(line_nr); if(diff) {
if(iter.has_tag(renderer->tag_removed_above)) auto line_nr = get_buffer()->get_insert()->get_iter().get_line();
--line_nr; auto iter = get_buffer()->get_iter_at_line(line_nr);
std::lock_guard<std::mutex> lock(parse_mutex); if(iter.has_tag(renderer->tag_removed_above))
parse_buffer = get_buffer()->get_text(); --line_nr;
details = diff->get_details(parse_buffer.raw(), line_nr); parse_buffer = get_buffer()->get_text();
details = diff->get_details(parse_buffer.raw(), line_nr);
}
} }
if(details.empty()) if(details.empty())
Info::get().print("No changes found at current line"); Info::get().print("No changes found at current line");
@ -312,7 +318,7 @@ std::unique_ptr<Git::Repository::Diff> Source::DiffView::get_diff() {
auto work_path = filesystem::get_normal_path(repository->get_work_path()); auto work_path = filesystem::get_normal_path(repository->get_work_path());
boost::filesystem::path relative_path; boost::filesystem::path relative_path;
{ {
std::lock_guard<std::mutex> lock(canonical_file_path_mutex); LockGuard lock(canonical_file_path_mutex);
if(!filesystem::file_in_path(canonical_file_path, work_path)) if(!filesystem::file_in_path(canonical_file_path, work_path))
throw std::runtime_error("not a relative path"); throw std::runtime_error("not a relative path");
relative_path = filesystem::get_relative_path(canonical_file_path, work_path); relative_path = filesystem::get_relative_path(canonical_file_path, work_path);

19
src/source_diff.h

@ -1,11 +1,11 @@
#pragma once #pragma once
#include "dispatcher.h" #include "dispatcher.h"
#include "git.h" #include "git.h"
#include "mutex.h"
#include "source_base.h" #include "source_base.h"
#include <atomic> #include <atomic>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <map> #include <map>
#include <mutex>
#include <set> #include <set>
#include <thread> #include <thread>
@ -40,24 +40,25 @@ namespace Source {
void git_goto_next_diff(); void git_goto_next_diff();
std::string git_get_diff_details(); std::string git_get_diff_details();
Mutex canonical_file_path_mutex;
/// Use canonical path to follow symbolic links /// 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: private:
std::mutex canonical_file_path_mutex;
std::unique_ptr<Renderer> renderer; std::unique_ptr<Renderer> renderer;
Dispatcher dispatcher; Dispatcher dispatcher;
Mutex parse_mutex;
std::shared_ptr<Git::Repository> repository; std::shared_ptr<Git::Repository> repository;
std::unique_ptr<Git::Repository::Diff> diff; std::unique_ptr<Git::Repository::Diff> diff GUARDED_BY(parse_mutex);
std::unique_ptr<Git::Repository::Diff> get_diff(); std::unique_ptr<Git::Repository::Diff> get_diff();
std::thread parse_thread; std::thread parse_thread;
std::atomic<ParseState> parse_state; std::atomic<ParseState> parse_state;
std::mutex parse_mutex;
std::atomic<bool> parse_stop; std::atomic<bool> parse_stop;
Glib::ustring parse_buffer; Glib::ustring parse_buffer GUARDED_BY(parse_mutex);
sigc::connection buffer_insert_connection; sigc::connection buffer_insert_connection;
sigc::connection buffer_erase_connection; sigc::connection buffer_erase_connection;
sigc::connection monitor_changed_connection; sigc::connection monitor_changed_connection;
@ -65,7 +66,7 @@ namespace Source {
sigc::connection delayed_monitor_changed_connection; sigc::connection delayed_monitor_changed_connection;
std::atomic<bool> monitor_changed; std::atomic<bool> monitor_changed;
Git::Repository::Diff::Lines lines; Git::Repository::Diff::Lines lines GUARDED_BY(parse_mutex);
void update_lines(); void update_lines() REQUIRES(parse_mutex);
}; };
} // namespace Source } // namespace Source

27
src/source_generic.cc

@ -122,7 +122,7 @@ std::vector<std::pair<Gtk::TextIter, Gtk::TextIter>> Source::GenericView::get_wo
void Source::GenericView::setup_buffer_words() { void Source::GenericView::setup_buffer_words() {
{ {
auto words = get_words(get_buffer()->begin(), get_buffer()->end()); auto words = get_words(get_buffer()->begin(), get_buffer()->end());
std::lock_guard<std::mutex> lock(buffer_words_mutex); LockGuard lock(buffer_words_mutex);
for(auto &word : words) { for(auto &word : words) {
auto result = buffer_words.emplace(get_buffer()->get_text(word.first, word.second), 1); auto result = buffer_words.emplace(get_buffer()->get_text(word.first, word.second), 1);
if(!result.second) if(!result.second)
@ -139,7 +139,7 @@ void Source::GenericView::setup_buffer_words() {
if(is_word_iter(iter)) { if(is_word_iter(iter)) {
auto word = get_word(iter); auto word = get_word(iter);
if(word.second.get_offset() - word.first.get_offset() >= 3) { if(word.second.get_offset() - word.first.get_offset() >= 3) {
std::lock_guard<std::mutex> lock(buffer_words_mutex); LockGuard lock(buffer_words_mutex);
auto it = buffer_words.find(get_buffer()->get_text(word.first, word.second)); auto it = buffer_words.find(get_buffer()->get_text(word.first, word.second));
if(it != buffer_words.end()) { if(it != buffer_words.end()) {
if(it->second > 1) if(it->second > 1)
@ -161,7 +161,7 @@ void Source::GenericView::setup_buffer_words() {
end.forward_char(); end.forward_char();
auto words = get_words(start, end); auto words = get_words(start, end);
std::lock_guard<std::mutex> lock(buffer_words_mutex); LockGuard lock(buffer_words_mutex);
for(auto &word : words) { for(auto &word : words) {
auto result = buffer_words.emplace(get_buffer()->get_text(word.first, word.second), 1); auto result = buffer_words.emplace(get_buffer()->get_text(word.first, word.second), 1);
if(!result.second) if(!result.second)
@ -177,7 +177,7 @@ void Source::GenericView::setup_buffer_words() {
start.backward_char(); start.backward_char();
end.forward_char(); end.forward_char();
auto words = get_words(start, end); auto words = get_words(start, end);
std::lock_guard<std::mutex> lock(buffer_words_mutex); LockGuard lock(buffer_words_mutex);
for(auto &word : words) { for(auto &word : words) {
auto it = buffer_words.find(get_buffer()->get_text(word.first, word.second)); auto it = buffer_words.find(get_buffer()->get_text(word.first, word.second));
if(it != buffer_words.end()) { if(it != buffer_words.end()) {
@ -197,7 +197,7 @@ void Source::GenericView::setup_buffer_words() {
if(is_word_iter(start)) { if(is_word_iter(start)) {
auto word = get_word(start); auto word = get_word(start);
if(word.second.get_offset() - word.first.get_offset() >= 3) { if(word.second.get_offset() - word.first.get_offset() >= 3) {
std::lock_guard<std::mutex> lock(buffer_words_mutex); LockGuard lock(buffer_words_mutex);
auto result = buffer_words.emplace(get_buffer()->get_text(word.first, word.second), 1); auto result = buffer_words.emplace(get_buffer()->get_text(word.first, word.second), 1);
if(!result.second) if(!result.second)
++(result.first->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)) while(start.backward_char() && ((*start >= '0' && *start <= '9') || (*start >= 'a' && *start <= 'z') || (*start >= 'A' && *start <= 'Z') || *start == '_' || *start >= 0x00C0))
++count; ++count;
if((start.is_start() || start.forward_char()) && count >= 3 && !(*start >= '0' && *start <= '9')) { if((start.is_start() || start.forward_char()) && count >= 3 && !(*start >= '0' && *start <= '9')) {
std::lock(autocomplete.prefix_mutex, buffer_words_mutex); LockGuard lock1(autocomplete.prefix_mutex);
std::lock_guard<std::mutex> lock1(autocomplete.prefix_mutex, std::adopt_lock); LockGuard lock2(buffer_words_mutex);
std::lock_guard<std::mutex> lock2(buffer_words_mutex, std::adopt_lock);
autocomplete.prefix = get_buffer()->get_text(start, end); autocomplete.prefix = get_buffer()->get_text(start, end);
show_prefix_buffer_word = buffer_words.find(autocomplete.prefix) != buffer_words.end(); show_prefix_buffer_word = buffer_words.find(autocomplete.prefix) != buffer_words.end();
return true; return true;
@ -250,9 +249,8 @@ void Source::GenericView::setup_autocomplete() {
} }
if(iter != end_iter) if(iter != end_iter)
iter.forward_char(); iter.forward_char();
std::lock(autocomplete.prefix_mutex, buffer_words_mutex); LockGuard lock1(autocomplete.prefix_mutex);
std::lock_guard<std::mutex> lock1(autocomplete.prefix_mutex, std::adopt_lock); LockGuard lock2(buffer_words_mutex);
std::lock_guard<std::mutex> lock2(buffer_words_mutex, std::adopt_lock);
autocomplete.prefix = get_buffer()->get_text(iter, end_iter); autocomplete.prefix = get_buffer()->get_text(iter, end_iter);
show_prefix_buffer_word = buffer_words.find(autocomplete.prefix) != buffer_words.end(); show_prefix_buffer_word = buffer_words.find(autocomplete.prefix) != buffer_words.end();
return true; return true;
@ -285,7 +283,7 @@ void Source::GenericView::setup_autocomplete() {
std::string prefix; std::string prefix;
{ {
std::lock_guard<std::mutex> lock(autocomplete.prefix_mutex); LockGuard lock(autocomplete.prefix_mutex);
prefix = autocomplete.prefix; prefix = autocomplete.prefix;
} }
for(auto &keyword : keywords) { for(auto &keyword : keywords) {
@ -295,9 +293,8 @@ void Source::GenericView::setup_autocomplete() {
autocomplete_comment.emplace_back(""); autocomplete_comment.emplace_back("");
} }
} }
bool show_prefix_buffer_word = this->show_prefix_buffer_word;
{ {
std::lock_guard<std::mutex> lock(buffer_words_mutex); LockGuard lock(buffer_words_mutex);
for(auto &buffer_word : buffer_words) { 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 && 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()) { keywords.find(buffer_word.first) == keywords.end()) {
@ -307,7 +304,7 @@ void Source::GenericView::setup_autocomplete() {
} }
} }
} }
std::lock_guard<std::mutex> lock(snippets_mutex); LockGuard lock(snippets_mutex);
if(snippets) { if(snippets) {
for(auto &snippet : *snippets) { for(auto &snippet : *snippets) {
if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) { if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) {

7
src/source_generic.h

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "autocomplete.h" #include "autocomplete.h"
#include "mutex.h"
#include "source.h" #include "source.h"
#include <atomic> #include <atomic>
@ -19,10 +20,10 @@ namespace Source {
std::pair<Gtk::TextIter, Gtk::TextIter> get_word(Gtk::TextIter iter); std::pair<Gtk::TextIter, Gtk::TextIter> get_word(Gtk::TextIter iter);
std::vector<std::pair<Gtk::TextIter, Gtk::TextIter>> get_words(const Gtk::TextIter &start, const Gtk::TextIter &end); std::vector<std::pair<Gtk::TextIter, Gtk::TextIter>> get_words(const Gtk::TextIter &start, const Gtk::TextIter &end);
std::mutex buffer_words_mutex; Mutex buffer_words_mutex ACQUIRED_AFTER(autocomplete.prefix_mutex);
std::map<std::string, size_t> buffer_words; std::map<std::string, size_t> 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(); void setup_buffer_words();
std::atomic<bool> show_prefix_buffer_word = {false}; /// To avoid showing the current word if it is unique in document
Autocomplete autocomplete; Autocomplete autocomplete;
std::vector<std::string> autocomplete_comment; std::vector<std::string> autocomplete_comment;

52
src/source_language_protocol.cc

@ -58,16 +58,17 @@ std::shared_ptr<LanguageProtocol::Client> LanguageProtocol::Client::get(const bo
auto cache_id = root_path.string() + '|' + language_id; auto cache_id = root_path.string() + '|' + language_id;
static std::unordered_map<std::string, std::weak_ptr<Client>> cache; static Mutex mutex;
static std::mutex mutex; static std::unordered_map<std::string, std::weak_ptr<Client>> cache GUARDED_BY(mutex);
std::lock_guard<std::mutex> lock(mutex);
LockGuard lock(mutex);
auto it = cache.find(cache_id); auto it = cache.find(cache_id);
if(it == cache.end()) if(it == cache.end())
it = cache.emplace(cache_id, std::weak_ptr<Client>()).first; it = cache.emplace(cache_id, std::weak_ptr<Client>()).first;
auto instance = it->second.lock(); auto instance = it->second.lock();
if(!instance) if(!instance)
it->second = instance = std::shared_ptr<Client>(new Client(root_path, language_id), [](Client *client_ptr) { it->second = instance = std::shared_ptr<Client>(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 client_ptr;
}); });
delete_thread.detach(); delete_thread.detach();
@ -84,7 +85,7 @@ LanguageProtocol::Client::~Client() {
}); });
result_processed.get_future().get(); result_processed.get_future().get();
std::lock_guard<std::mutex> lock(timeout_threads_mutex); LockGuard lock(timeout_threads_mutex);
for(auto &thread : timeout_threads) for(auto &thread : timeout_threads)
thread.join(); thread.join();
@ -102,17 +103,22 @@ LanguageProtocol::Client::~Client() {
LanguageProtocol::Capabilities LanguageProtocol::Client::initialize(Source::LanguageProtocolView *view) { LanguageProtocol::Capabilities LanguageProtocol::Client::initialize(Source::LanguageProtocolView *view) {
if(view) { if(view) {
std::lock_guard<std::mutex> lock(views_mutex); LockGuard lock(views_mutex);
views.emplace(view); views.emplace(view);
} }
std::lock_guard<std::mutex> lock(initialize_mutex); LockGuard lock(initialize_mutex);
if(initialized) if(initialized)
return capabilities; return capabilities;
std::promise<void> result_processed; std::promise<void> 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) { if(!error) {
auto capabilities_pt = result.find("capabilities"); auto capabilities_pt = result.find("capabilities");
if(capabilities_pt != result.not_found()) { if(capabilities_pt != result.not_found()) {
@ -150,12 +156,12 @@ LanguageProtocol::Capabilities LanguageProtocol::Client::initialize(Source::Lang
void LanguageProtocol::Client::close(Source::LanguageProtocolView *view) { void LanguageProtocol::Client::close(Source::LanguageProtocolView *view) {
{ {
std::lock_guard<std::mutex> lock(views_mutex); LockGuard lock(views_mutex);
auto it = views.find(view); auto it = views.find(view);
if(it != views.end()) if(it != views.end())
views.erase(it); views.erase(it);
} }
std::lock_guard<std::mutex> lock(read_write_mutex); LockGuard lock(read_write_mutex);
for(auto it = handlers.begin(); it != handlers.end();) { for(auto it = handlers.begin(); it != handlers.end();) {
if(it->second.first == view) if(it->second.first == view)
it = handlers.erase(it); it = handlers.erase(it);
@ -217,7 +223,7 @@ void LanguageProtocol::Client::parse_server_message() {
auto result_it = pt.find("result"); auto result_it = pt.find("result");
auto error_it = pt.find("error"); auto error_it = pt.find("error");
{ {
std::unique_lock<std::mutex> lock(read_write_mutex); LockGuard lock(read_write_mutex);
if(result_it != pt.not_found()) { if(result_it != pt.not_found()) {
if(message_id) { if(message_id) {
auto id_it = handlers.find(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 &params, std::function<void(const boost::property_tree::ptree &, bool error)> &&function) { void LanguageProtocol::Client::write_request(Source::LanguageProtocolView *view, const std::string &method, const std::string &params, std::function<void(const boost::property_tree::ptree &, bool error)> &&function) {
std::unique_lock<std::mutex> lock(read_write_mutex); LockGuard lock(read_write_mutex);
if(function) { if(function) {
handlers.emplace(message_id, std::make_pair(view, std::move(function))); handlers.emplace(message_id, std::make_pair(view, std::move(function)));
auto message_id = this->message_id; auto message_id = this->message_id;
std::lock_guard<std::mutex> lock(timeout_threads_mutex); LockGuard lock(timeout_threads_mutex);
timeout_threads.emplace_back([this, message_id] { timeout_threads.emplace_back([this, message_id] {
for(size_t c = 0; c < 20; ++c) { for(size_t c = 0; c < 20; ++c) {
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::lock_guard<std::mutex> lock(read_write_mutex); LockGuard lock(read_write_mutex);
auto id_it = handlers.find(message_id); auto id_it = handlers.find(message_id);
if(id_it == handlers.end()) if(id_it == handlers.end())
return; return;
} }
std::unique_lock<std::mutex> lock(read_write_mutex); LockGuard lock(read_write_mutex);
auto id_it = handlers.find(message_id); auto id_it = handlers.find(message_id);
if(id_it != handlers.end()) { 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); 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 &params) { void LanguageProtocol::Client::write_notification(const std::string &method, const std::string &params) {
std::lock_guard<std::mutex> lock(read_write_mutex); LockGuard lock(read_write_mutex);
std::string content(R"({"jsonrpc":"2.0","method":")" + method + R"(","params":{)" + params + "}}"); 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; auto message = "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n" + content;
if(Config::get().log.language_server) if(Config::get().log.language_server)
@ -337,7 +343,7 @@ void LanguageProtocol::Client::handle_server_request(const std::string &method,
catch(...) { catch(...) {
} }
} }
std::lock_guard<std::mutex> lock(views_mutex); LockGuard lock(views_mutex);
for(auto view : views) { for(auto view : views) {
if(file == view->file_path) { if(file == view->file_path) {
view->update_diagnostics(std::move(diagnostics)); view->update_diagnostics(std::move(diagnostics));
@ -1267,7 +1273,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
std::smatch sm; std::smatch sm;
if(std::regex_match(line, sm, regex)) { if(std::regex_match(line, sm, regex)) {
{ {
std::lock_guard<std::mutex> 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(); autocomplete.prefix = sm.length(2) ? sm[3].str() : sm.length(4) ? sm[5].str() : sm[6].str();
if(!sm.length(2) && !sm.length(4)) if(!sm.length(2) && !sm.length(4))
autocomplete_enable_snippets = true; autocomplete_enable_snippets = true;
@ -1276,7 +1282,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
} }
else if(is_possible_argument()) { else if(is_possible_argument()) {
autocomplete_show_parameters = true; autocomplete_show_parameters = true;
std::lock_guard<std::mutex> lock(autocomplete.prefix_mutex); LockGuard lock(autocomplete.prefix_mutex);
autocomplete.prefix = ""; autocomplete.prefix = "";
return true; return true;
} }
@ -1289,7 +1295,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
iter.forward_char(); iter.forward_char();
{ {
std::lock_guard<std::mutex> lock(autocomplete.prefix_mutex); LockGuard lock(autocomplete.prefix_mutex);
autocomplete.prefix = get_buffer()->get_text(iter, end_iter); autocomplete.prefix = get_buffer()->get_text(iter, end_iter);
} }
auto prev1 = iter; auto prev1 = iter;
@ -1397,7 +1403,7 @@ void Source::LanguageProtocolView::setup_autocomplete() {
if(!label.empty()) { if(!label.empty()) {
std::string prefix; std::string prefix;
{ {
std::lock_guard<std::mutex> lock(autocomplete.prefix_mutex); LockGuard lock(autocomplete.prefix_mutex);
prefix = autocomplete.prefix; prefix = autocomplete.prefix;
} }
if(prefix.compare(0, prefix.size(), label, 0, prefix.size()) == 0) { if(prefix.compare(0, prefix.size(), label, 0, prefix.size()) == 0) {
@ -1416,10 +1422,10 @@ void Source::LanguageProtocolView::setup_autocomplete() {
if(autocomplete_enable_snippets) { if(autocomplete_enable_snippets) {
std::string prefix; std::string prefix;
{ {
std::lock_guard<std::mutex> lock(autocomplete.prefix_mutex); LockGuard lock(autocomplete.prefix_mutex);
prefix = autocomplete.prefix; prefix = autocomplete.prefix;
} }
std::lock_guard<std::mutex> lock(snippets_mutex); LockGuard lock(snippets_mutex);
if(snippets) { if(snippets) {
for(auto &snippet : *snippets) { for(auto &snippet : *snippets) {
if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) { if(prefix.compare(0, prefix.size(), snippet.prefix, 0, prefix.size()) == 0) {

23
src/source_language_protocol.h

@ -1,12 +1,12 @@
#pragma once #pragma once
#include "autocomplete.h" #include "autocomplete.h"
#include "mutex.h"
#include "process.hpp" #include "process.hpp"
#include "source.h" #include "source.h"
#include <atomic> #include <atomic>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <list> #include <list>
#include <map> #include <map>
#include <mutex>
#include <set> #include <set>
#include <sstream> #include <sstream>
@ -105,31 +105,32 @@ namespace LanguageProtocol {
Capabilities capabilities; Capabilities capabilities;
std::set<Source::LanguageProtocolView *> views; Mutex views_mutex;
std::mutex views_mutex; std::set<Source::LanguageProtocolView *> views GUARDED_BY(views_mutex);
std::mutex initialize_mutex; Mutex initialize_mutex;
bool initialized GUARDED_BY(initialize_mutex) = false;
std::unique_ptr<TinyProcessLib::Process> process; Mutex read_write_mutex;
std::mutex read_write_mutex; std::unique_ptr<TinyProcessLib::Process> process GUARDED_BY(read_write_mutex);
std::stringstream server_message_stream; std::stringstream server_message_stream;
size_t server_message_size = static_cast<size_t>(-1); size_t server_message_size = static_cast<size_t>(-1);
size_t server_message_content_pos; size_t server_message_content_pos;
bool header_read = false; bool header_read = false;
size_t message_id = 1; size_t message_id GUARDED_BY(read_write_mutex) = 1;
std::map<size_t, std::pair<Source::LanguageProtocolView *, std::function<void(const boost::property_tree::ptree &, bool error)>>> handlers; std::map<size_t, std::pair<Source::LanguageProtocolView *, std::function<void(const boost::property_tree::ptree &, bool error)>>> handlers GUARDED_BY(read_write_mutex);
std::vector<std::thread> timeout_threads;
std::mutex timeout_threads_mutex; Mutex timeout_threads_mutex;
std::vector<std::thread> timeout_threads GUARDED_BY(timeout_threads_mutex);
public: public:
static std::shared_ptr<Client> get(const boost::filesystem::path &file_path, const std::string &language_id); static std::shared_ptr<Client> get(const boost::filesystem::path &file_path, const std::string &language_id);
~Client(); ~Client();
bool initialized = false;
Capabilities initialize(Source::LanguageProtocolView *view); Capabilities initialize(Source::LanguageProtocolView *view);
void close(Source::LanguageProtocolView *view); void close(Source::LanguageProtocolView *view);

16
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<void(int exit_status)> &callback, bool quiet) { void Terminal::async_process(const std::string &command, const boost::filesystem::path &path, const std::function<void(int exit_status)> &callback, bool quiet) {
std::thread async_execute_thread([this, command, path, callback, quiet]() { std::thread async_execute_thread([this, command, path, callback, quiet]() {
std::unique_lock<std::mutex> processes_lock(processes_mutex); LockGuard lock(processes_mutex);
stdin_buffer.clear(); stdin_buffer.clear();
auto process = std::make_shared<TinyProcessLib::Process>(command, path.string(), [this, quiet](const char *bytes, size_t n) { auto process = std::make_shared<TinyProcessLib::Process>(command, path.string(), [this, quiet](const char *bytes, size_t n) {
if(!quiet) if(!quiet)
@ -90,7 +90,7 @@ void Terminal::async_process(const std::string &command, const boost::filesystem
}, true); }, true);
auto pid = process->get_id(); auto pid = process->get_id();
if(pid <= 0) { if(pid <= 0) {
processes_lock.unlock(); lock.unlock();
async_print("Error: failed to run command: " + command + "\n", true); async_print("Error: failed to run command: " + command + "\n", true);
if(callback) if(callback)
callback(-1); callback(-1);
@ -98,12 +98,12 @@ void Terminal::async_process(const std::string &command, const boost::filesystem
} }
else { else {
processes.emplace_back(process); processes.emplace_back(process);
processes_lock.unlock(); lock.unlock();
} }
auto exit_status = process->get_exit_status(); auto exit_status = process->get_exit_status();
processes_lock.lock(); lock.lock();
for(auto it = processes.begin(); it != processes.end(); it++) { for(auto it = processes.begin(); it != processes.end(); it++) {
if((*it)->get_id() == pid) { if((*it)->get_id() == pid) {
processes.erase(it); processes.erase(it);
@ -111,7 +111,7 @@ void Terminal::async_process(const std::string &command, const boost::filesystem
} }
} }
stdin_buffer.clear(); stdin_buffer.clear();
processes_lock.unlock(); lock.unlock();
if(callback) if(callback)
callback(exit_status); 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) { void Terminal::kill_last_async_process(bool force) {
std::lock_guard<std::mutex> lock(processes_mutex); LockGuard lock(processes_mutex);
if(processes.empty()) if(processes.empty())
Info::get().print("No running processes"); Info::get().print("No running processes");
else else
@ -128,7 +128,7 @@ void Terminal::kill_last_async_process(bool force) {
} }
void Terminal::kill_async_processes(bool force) { void Terminal::kill_async_processes(bool force) {
std::lock_guard<std::mutex> lock(processes_mutex); LockGuard lock(processes_mutex);
for(auto &process : processes) for(auto &process : processes)
process->kill(force); process->kill(force);
} }
@ -393,7 +393,7 @@ bool Terminal::on_button_press_event(GdkEventButton *button_event) {
} }
bool Terminal::on_key_press_event(GdkEventKey *event) { bool Terminal::on_key_press_event(GdkEventKey *event) {
std::lock_guard<std::mutex> lock(processes_mutex); LockGuard lock(processes_mutex);
bool debug_is_running = false; bool debug_is_running = false;
#ifdef JUCI_ENABLE_DEBUG #ifdef JUCI_ENABLE_DEBUG
debug_is_running = Project::current ? Project::current->debug_is_running() : false; debug_is_running = Project::current ? Project::current->debug_is_running() : false;

6
src/terminal.h

@ -1,11 +1,11 @@
#pragma once #pragma once
#include "dispatcher.h" #include "dispatcher.h"
#include "gtkmm.h" #include "gtkmm.h"
#include "mutex.h"
#include "process.hpp" #include "process.hpp"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <mutex>
#include <tuple> #include <tuple>
class Terminal : public Gtk::TextView { class Terminal : public Gtk::TextView {
@ -47,7 +47,7 @@ private:
std::tuple<size_t, size_t, std::string, std::string, std::string> find_link(const std::string &line); std::tuple<size_t, size_t, std::string, std::string, std::string> find_link(const std::string &line);
void apply_link_tags(const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter); void apply_link_tags(const Gtk::TextIter &start_iter, const Gtk::TextIter &end_iter);
std::vector<std::shared_ptr<TinyProcessLib::Process>> processes; Mutex processes_mutex;
std::mutex processes_mutex; std::vector<std::shared_ptr<TinyProcessLib::Process>> processes GUARDED_BY(processes_mutex);
Glib::ustring stdin_buffer; Glib::ustring stdin_buffer;
}; };

43
src/usages_clang.cc

@ -3,6 +3,7 @@
#include "config.h" #include "config.h"
#include "dialogs.h" #include "dialogs.h"
#include "filesystem.h" #include "filesystem.h"
#include "utility.h"
#include <chrono> #include <chrono>
#include <fstream> #include <fstream>
#include <regex> #include <regex>
@ -22,7 +23,7 @@ pid_t get_current_process_id() {
const boost::filesystem::path Usages::Clang::cache_folder = ".usages_clang"; const boost::filesystem::path Usages::Clang::cache_folder = ".usages_clang";
std::map<boost::filesystem::path, Usages::Clang::Cache> Usages::Clang::caches; std::map<boost::filesystem::path, Usages::Clang::Cache> Usages::Clang::caches;
std::mutex Usages::Clang::caches_mutex; Mutex Usages::Clang::caches_mutex;
std::atomic<size_t> Usages::Clang::cache_in_progress_count(0); std::atomic<size_t> Usages::Clang::cache_in_progress_count(0);
bool Usages::Clang::Cache::Cursor::operator==(const Cursor &o) { bool Usages::Clang::Cache::Cursor::operator==(const Cursor &o) {
@ -170,7 +171,7 @@ std::vector<Usages::Clang::Usages> Usages::Clang::get_usages(const boost::filesy
// Use cache // Use cache
for(auto it = potential_paths.begin(); it != potential_paths.end();) { for(auto it = potential_paths.begin(); it != potential_paths.end();) {
std::lock_guard<std::mutex> lock(caches_mutex); LockGuard lock(caches_mutex);
auto caches_it = caches.find(*it); auto caches_it = caches.find(*it);
// Load cache from file if not found in memory and if cache file exists // Load cache from file if not found in memory and if cache file exists
@ -221,21 +222,14 @@ std::vector<Usages::Clang::Usages> Usages::Clang::get_usages(const boost::filesy
while(true) { while(true) {
boost::filesystem::path path; boost::filesystem::path path;
{ {
static std::mutex mutex; static Mutex mutex;
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
if(it == potential_paths.end()) if(it == potential_paths.end())
return; return;
path = *it; path = *it;
++it; ++it;
} }
// {
// static std::mutex mutex;
// std::lock_guard<std::mutex> 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::ifstream stream(path.string(), std::ifstream::binary);
std::string buffer; std::string buffer;
buffer.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>()); buffer.assign(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
@ -256,8 +250,8 @@ std::vector<Usages::Clang::Usages> Usages::Clang::get_usages(const boost::filesy
clangmm::TranslationUnit translation_unit(std::make_shared<clangmm::Index>(0, 0), path.string(), arguments, &buffer, flags); clangmm::TranslationUnit translation_unit(std::make_shared<clangmm::Index>(0, 0), path.string(), arguments, &buffer, flags);
{ {
static std::mutex mutex; static Mutex mutex;
std::lock_guard<std::mutex> lock(mutex); LockGuard lock(mutex);
add_usages(project_path, build_path, path, usages, visited, spelling, cursor, &translation_unit, true); 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); add_usages_from_includes(project_path, build_path, usages, visited, spelling, cursor, &translation_unit, true);
} }
@ -279,22 +273,13 @@ std::vector<Usages::Clang::Usages> 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, 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) { std::time_t before_parse_time, const PathSet &project_paths_in_use, clangmm::TranslationUnit *translation_unit, clangmm::Tokens *tokens) {
class ScopeExit { ScopeGuard guard{[] { --cache_in_progress_count; }};
public:
std::function<void()> f;
~ScopeExit() {
f();
}
};
ScopeExit scope_exit{[] {
--cache_in_progress_count;
}};
if(project_path.empty()) if(project_path.empty())
return; return;
{ {
std::lock_guard<std::mutex> lock(caches_mutex); LockGuard lock(caches_mutex);
if(project_paths_in_use.count(project_path)) { if(project_paths_in_use.count(project_path)) {
caches.erase(path); caches.erase(path);
caches.emplace(path, Cache(project_path, build_path, path, before_parse_time, translation_unit, tokens)); 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<boost::uintmax_t>(-1) || ec) if(file_size == static_cast<boost::uintmax_t>(-1) || ec)
continue; continue;
auto tokens = translation_unit->get_tokens(path.string(), 0, file_size - 1); auto tokens = translation_unit->get_tokens(path.string(), 0, file_size - 1);
std::lock_guard<std::mutex> lock(caches_mutex); LockGuard lock(caches_mutex);
if(project_paths_in_use.count(project_path)) { if(project_paths_in_use.count(project_path)) {
caches.erase(path); caches.erase(path);
caches.emplace(path, Cache(project_path, build_path, path, before_parse_time, translation_unit, tokens.get())); 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) { void Usages::Clang::erase_unused_caches(const PathSet &project_paths_in_use) {
std::lock_guard<std::mutex> lock(caches_mutex); LockGuard lock(caches_mutex);
for(auto it = caches.begin(); it != caches.end();) { for(auto it = caches.begin(); it != caches.end();) {
bool found = false; bool found = false;
for(auto &project_path : project_paths_in_use) { 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) { void Usages::Clang::erase_cache(const boost::filesystem::path &path) {
std::lock_guard<std::mutex> lock(caches_mutex); LockGuard lock(caches_mutex);
auto it = caches.find(path); auto it = caches.find(path);
if(it == caches.end()) 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) if(cache_in_progress_count != 0)
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock(caches_mutex); LockGuard lock(caches_mutex);
boost::system::error_code ec; boost::system::error_code ec;
auto usages_clang_path = build_path / cache_folder; auto usages_clang_path = build_path / cache_folder;
if(boost::filesystem::exists(usages_clang_path, ec) && boost::filesystem::is_directory(usages_clang_path, ec)) { 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)) { if(store_in_cache && filesystem::file_in_path(path, project_path)) {
std::lock_guard<std::mutex> lock(caches_mutex); LockGuard lock(caches_mutex);
caches.erase(path); caches.erase(path);
caches.emplace(path, Cache(project_path, build_path, path, before_parse_time, translation_unit, tokens.get())); caches.emplace(path, Cache(project_path, build_path, path, before_parse_time, translation_unit, tokens.get()));
} }

10
src/usages_clang.h

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "clangmm.h" #include "clangmm.h"
#include "mutex.h"
#include <atomic> #include <atomic>
#include <boost/archive/text_iarchive.hpp> #include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp> #include <boost/archive/text_oarchive.hpp>
@ -8,7 +9,6 @@
#include <boost/serialization/unordered_set.hpp> #include <boost/serialization/unordered_set.hpp>
#include <boost/serialization/vector.hpp> #include <boost/serialization/vector.hpp>
#include <map> #include <map>
#include <mutex>
#include <regex> #include <regex>
#include <set> #include <set>
#include <unordered_set> #include <unordered_set>
@ -102,8 +102,8 @@ namespace Usages {
private: private:
const static boost::filesystem::path cache_folder; const static boost::filesystem::path cache_folder;
static std::map<boost::filesystem::path, Cache> caches; static Mutex caches_mutex;
static std::mutex caches_mutex; static std::map<boost::filesystem::path, Cache> caches GUARDED_BY(caches_mutex);
static std::atomic<size_t> cache_in_progress_count; static std::atomic<size_t> cache_in_progress_count;
@ -142,7 +142,7 @@ namespace Usages {
static std::pair<Clang::PathSet, Clang::PathSet> find_potential_paths(const PathSet &paths, const boost::filesystem::path &project_path, static std::pair<Clang::PathSet, Clang::PathSet> find_potential_paths(const PathSet &paths, const boost::filesystem::path &project_path,
const std::map<boost::filesystem::path, PathSet> &paths_includes, const PathSet &paths_with_spelling); const std::map<boost::filesystem::path, PathSet> &paths_includes, const PathSet &paths_with_spelling);
static void write_cache(const boost::filesystem::path &path, const Cache &cache); 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); 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 } // namespace Usages

4
tests/CMakeLists.txt

@ -1,4 +1,8 @@
add_compile_options(-fno-access-control) 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}") add_definitions(-DJUCI_BUILD_PATH="${CMAKE_BINARY_DIR}" -DJUCI_TESTS_PATH="${CMAKE_CURRENT_SOURCE_DIR}")
include_directories( include_directories(

2
tests/lldb_test.cc

@ -85,7 +85,7 @@ int main() {
exited = true; exited = true;
}); });
Debug::LLDB::get().on_event.emplace_back([&](const lldb::SBEvent &event) { Debug::LLDB::get().on_event.emplace_back([&](const lldb::SBEvent &event) {
std::unique_lock<std::mutex> lock(Debug::LLDB::get().mutex); LockGuard lock(Debug::LLDB::get().mutex);
auto process = lldb::SBProcess::GetProcessFromEvent(event); auto process = lldb::SBProcess::GetProcessFromEvent(event);
auto state = lldb::SBProcess::GetStateFromEvent(event); auto state = lldb::SBProcess::GetStateFromEvent(event);
if(state == lldb::StateType::eStateStopped) { if(state == lldb::StateType::eStateStopped) {

Loading…
Cancel
Save