commit 92845f0980b93eaf2ac1688899791f74af66a521 from: Stefan Sperling via: Thomas Adam date: Wed Jul 26 19:15:43 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 - 9f699a2ef4b0a3aae91766add71ad98770e72d94 commit + 92845f0980b93eaf2ac1688899791f74af66a521 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 - d0d12d1eae6757e0ccdad130e915914bdfe94838 blob + 6fa1241b8f765c16f2781e8b851c292d1f55a064 --- lib/worktree.c +++ lib/worktree.c @@ -3270,7 +3270,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) { @@ -3281,10 +3281,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 - 239bf9fe528a7c61926e9a65cdfe27dc99a9cc43 blob + 1f00b73ac81c1759f6989da29284af790fed4b74 --- tog/tog.c +++ tog/tog.c @@ -374,6 +374,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; @@ -384,6 +385,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 { @@ -734,7 +737,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); @@ -2427,6 +2430,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"); @@ -2486,7 +2493,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); @@ -2713,10 +2720,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); @@ -3331,6 +3334,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) @@ -3364,10 +3368,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) { @@ -3382,6 +3425,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"); @@ -3389,6 +3439,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; } @@ -3709,7 +3763,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; @@ -3807,6 +3862,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; @@ -3824,6 +3887,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) @@ -3848,6 +3914,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; + } } } @@ -4397,10 +4473,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 * @@ -4555,18 +4628,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; } @@ -6818,7 +6893,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 @@ -7620,7 +7695,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 @@ -8478,7 +8553,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); @@ -10086,6 +10161,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);