diff --git a/src/source_clang.cc b/src/source_clang.cc index b2f763b..8025e34 100644 --- a/src/source_clang.cc +++ b/src/source_clang.cc @@ -21,7 +21,7 @@ namespace sigc { clang::Index Source::ClangViewParse::clang_index(0, 0); Source::ClangViewParse::ClangViewParse(const boost::filesystem::path &file_path, const boost::filesystem::path& project_path, Glib::RefPtr language): -Source::View(file_path, project_path, language), parse_error(false) { +Source::View(file_path, project_path, language) { JDEBUG("start"); auto tag_table=get_buffer()->get_tag_table(); @@ -35,36 +35,35 @@ Source::View(file_path, project_path, language), parse_error(false) { parsing_in_progress=Singleton::terminal->print_in_progress("Parsing "+file_path.string()); //GTK-calls must happen in main thread, so the parse_thread //sends signals to the main thread that it is to call the following functions: - parse_start_connection=parse_start.connect([this]{ - if(parse_thread_buffer_mutex.try_lock()) { - parse_thread_buffer=get_buffer()->get_text(); - parse_thread_mapped=true; - parse_thread_buffer_mutex.unlock(); + parse_start_connection=parse_preprocess.connect([this]{ + auto expected=ParseProcessState::PREPROCESSING; + if(parse_mutex.try_lock()) { + if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PROCESSING)) + parse_thread_buffer=get_buffer()->get_text(); + parse_mutex.unlock(); } - parse_thread_go=true; + else + parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING); }); - parse_done_connection=parse_done.connect([this](){ - if(parse_thread_mapped) { - if(parsing_mutex.try_lock()) { + parse_done_connection=parse_postprocess.connect([this](){ + if(parse_mutex.try_lock()) { + auto expected=ParseProcessState::POSTPROCESSING; + if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::IDLE)) { update_syntax(); update_diagnostics(); parsed=true; set_status(""); - parsing_mutex.unlock(); } - parsing_in_progress->done("done"); - } - else { - parse_thread_go=true; + parse_mutex.unlock(); } }); - parse_fail_connection=parse_fail.connect([this](){ + parse_fail_connection=parse_error.connect([this](){ Singleton::terminal->print("Error: failed to reparse "+this->file_path.string()+".\n", true); set_status(""); set_info(""); parsing_in_progress->cancel("failed"); }); - init_parse(); + parse_initialize(); get_buffer()->signal_changed().connect([this]() { soft_reparse(); @@ -105,13 +104,14 @@ void Source::ClangViewParse::configure() { no_bracket_no_para_statement_regex=boost::regex("^([ \\t]*)(else|try|do) *$"); } -void Source::ClangViewParse::init_parse() { +void Source::ClangViewParse::parse_initialize() { type_tooltips.hide(); diagnostic_tooltips.hide(); parsed=false; - parse_thread_go=true; - parse_thread_mapped=false; - parse_thread_stop=false; + if(parse_thread.joinable()) + parse_thread.join(); + parse_process_state=ParseProcessState::STARTING; + parse_state=ParseState::WORKING; auto buffer=get_buffer()->get_text(); //Remove includes for first parse for initial syntax highlighting @@ -131,46 +131,53 @@ void Source::ClangViewParse::init_parse() { update_syntax(); set_status("parsing..."); - if(parse_thread.joinable()) - parse_thread.join(); parse_thread=std::thread([this]() { while(true) { - while(!parse_thread_go && !parse_thread_stop) + while(parse_state==ParseState::WORKING && (parse_process_state==ParseProcessState::IDLE || parse_process_state==ParseProcessState::PREPROCESSING || parse_process_state==ParseProcessState::POSTPROCESSING)) std::this_thread::sleep_for(std::chrono::milliseconds(10)); - if(parse_thread_stop) + if(parse_state!=ParseState::WORKING) break; - if(!parse_thread_mapped) { - parse_thread_go=false; - parse_start(); - } - else if (parse_thread_mapped && parsing_mutex.try_lock() && parse_thread_buffer_mutex.try_lock()) { - auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer.raw()); - if(status==0) - clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer.bytes()-1); - else - parse_error=true; - parse_thread_go=false; - parsing_mutex.unlock(); - parse_thread_buffer_mutex.unlock(); - if(status!=0) { - parse_fail(); - parse_thread_stop=true; + auto expected=ParseProcessState::STARTING; + if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::PREPROCESSING)) + parse_preprocess(); + else if (parse_process_state==ParseProcessState::PROCESSING && parse_mutex.try_lock()) { + if(parse_process_state==ParseProcessState::PROCESSING) { + auto status=clang_tu->ReparseTranslationUnit(parse_thread_buffer.raw()); + parsing_in_progress->done("done"); + if(status==0) { + auto expected=ParseProcessState::PROCESSING; + if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::POSTPROCESSING)) { + clang_tokens=clang_tu->get_tokens(0, parse_thread_buffer.bytes()-1); + parse_mutex.unlock(); + parse_postprocess(); + } + else + parse_mutex.unlock(); + } + else { + parse_state=ParseState::STOP; + parse_mutex.unlock(); + parse_error(); + } } else - parse_done(); + parse_mutex.unlock(); } } }); } void Source::ClangViewParse::soft_reparse() { - parse_thread_mapped=false; parsed=false; + if(parse_state!=ParseState::WORKING) + return; + parse_process_state=ParseProcessState::IDLE; delayed_reparse_connection.disconnect(); delayed_reparse_connection=Glib::signal_timeout().connect([this]() { parsed=false; - parse_thread_go=true; - set_status("parsing..."); + auto expected=ParseProcessState::IDLE; + if(parse_process_state.compare_exchange_strong(expected, ParseProcessState::STARTING)) + set_status("parsing..."); return false; }, 1000); } @@ -655,7 +662,7 @@ Source::ClangViewParse(file_path, project_path, language), autocomplete_cancel_s delete this; }); do_full_reparse.connect([this](){ - init_parse(); + parse_initialize(); full_reparse_running=false; }); } @@ -711,9 +718,8 @@ void Source::ClangViewAutocomplete::start_autocomplete() { } void Source::ClangViewAutocomplete::autocomplete() { - if(parse_thread_stop) { + if(parse_state!=ParseState::WORKING) return; - } if(!autocomplete_starting) { autocomplete_starting=true; @@ -815,14 +821,16 @@ void Source::ClangViewAutocomplete::autocomplete() { pos--; } autocomplete_thread=std::thread([this, ac_data, line_nr, column_nr, buffer](){ - parsing_mutex.lock(); - if(!parse_thread_stop) + parse_mutex.lock(); + if(parse_state==ParseState::WORKING) { + parse_process_state=ParseProcessState::IDLE; *ac_data=get_autocomplete_suggestions(buffer->raw(), line_nr, column_nr); - if(!parse_thread_stop) + } + if(parse_state==ParseState::WORKING) autocomplete_done(); else autocomplete_fail(); - parsing_mutex.unlock(); + parse_mutex.unlock(); }); } } @@ -831,7 +839,8 @@ std::vector Source::ClangViewAu std::vector suggestions; auto results=clang_tu->get_code_completions(buffer, line_number, column); if(results.cx_results==NULL) { - parse_thread_stop=true; + auto expected=ParseState::WORKING; + parse_state.compare_exchange_strong(expected, ParseState::RESTARTING); return suggestions; } @@ -863,7 +872,7 @@ std::vector Source::ClangViewAu void Source::ClangViewAutocomplete::async_delete() { parsing_in_progress->cancel("canceled, freeing resources in the background"); - parse_thread_stop=true; + parse_state=ParseState::STOP; delete_thread=std::thread([this](){ //TODO: Is it possible to stop the clang-process in progress? if(full_reparse_thread.joinable()) @@ -877,10 +886,15 @@ void Source::ClangViewAutocomplete::async_delete() { } bool Source::ClangViewAutocomplete::full_reparse() { - if(!full_reparse_running && !parse_error) { + if(!full_reparse_running) { + auto expected=ParseState::WORKING; + if(!parse_state.compare_exchange_strong(expected, ParseState::RESTARTING)) { + expected=ParseState::RESTARTING; + if(!parse_state.compare_exchange_strong(expected, ParseState::RESTARTING)) + return false; + } soft_reparse_needed=false; full_reparse_running=true; - parse_thread_stop=true; if(full_reparse_thread.joinable()) full_reparse_thread.join(); full_reparse_thread=std::thread([this](){ diff --git a/src/source_clang.h b/src/source_clang.h index 2e6a4d0..eb944c4 100644 --- a/src/source_clang.h +++ b/src/source_clang.h @@ -12,6 +12,9 @@ namespace Source { class ClangViewParse : public View { + protected: + enum class ParseProcessState {IDLE, STARTING, PREPROCESSING, PROCESSING, POSTPROCESSING}; + enum class ParseState {WORKING, RESTARTING, STOP}; public: class TokenRange { public: @@ -28,16 +31,12 @@ namespace Source { void soft_reparse() override; protected: - void init_parse(); + void parse_initialize(); std::unique_ptr clang_tu; - std::mutex parsing_mutex; std::unique_ptr clang_tokens; sigc::connection delayed_reparse_connection; std::shared_ptr parsing_in_progress; - std::thread parse_thread; - std::atomic parse_thread_stop; - std::atomic parse_error; void show_diagnostic_tooltips(const Gdk::Rectangle &rectangle) override; void show_type_tooltips(const Gdk::Rectangle &rectangle) override; @@ -49,24 +48,25 @@ namespace Source { std::set diagnostic_offsets; std::vector fix_its; + std::thread parse_thread; + std::mutex parse_mutex; + std::atomic parse_process_state; + std::atomic parse_state; sigc::connection parse_done_connection; sigc::connection parse_start_connection; sigc::connection parse_fail_connection; private: + Glib::Dispatcher parse_preprocess; + Glib::Dispatcher parse_postprocess; + Glib::Dispatcher parse_error; + Glib::ustring parse_thread_buffer; + void update_syntax(); std::set last_syntax_tags; void update_diagnostics(); static clang::Index clang_index; std::vector get_compilation_commands(); - - Glib::Dispatcher parse_done; - Glib::Dispatcher parse_start; - Glib::Dispatcher parse_fail; - Glib::ustring parse_thread_buffer; - std::mutex parse_thread_buffer_mutex; - std::atomic parse_thread_go; - std::atomic parse_thread_mapped; }; class ClangViewAutocomplete : public ClangViewParse {