commit 99301cec42290cd26b9757c17eaaab5d70781bf1 from: Stefan Sperling date: Mon Jul 24 12:27:18 2023 UTC load tog's worktree base commit marker in the log thread for startup speed Walking the whole file index can take some time. Avoid delaying the perceived start-up time of tog by reading the file index in the background log thread. Problem pointed out by op@ with fixes from + ok jamsek commit - ccdddf69864eb9ac5c10c0f5d58b68df1cf26ea1 commit + 99301cec42290cd26b9757c17eaaab5d70781bf1 blob - 07cb22fd98f7b10c110a8a5d0c0627770c5b7e76 blob + 02e203f9626eba60a84658a1f9e7a8d0fb043418 --- include/got_worktree.h +++ include/got_worktree.h @@ -137,13 +137,14 @@ const struct got_error *got_worktree_set_base_commit_i * Get the state of the work tree. If the work tree's global base commit is * the tip of the work tree's current branch, and each file in the index is * based on this same commit, the char out parameter will be - * GOT_WORKTREE_UPTODATE, else it will be GOT_WORKTREE_OUTOFDATE. + * GOT_WORKTREE_STATE_UPTODATE, else it will be GOT_WORKTREE_STATE_OUTOFDATE. */ const struct got_error *got_worktree_get_state(char *, struct got_repository *, struct got_worktree *); -#define GOT_WORKTREE_UPTODATE '*' -#define GOT_WORKTREE_OUTOFDATE '~' +#define GOT_WORKTREE_STATE_UNKNOWN ' ' +#define GOT_WORKTREE_STATE_UPTODATE '*' +#define GOT_WORKTREE_STATE_OUTOFDATE '~' /* * Obtain a parsed representation of this worktree's got.conf file. blob - 6b14bc178bf5fbc6065088656b0ecae4a8fd2aad blob + e0c7318204bffd754947e3848dcb9e7b6ad3ae9e --- lib/worktree.c +++ lib/worktree.c @@ -3273,7 +3273,7 @@ got_worktree_get_state(char *state, struct got_reposit if (err) goto done; - *state = GOT_WORKTREE_OUTOFDATE; + *state = GOT_WORKTREE_STATE_UNKNOWN; base_id = got_worktree_get_base_commit_id(worktree); if (got_object_id_cmp(base_id, head_id) == 0) { @@ -3284,10 +3284,13 @@ got_worktree_get_state(char *state, struct got_reposit err = got_fileindex_for_each_entry_safe(fileindex, check_mixed_commits, worktree); if (err == NULL) - *state = GOT_WORKTREE_UPTODATE; - else if (err->code == GOT_ERR_MIXED_COMMITS) + *state = GOT_WORKTREE_STATE_UPTODATE; + else if (err->code == GOT_ERR_MIXED_COMMITS) { + *state = GOT_WORKTREE_STATE_OUTOFDATE; err = NULL; - } + } + } else + *state = GOT_WORKTREE_STATE_OUTOFDATE; done: free(head_id); blob - d10c8e4e269c29522a88fa8b7f94b5658cacd469 blob + a7c92801a63e2df3de71afd808cde146986b7efc --- regress/tog/log.sh +++ regress/tog/log.sh @@ -397,6 +397,7 @@ test_log_commit_keywords() done cat <<-EOF >$TOG_TEST_SCRIPT + WAIT_FOR_UI wait for log thread to finish SCREENDUMP EOF @@ -526,6 +527,7 @@ test_log_show_base_commit() # check up-to-date base commit marker prefixes base commit log message cat <<-EOF >$TOG_TEST_SCRIPT + WAIT_FOR_UI wait for log thread to finish SCREENDUMP EOF @@ -566,6 +568,7 @@ test_log_show_base_commit() head_id=$(git_show_head "$repo") cat <<-EOF >$TOG_TEST_SCRIPT + WAIT_FOR_UI wait for log thread to finish SCREENDUMP EOF blob - 897b0c14098c0b8440dbdfbedd2bb8c0792e6efb blob + 1bd75430ffde37cc2e0c9ceb60677641c9813dc3 --- tog/tog.c +++ tog/tog.c @@ -370,6 +370,7 @@ struct tog_log_thread_args { struct got_repository *repo; int *pack_fds; int log_complete; + pthread_cond_t log_loaded; sig_atomic_t *quit; struct commit_queue_entry **first_displayed_entry; struct commit_queue_entry **selected_entry; @@ -380,6 +381,8 @@ struct tog_log_thread_args { int limit_match; regex_t *limit_regex; struct commit_queue *limit_commits; + struct got_worktree *worktree; + int need_commit_marker; }; struct tog_log_view_state { @@ -730,7 +733,7 @@ static const struct got_error *search_next_view_match( static const struct got_error *open_log_view(struct tog_view *, struct got_object_id *, struct got_repository *, - const char *, const char *, int); + const char *, const char *, int, struct got_worktree *); static const struct got_error * show_log_view(struct tog_view *); static const struct got_error *input_log_view(struct tog_view **, struct tog_view *, int); @@ -2423,6 +2426,10 @@ draw_commit(struct tog_view *view, struct commit_queue struct tog_color *tc; struct got_reflist_head *refs; + if (tog_base_commit.id != NULL && tog_base_commit.idx == -1 && + got_object_id_cmp(id, tog_base_commit.id) == 0) + tog_base_commit.idx = entry->idx; + committer_time = got_object_commit_get_committer_time(commit); if (gmtime_r(&committer_time, &tm) == NULL) return got_error_from_errno("gmtime_r"); @@ -2482,7 +2489,7 @@ draw_commit(struct tog_view *view, struct commit_queue waddwstr(view->window, wauthor); col += author_width; while (col < avail && author_width < author_display_cols + 2) { - if (tog_base_commit.id != NULL && + if (tog_base_commit.marker != GOT_WORKTREE_STATE_UNKNOWN && author_width == marker_column && entry->idx == tog_base_commit.idx) { tc = get_color(&s->colors, TOG_COLOR_COMMIT); @@ -2709,10 +2716,6 @@ queue_commits(struct tog_log_thread_args *a) TAILQ_INSERT_TAIL(&a->real_commits->head, entry, entry); a->real_commits->ncommits++; - if (tog_base_commit.id != NULL && tog_base_commit.idx == -1 && - got_object_id_cmp(&id, tog_base_commit.id) == 0) - tog_base_commit.idx = entry->idx; - if (*a->limiting) { err = match_commit(&limit_match, &id, commit, a->limit_regex); @@ -3327,6 +3330,7 @@ log_thread(void *arg) goto done; err = NULL; done = 1; + a->commits_needed = 0; } else if (a->commits_needed > 0 && !a->load_all) { if (*a->limiting) { if (a->limit_match) @@ -3360,10 +3364,49 @@ log_thread(void *arg) goto done; } + if (a->commits_needed == 0 && + a->need_commit_marker && a->worktree) { + errcode = pthread_mutex_unlock(&tog_mutex); + if (errcode) { + err = got_error_set_errno(errcode, + "pthread_mutex_unlock"); + goto done; + } + err = got_worktree_get_state(&tog_base_commit.marker, + a->repo, a->worktree); + if (err) + goto done; + errcode = pthread_mutex_lock(&tog_mutex); + if (errcode) { + err = got_error_set_errno(errcode, + "pthread_mutex_lock"); + goto done; + } + a->need_commit_marker = 0; + /* + * The main thread did not close this + * work tree yet. Close it now. + */ + got_worktree_close(a->worktree); + a->worktree = NULL; + + if (*a->quit) + done = 1; + } + if (done) a->commits_needed = 0; else { if (a->commits_needed == 0 && !a->load_all) { + if (tog_io.wait_for_ui) { + errcode = pthread_cond_signal( + &a->log_loaded); + if (errcode && err == NULL) + err = got_error_set_errno( + errcode, + "pthread_cond_signal"); + } + errcode = pthread_cond_wait(&a->need_commits, &tog_mutex); if (errcode) { @@ -3378,6 +3421,13 @@ log_thread(void *arg) } } a->log_complete = 1; + if (tog_io.wait_for_ui) { + errcode = pthread_cond_signal(&a->log_loaded); + if (errcode && err == NULL) + err = got_error_set_errno(errcode, + "pthread_cond_signal"); + } + errcode = pthread_mutex_unlock(&tog_mutex); if (errcode) err = got_error_set_errno(errcode, "pthread_mutex_unlock"); @@ -3385,6 +3435,10 @@ done: if (err) { tog_thread_error = 1; pthread_cond_signal(&a->commit_loaded); + if (a->worktree) { + got_worktree_close(a->worktree); + a->worktree = NULL; + } } return (void *)err; } @@ -3705,7 +3759,8 @@ search_next_log_view(struct tog_view *view) static const struct got_error * open_log_view(struct tog_view *view, struct got_object_id *start_id, struct got_repository *repo, const char *head_ref_name, - const char *in_repo_path, int log_branches) + const char *in_repo_path, int log_branches, + struct got_worktree *worktree) { const struct got_error *err = NULL; struct tog_log_view_state *s = &view->state.log; @@ -3803,6 +3858,14 @@ open_log_view(struct tog_view *view, struct got_object goto done; } + if (using_mock_io) { + int rc; + + rc = pthread_cond_init(&s->thread_args.log_loaded, NULL); + if (rc) + return got_error_set_errno(rc, "pthread_cond_init"); + } + s->thread_args.commits_needed = view->nlines; s->thread_args.graph = thread_graph; s->thread_args.real_commits = &s->real_commits; @@ -3820,6 +3883,9 @@ open_log_view(struct tog_view *view, struct got_object s->thread_args.limiting = &s->limit_view; s->thread_args.limit_regex = &s->limit_regex; s->thread_args.limit_commits = &s->limit_commits; + s->thread_args.worktree = worktree; + if (worktree) + s->thread_args.need_commit_marker = 1; done: if (err) { if (view->close == NULL) @@ -3844,6 +3910,16 @@ show_log_view(struct tog_view *view) err = trigger_log_thread(view, 1); if (err) return err; + if (tog_io.wait_for_ui) { + int rc; + + rc = pthread_cond_wait(&s->thread_args.log_loaded, + &tog_mutex); + if (rc) + return got_error_set_errno(rc, + "pthread_cond_wait"); + tog_io.wait_for_ui = 0; + } } } @@ -4393,10 +4469,7 @@ set_tog_base_commit(struct got_repository *repo, struc if (tog_base_commit.id == NULL) return got_error_from_errno( "got_object_id_dup"); - tog_base_commit.idx = -1; - - return got_worktree_get_state(&tog_base_commit.marker, repo, - worktree); + return NULL; } static const struct got_error * @@ -4551,18 +4624,20 @@ cmd_log(int argc, char *argv[]) error = got_error_from_errno("view_open"); goto done; } - error = open_log_view(view, start_id, repo, head_ref_name, - in_repo_path, log_branches); - if (error) - goto done; if (worktree) { error = set_tog_base_commit(repo, worktree); if (error != NULL) goto done; + } - /* Release work tree lock. */ - got_worktree_close(worktree); + error = open_log_view(view, start_id, repo, head_ref_name, + in_repo_path, log_branches, worktree); + if (error) + goto done; + + if (worktree) { + /* The work tree will be closed by the log thread. */ worktree = NULL; } @@ -6814,7 +6889,7 @@ log_annotated_line(struct tog_view **new_view, int beg if (log_view == NULL) return got_error_from_errno("view_open"); - err = open_log_view(log_view, id, repo, GOT_REF_HEAD, "", 0); + err = open_log_view(log_view, id, repo, GOT_REF_HEAD, "", 0, NULL); if (err) view_close(log_view); else @@ -7616,7 +7691,7 @@ log_selected_tree_entry(struct tog_view **new_view, in return err; err = open_log_view(log_view, s->commit_id, s->repo, s->head_ref_name, - path, 0); + path, 0, NULL); if (err) view_close(log_view); else @@ -8474,7 +8549,7 @@ log_ref_entry(struct tog_view **new_view, int begin_y, } err = open_log_view(log_view, commit_id, repo, - got_ref_get_name(re->ref), "", 0); + got_ref_get_name(re->ref), "", 0, NULL); done: if (err) view_close(log_view); @@ -10082,6 +10157,9 @@ main(int argc, char *argv[]) tog_diff_algo = GOT_DIFF_ALGORITHM_MYERS; } + tog_base_commit.idx = -1; + tog_base_commit.marker = GOT_WORKTREE_STATE_UNKNOWN; + if (cmd == NULL) { if (argc != 1) usage(0, 1);