Commit Diff


commit - f41a30edc6561e691bd077f02975a523447bfa50
commit + 1a76625ff1fb0585ad07bacb1a753bf01939f910
blob - 8a462cdf7347b01860055e86ac11d6b3957a0b22
blob + 83ea51ae92d4b1a3559fb427fa0a9c5f519c27be
--- include/got_error.h
+++ include/got_error.h
@@ -125,6 +125,13 @@ const struct got_error *got_error(int);
 const struct got_error *got_error_from_errno(void);
 
 /*
+ * Set errno to the specified error code and return a statically
+ * allocated error object with code GOT_ERR_ERRNO and an error
+ * message obtained from strerror(3).
+ */
+const struct got_error *got_error_set_errno(int);
+
+/*
  * If ferror(3) indicates an error status for the FILE, obtain an error
  * from got_error_from_errno(). Else, obtain the error via got_error()
  * with the error code provided in the second argument.
blob - 4f7f4abd51fe6798c3e75e0811f233c7347bed5e
blob + b12d036c856b9a3528f9e4e08295d75ed88c1186
--- lib/error.c
+++ lib/error.c
@@ -49,6 +49,13 @@ got_error_from_errno()
 }
 
 const struct got_error *
+got_error_set_errno(int code)
+{
+	errno = code;
+	return got_error_from_errno();
+}
+
+const struct got_error *
 got_ferror(FILE *f, int code)
 {
 	if (ferror(f))
blob - 2d26c2ee57e5dd3a42e6f75cd713c7700171af50
blob + 26290c76cbb8f92d44f52d221da870a2de4c6313
--- tog/tog.c
+++ tog/tog.c
@@ -104,6 +104,7 @@ struct commit_queue_entry {
 	TAILQ_ENTRY(commit_queue_entry) entry;
 	struct got_object_id *id;
 	struct got_commit_object *commit;
+	int idx;
 };
 TAILQ_HEAD(commit_queue_head, commit_queue_entry);
 struct commit_queue {
@@ -111,8 +112,26 @@ struct commit_queue {
 	struct commit_queue_head head;
 };
 
-struct tog_log_view_state {
+pthread_mutex_t tog_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct tog_log_thread_args {
+	pthread_cond_t need_commits;
+	int commits_needed;
 	struct got_commit_graph *graph;
+	struct commit_queue *commits;
+	const char *in_repo_path;
+	struct got_object_id *start_id;
+	struct got_repository *repo;
+	int log_complete;
+	sig_atomic_t *quit;
+	struct tog_view *view;
+	struct commit_queue_entry **first_displayed_entry;
+	struct commit_queue_entry **last_displayed_entry;
+	struct commit_queue_entry **selected_entry;
+	int *selected;
+};
+
+struct tog_log_view_state {
 	struct commit_queue commits;
 	struct commit_queue_entry *first_displayed_entry;
 	struct commit_queue_entry *last_displayed_entry;
@@ -121,10 +140,12 @@ struct tog_log_view_state {
 	char *in_repo_path;
 	struct got_repository *repo;
 	struct got_object_id *start_id;
+	sig_atomic_t quit;
+	pthread_t thread;
+	struct tog_log_thread_args thread_args;
 };
 
 struct tog_blame_cb_args {
-	pthread_mutex_t *mutex;
 	struct tog_blame_line *lines; /* one per line */
 	int nlines;
 
@@ -164,7 +185,6 @@ struct tog_blame_view_state {
 	int blame_complete;
 	int eof;
 	int done;
-	pthread_mutex_t mutex;
 	struct got_object_id_queue blamed_commits;
 	struct got_object_qid *blamed_commit;
 	char *path;
@@ -423,7 +443,7 @@ view_resize(struct tog_view *view)
 static const struct got_error *
 view_close_child(struct tog_view *view)
 {
-	const struct got_error *err;
+	const struct got_error *err = NULL;
 
 	if (view->child == NULL)
 		return NULL;
@@ -456,14 +476,21 @@ view_input(struct tog_view **new, struct tog_view **de
 {
 	const struct got_error *err = NULL;
 	struct tog_view *v;
-	int ch;
+	int ch, errcode;
 
 	*new = NULL;
 	*dead = NULL;
 	*focus = NULL;
 
 	nodelay(stdscr, FALSE);
+	/* Allow threads to make progress while we are waiting for input. */
+	errcode = pthread_mutex_unlock(&tog_mutex);
+	if (errcode)
+		return got_error_set_errno(errcode);
 	ch = wgetch(view->window);
+	errcode = pthread_mutex_lock(&tog_mutex);
+	if (errcode)
+		return got_error_set_errno(errcode);
 	nodelay(stdscr, TRUE);
 	switch (ch) {
 		case ERR:
@@ -566,8 +593,12 @@ view_loop(struct tog_view *view)
 	const struct got_error *err = NULL;
 	struct tog_view_list_head views;
 	struct tog_view *new_view, *dead_view, *focus_view, *main_view;
-	int done = 0;
+	int done = 0, errcode;
 
+	errcode = pthread_mutex_lock(&tog_mutex);
+	if (errcode)
+		return got_error_set_errno(errcode);
+
 	TAILQ_INIT(&views);
 	TAILQ_INSERT_HEAD(&views, view, entry);
 
@@ -636,7 +667,7 @@ view_loop(struct tog_view *view)
 			TAILQ_INSERT_TAIL(&views, new_view, entry);
 			if (focus_view == NULL)
 				focus_view = new_view;
-		} 
+		}
 		if (focus_view) {
 			show_panel(focus_view->panel);
 			if (view)
@@ -649,22 +680,26 @@ view_loop(struct tog_view *view)
 				show_panel(view->child->panel);
 		}
 		if (view) {
+			if (focus_view == NULL) {
+				focus_view = view;
+				focus_view->focussed = 1;
+			}
 			if (view->parent) {
 				err = view->parent->show(view->parent);
 				if (err)
-					return err;
+					goto done;
 			}
 			err = view->show(view);
 			if (err)
-				return err;
+				goto done;
 			if (view->child) {
 				err = view->child->show(view->child);
 				if (err)
-					return err;
+					goto done;
 			}
+			update_panels();
+			doupdate();
 		}
-		update_panels();
-		doupdate();
 	}
 done:
 	while (!TAILQ_EMPTY(&views)) {
@@ -672,6 +707,11 @@ done:
 		TAILQ_REMOVE(&views, view, entry);
 		view_close(view);
 	}
+
+	errcode = pthread_mutex_unlock(&tog_mutex);
+	if (errcode)
+		return got_error_set_errno(errcode);
+
 	return err;
 }
 
@@ -904,29 +944,24 @@ free_commits(struct commit_queue *commits)
 
 static const struct got_error *
 queue_commits(struct got_commit_graph *graph, struct commit_queue *commits,
-    struct got_object_id *start_id, int minqueue,
-    struct got_repository *repo, const char *path)
+    int minqueue, struct got_repository *repo, const char *path)
 {
 	const struct got_error *err = NULL;
 	int nqueued = 0;
 
-	if (start_id) {
-		err = got_commit_graph_iter_start(graph, start_id, repo);
-		if (err)
-			return err;
-	}
-
+	/*
+	 * We keep all commits open throughout the lifetime of the log
+	 * view in order to avoid having to re-fetch commits from disk
+	 * while updating the display.
+	 */
 	while (nqueued < minqueue) {
 		struct got_object_id *id;
 		struct got_commit_object *commit;
 		struct commit_queue_entry *entry;
+		int errcode;
 
 		err = got_commit_graph_iter_next(&id, graph);
 		if (err) {
-			if (err->code == GOT_ERR_ITER_COMPLETED) {
-				err = NULL;
-				break;
-			}
 			if (err->code != GOT_ERR_ITER_NEED_MORE)
 				break;
 			err = got_commit_graph_fetch_commits(graph,
@@ -948,34 +983,26 @@ queue_commits(struct got_commit_graph *graph, struct c
 			break;
 		}
 
+		errcode = pthread_mutex_lock(&tog_mutex);
+		if (errcode) {
+			err = got_error_set_errno(errcode);
+			break;
+		}
+
+		entry->idx = commits->ncommits;
 		TAILQ_INSERT_TAIL(&commits->head, entry, entry);
 		nqueued++;
 		commits->ncommits++;
+
+		errcode = pthread_mutex_unlock(&tog_mutex);
+		if (errcode && err == NULL)
+			err = got_error_set_errno(errcode);
 	}
 
 	return err;
 }
 
 static const struct got_error *
-fetch_next_commit(struct commit_queue_entry **pentry,
-    struct commit_queue_entry *entry, struct commit_queue *commits,
-    struct got_commit_graph *graph, struct got_repository *repo,
-    const char *path)
-{
-	const struct got_error *err = NULL;
-
-	*pentry = NULL;
-
-	err = queue_commits(graph, commits, NULL, 1, repo, path);
-	if (err)
-		return err;
-
-	/* Next entry to display should now be available. */
-	*pentry = TAILQ_NEXT(entry, entry);
-	return NULL;
-}
-
-static const struct got_error *
 get_head_commit_id(struct got_object_id **head_id, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
@@ -1001,13 +1028,12 @@ static const struct got_error *
 draw_commits(struct tog_view *view, struct commit_queue_entry **last,
     struct commit_queue_entry **selected, struct commit_queue_entry *first,
     struct commit_queue *commits, int selected_idx, int limit,
-    struct got_commit_graph *graph, struct got_repository *repo,
-    const char *path)
+    const char *path, int commits_needed)
 {
 	const struct got_error *err = NULL;
 	struct commit_queue_entry *entry;
 	int ncommits, width;
-	char *id_str, *header;
+	char *id_str = NULL, *header = NULL, *ncommits_str = NULL;
 	wchar_t *wline;
 
 	entry = first;
@@ -1021,41 +1047,50 @@ draw_commits(struct tog_view *view, struct commit_queu
 		ncommits++;
 	}
 
-	err = got_object_id_str(&id_str, (*selected)->id);
-	if (err)
-		return err;
-
-	if (path && strcmp(path, "/") != 0) {
-		if (asprintf(&header, "commit: %s [%s]", id_str, path) == -1) {
-			err = got_error_from_errno();
-			free(id_str);
+	if (*selected) {
+		err = got_object_id_str(&id_str, (*selected)->id);
+		if (err)
 			return err;
-		}
-	} else if (asprintf(&header, "commit: %s", id_str) == -1) {
-		err = got_error_from_errno();
-		free(id_str);
-		return err;
 	}
-	free(id_str);
-	err = format_line(&wline, &width, header, view->ncols);
-	if (err) {
-		free(header);
-		return err;
+
+	if (asprintf(&ncommits_str, " [%d/%d]%s ",
+	    entry ? entry->idx + 1 : 0, commits->ncommits,
+	    commits_needed == 0 ? "" : " loading...") == -1)
+		return got_error_from_errno();
+
+	if (path && strcmp(path, "/") != 0) {
+		if (asprintf(&header, "commit: %s %s%s",
+		    id_str ? id_str : "........................................",
+		    path, ncommits_str) == -1) {
+			err = got_error_from_errno();
+			header = NULL;
+			goto done;
+		}
+	} else if (asprintf(&header, "commit: %s%s",
+	    id_str ? id_str : "........................................",
+	    ncommits_str) == -1) {
+		err = got_error_from_errno();
+		header = NULL;
+		goto done;
 	}
-	free(header);
+	err = format_line(&wline, &width, header, view->ncols);
+	if (err)
+		goto done;
 
 	werase(view->window);
 
 	if (view_needs_focus_indication(view))
 		wstandout(view->window);
 	waddwstr(view->window, wline);
+	while (width < view->ncols) {
+		waddch(view->window, ' ');
+		width++;
+	}
 	if (view_needs_focus_indication(view))
 		wstandend(view->window);
-	if (width < view->ncols)
-		waddch(view->window, '\n');
 	free(wline);
 	if (limit <= 1)
-		return NULL;
+		goto done;
 
 	entry = first;
 	*last = first;
@@ -1072,20 +1107,14 @@ draw_commits(struct tog_view *view, struct commit_queu
 			break;
 		ncommits++;
 		*last = entry;
-		if (entry == TAILQ_LAST(&commits->head, commit_queue_head)) {
-			err = queue_commits(graph, commits, NULL, 1,
-			    repo, path);
-			if (err) {
-				if (err->code != GOT_ERR_ITER_COMPLETED)
-					return err;
-				err = NULL;
-			}
-		}
 		entry = TAILQ_NEXT(entry, entry);
 	}
 
 	view_vborder(view);
-
+done:
+	free(id_str);
+	free(ncommits_str);
+	free(header);
 	return err;
 }
 
@@ -1113,20 +1142,27 @@ scroll_up(struct commit_queue_entry **first_displayed_
 static const struct got_error *
 scroll_down(struct commit_queue_entry **first_displayed_entry, int maxscroll,
     struct commit_queue_entry **last_displayed_entry,
-    struct commit_queue *commits, struct got_commit_graph *graph,
-    struct got_repository *repo, const char *path)
+    struct commit_queue *commits, int *log_complete, int *commits_needed,
+    pthread_cond_t *need_commits)
 {
 	const struct got_error *err = NULL;
 	struct commit_queue_entry *pentry;
 	int nscrolled = 0;
 
+	if (*last_displayed_entry == NULL)
+		return NULL;
+
 	do {
 		pentry = TAILQ_NEXT(*last_displayed_entry, entry);
 		if (pentry == NULL) {
-			err = fetch_next_commit(&pentry, *last_displayed_entry,
-			    commits, graph, repo, path);
-			if (err || pentry == NULL)
-				break;
+			int errcode;
+			if (*log_complete)
+				return NULL;
+			*commits_needed = maxscroll + 20;
+			errcode = pthread_cond_signal(need_commits);
+			if (errcode)
+				return got_error_set_errno(errcode);
+			return NULL;
 		}
 		*last_displayed_entry = pentry;
 
@@ -1198,44 +1234,142 @@ browse_commit(struct tog_view **new_view, int begin_x,
 		got_object_tree_close(tree);
 	else
 		*new_view = tree_view;
+	return err;
+}
+
+static void *
+log_thread(void *arg)
+{
+	const struct got_error *err = NULL;
+	int errcode = 0;
+	struct tog_log_thread_args *a = arg;
+	int done = 0;
+
+	err = got_commit_graph_iter_start(a->graph, a->start_id, a->repo);
+	if (err)
+		return (void *)err;
+
+	while (!done && !err) {
+		err = queue_commits(a->graph, a->commits, 1, a->repo,
+		    a->in_repo_path);
+		if (err) {
+			if (err->code != GOT_ERR_ITER_COMPLETED)
+				return (void *)err;
+			err = NULL;
+			done = 1;
+		} else if (a->commits_needed > 0)
+			a->commits_needed--;
+
+		errcode = pthread_mutex_lock(&tog_mutex);
+		if (errcode)
+			return (void *)got_error_set_errno(errcode);
+
+		if (done)
+			a->log_complete = 1;
+		else if (*a->quit) {
+			done = 1;
+			a->log_complete = 1;
+		} else if (*a->first_displayed_entry == NULL) {
+			*a->first_displayed_entry =
+			    TAILQ_FIRST(&a->commits->head);
+			*a->selected_entry = *a->first_displayed_entry;
+		}
+
+		err = draw_commits(a->view, a->last_displayed_entry,
+		    a->selected_entry, *a->first_displayed_entry,
+		    a->commits, *a->selected, a->view->nlines,
+		    a->in_repo_path, a->commits_needed);
+
+		update_panels();
+		doupdate();
+
+		if (done)
+			a->commits_needed = 0;
+		else if (a->commits_needed == 0) {
+			errcode = pthread_cond_wait(&a->need_commits,
+			    &tog_mutex);
+			if (errcode)
+				err = got_error_set_errno(errcode);
+		}
+
+		errcode = pthread_mutex_unlock(&tog_mutex);
+		if (errcode && err == NULL)
+			err = got_error_set_errno(errcode);
+	}
+	return (void *)err;
+}
+
+static const struct got_error *
+stop_log_thread(struct tog_log_view_state *s)
+{
+	const struct got_error *err = NULL;
+	int errcode;
+
+	if (s->thread) {
+		s->quit = 1;
+		errcode = pthread_cond_signal(&s->thread_args.need_commits);
+		if (errcode)
+			return got_error_set_errno(errcode);
+		errcode = pthread_mutex_unlock(&tog_mutex);
+		if (errcode)
+			return got_error_set_errno(errcode);
+		errcode = pthread_join(s->thread, (void **)&err);
+		if (errcode)
+			return got_error_set_errno(errcode);
+		errcode = pthread_mutex_lock(&tog_mutex);
+		if (errcode)
+			return got_error_set_errno(errcode);
+		s->thread = NULL;
+	}
+
+	errcode = pthread_cond_destroy(&s->thread_args.need_commits);
+	if (errcode && err == NULL)
+		err = got_error_set_errno(errcode);
+
+	if (s->thread_args.repo) {
+		got_repo_close(s->thread_args.repo);
+		s->thread_args.repo = NULL;
+	}
+
+	if (s->thread_args.graph) {
+		got_commit_graph_close(s->thread_args.graph);
+		s->thread_args.graph = NULL;
+	}
+
 	return err;
 }
 
 static const struct got_error *
+close_log_view(struct tog_view *view)
+{
+	const struct got_error *err = NULL;
+	struct tog_log_view_state *s = &view->state.log;
+
+	err = stop_log_thread(s);
+	free_commits(&s->commits);
+	free(s->in_repo_path);
+	free(s->start_id);
+	return err;
+}
+
+static const struct got_error *
 open_log_view(struct tog_view *view, struct got_object_id *start_id,
     struct got_repository *repo, const char *path)
 {
 	const struct got_error *err = NULL;
 	struct tog_log_view_state *s = &view->state.log;
+	struct got_repository *thread_repo = NULL;
+	struct got_commit_graph *thread_graph = NULL;
+	int errcode;
 
 	err = got_repo_map_path(&s->in_repo_path, repo, path);
 	if (err != NULL)
 		goto done;
 
-	err = got_commit_graph_open(&s->graph, start_id, s->in_repo_path,
-	    0, repo);
-	if (err)
-		goto done;
 	/* The commit queue only contains commits being displayed. */
 	TAILQ_INIT(&s->commits.head);
 	s->commits.ncommits = 0;
 
-	/*
-	 * Open the initial batch of commits, sorted in commit graph order.
-	 * We keep all commits open throughout the lifetime of the log view
-	 * in order to avoid having to re-fetch commits from disk while
-	 * updating the display.
-	 */
-	err = queue_commits(s->graph, &s->commits, start_id, view->nlines,
-	    repo, s->in_repo_path);
-	if (err) {
-		if (err->code != GOT_ERR_ITER_COMPLETED)
-			goto done;
-		err = NULL;
-	}
-
-	s->first_displayed_entry = TAILQ_FIRST(&s->commits.head);
-	s->selected_entry = s->first_displayed_entry;
 	s->repo = repo;
 	s->start_id = got_object_id_dup(start_id);
 	if (s->start_id == NULL) {
@@ -1246,35 +1380,57 @@ open_log_view(struct tog_view *view, struct got_object
 	view->show = show_log_view;
 	view->input = input_log_view;
 	view->close = close_log_view;
+
+	err = got_repo_open(&thread_repo, got_repo_get_path(repo));
+	if (err)
+		goto done;
+	err = got_commit_graph_open(&thread_graph, start_id, s->in_repo_path,
+	    0, thread_repo);
+	if (err)
+		goto done;
+
+	errcode = pthread_cond_init(&s->thread_args.need_commits, NULL);
+	if (errcode) {
+		err = got_error_set_errno(errcode);
+		goto done;
+	}
+
+	s->thread_args.commits_needed = view->nlines;
+	s->thread_args.graph = thread_graph;
+	s->thread_args.commits = &s->commits;
+	s->thread_args.in_repo_path = s->in_repo_path;
+	s->thread_args.start_id = s->start_id;
+	s->thread_args.repo = thread_repo;
+	s->thread_args.log_complete = 0;
+	s->thread_args.quit = &s->quit;
+	s->thread_args.view = view;
+	s->thread_args.first_displayed_entry = &s->first_displayed_entry;
+	s->thread_args.last_displayed_entry = &s->last_displayed_entry;
+	s->thread_args.selected_entry = &s->selected_entry;
+	s->thread_args.selected = &s->selected;
+
+	errcode = pthread_create(&s->thread, NULL, log_thread,
+	    &s->thread_args);
+	if (errcode) {
+		err = got_error_set_errno(errcode);
+		goto done;
+	}
+
 done:
+	if (err)
+		close_log_view(view);
 	return err;
 }
 
 static const struct got_error *
-close_log_view(struct tog_view *view)
-{
-	struct tog_log_view_state *s = &view->state.log;
-
-	if (s->graph)
-		got_commit_graph_close(s->graph);
-	free_commits(&s->commits);
-	free(s->in_repo_path);
-	free(s->start_id);
-	return NULL;
-}
-
-static const struct got_error *
 show_log_view(struct tog_view *view)
 {
-	const struct got_error *err = NULL;
 	struct tog_log_view_state *s = &view->state.log;
 
 	return draw_commits(view, &s->last_displayed_entry,
 	    &s->selected_entry, s->first_displayed_entry,
-	    &s->commits, s->selected, view->nlines, s->graph,
-	    s->repo, s->in_repo_path);
-	if (err)
-		return err;
+	    &s->commits, s->selected, view->nlines,
+	    s->in_repo_path, s->thread_args.commits_needed);
 }
 
 static const struct got_error *
@@ -1288,6 +1444,9 @@ input_log_view(struct tog_view **new_view, struct tog_
 	int begin_x = 0;
 
 	switch (ch) {
+		case 'q':
+			s->quit = 1;
+			break;
 		case 'k':
 		case KEY_UP:
 			if (s->selected > 0)
@@ -1315,22 +1474,18 @@ input_log_view(struct tog_view **new_view, struct tog_
 			}
 			err = scroll_down(&s->first_displayed_entry, 1,
 			    &s->last_displayed_entry, &s->commits,
-			    s->graph, s->repo, s->in_repo_path);
-			if (err) {
-				if (err->code != GOT_ERR_ITER_COMPLETED)
-					break;
-				err = NULL;
-			}
+			    &s->thread_args.log_complete,
+			    &s->thread_args.commits_needed,
+			    &s->thread_args.need_commits);
 			break;
 		case KEY_NPAGE: {
 			struct commit_queue_entry *first;
 			first = s->first_displayed_entry;
 			err = scroll_down(&s->first_displayed_entry,
 			    view->nlines, &s->last_displayed_entry,
-			    &s->commits, s->graph, s->repo,
-			    s->in_repo_path);
-			if (err && err->code != GOT_ERR_ITER_COMPLETED)
-				break;
+			    &s->commits, &s->thread_args.log_complete,
+			    &s->thread_args.commits_needed,
+			    &s->thread_args.need_commits);
 			if (first == s->first_displayed_entry &&
 			    s->selected < MIN(view->nlines - 2,
 			    s->commits.ncommits - 1)) {
@@ -1397,6 +1552,9 @@ input_log_view(struct tog_view **new_view, struct tog_
 			parent_path = dirname(s->in_repo_path);
 			if (parent_path && strcmp(parent_path, ".") != 0) {
 				struct tog_view *lv;
+				err = stop_log_thread(s);
+				if (err)
+					return err;
 				lv = view_open(view->nlines, view->ncols,
 				    view->begin_y, view->begin_x, TOG_VIEW_LOG);
 				if (lv == NULL)
@@ -1404,13 +1562,14 @@ input_log_view(struct tog_view **new_view, struct tog_
 				err = open_log_view(lv, s->start_id, s->repo,
 				    parent_path);
 				if (err)
-					break;
+					return err;;
 				if (view_is_parent_view(view))
 					*new_view = lv;
 				else {
 					view_set_child(view->parent, lv);
 					*dead_view = view;
 				}
+				return NULL;
 			}
 			break;
 		default:
@@ -2025,13 +2184,15 @@ blame_cb(void *arg, int nlines, int lineno, struct got
 	const struct got_error *err = NULL;
 	struct tog_blame_cb_args *a = arg;
 	struct tog_blame_line *line;
+	int errcode;
 
 	if (nlines != a->nlines ||
 	    (lineno != -1 && lineno < 1) || lineno > a->nlines)
 		return got_error(GOT_ERR_RANGE);
 
-	if (pthread_mutex_lock(a->mutex) != 0)
-		return got_error_from_errno();
+	errcode = pthread_mutex_lock(&tog_mutex);
+	if (errcode)
+		return got_error_set_errno(errcode);
 
 	if (*a->quit) {	/* user has quit the blame view */
 		err = got_error(GOT_ERR_ITER_COMPLETED);
@@ -2056,8 +2217,9 @@ blame_cb(void *arg, int nlines, int lineno, struct got
 	    a->lines, a->nlines, 0, *a->selected_line, a->first_displayed_line,
 	    a->last_displayed_line, a->eof, a->view->nlines);
 done:
-	if (pthread_mutex_unlock(a->mutex) != 0)
-		return got_error_from_errno();
+	errcode = pthread_mutex_unlock(&tog_mutex);
+	if (errcode)
+		err = got_error_set_errno(errcode);
 	return err;
 }
 
@@ -2067,24 +2229,32 @@ blame_thread(void *arg)
 	const struct got_error *err;
 	struct tog_blame_thread_args *ta = arg;
 	struct tog_blame_cb_args *a = ta->cb_args;
+	int errcode;
 
 	err = got_blame_incremental(ta->path, a->commit_id, ta->repo,
 	    blame_cb, ta->cb_args);
 
-	if (pthread_mutex_lock(a->mutex) != 0)
-		return (void *)got_error_from_errno();
+	errcode = pthread_mutex_lock(&tog_mutex);
+	if (errcode)
+		return (void *)got_error_set_errno(errcode);
 
 	got_repo_close(ta->repo);
 	ta->repo = NULL;
 	*ta->complete = 1;
-	if (!err)
+	if (!err) {
 		err = draw_blame(a->view, a->commit_id, a->f, a->path,
 		    a->lines, a->nlines, 1, *a->selected_line,
 		    a->first_displayed_line, a->last_displayed_line, a->eof,
 		    a->view->nlines);
+		if (!err) {
+			update_panels();
+			doupdate();
+		}
+	}
 
-	if (pthread_mutex_unlock(a->mutex) != 0 && err == NULL)
-		err = got_error_from_errno();
+	errcode = pthread_mutex_unlock(&tog_mutex);
+	if (errcode && err == NULL)
+		err = got_error_set_errno(errcode);
 
 	return (void *)err;
 }
@@ -2147,8 +2317,16 @@ stop_blame(struct tog_blame *blame)
 	int i;
 
 	if (blame->thread) {
-		if (pthread_join(blame->thread, (void **)&err) != 0)
-			err = got_error_from_errno();
+		int errcode;
+		errcode = pthread_mutex_unlock(&tog_mutex);
+		if (errcode)
+			return got_error_set_errno(errcode);
+		errcode = pthread_join(blame->thread, (void **)&err);
+		if (errcode)
+			return got_error_set_errno(errcode);
+		errcode = pthread_mutex_lock(&tog_mutex);
+		if (errcode)
+			return got_error_set_errno(errcode);
 		if (err && err->code == GOT_ERR_ITER_COMPLETED)
 			err = NULL;
 		blame->thread = NULL;
@@ -2172,11 +2350,9 @@ stop_blame(struct tog_blame *blame)
 }
 
 static const struct got_error *
-run_blame(struct tog_blame *blame, pthread_mutex_t *mutex,
-    struct tog_view *view, int *blame_complete,
-    int *first_displayed_line, int *last_displayed_line,
-    int *selected_line, int *done, int *eof, const char *path,
-    struct got_object_id *commit_id,
+run_blame(struct tog_blame *blame, struct tog_view *view, int *blame_complete,
+    int *first_displayed_line, int *last_displayed_line, int *selected_line,
+    int *done, int *eof, const char *path, struct got_object_id *commit_id,
     struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
@@ -2184,6 +2360,7 @@ run_blame(struct tog_blame *blame, pthread_mutex_t *mu
 	struct got_repository *thread_repo = NULL;
 	struct got_object_id *obj_id = NULL;
 	struct got_object *obj = NULL;
+	int errcode;
 
 	err = got_object_id_by_path(&obj_id, repo, commit_id, path);
 	if (err)
@@ -2224,7 +2401,6 @@ run_blame(struct tog_blame *blame, pthread_mutex_t *mu
 	blame->cb_args.view = view;
 	blame->cb_args.lines = blame->lines;
 	blame->cb_args.nlines = blame->nlines;
-	blame->cb_args.mutex = mutex;
 	blame->cb_args.commit_id = got_object_id_dup(commit_id);
 	if (blame->cb_args.commit_id == NULL) {
 		err = got_error_from_errno();
@@ -2244,9 +2420,10 @@ run_blame(struct tog_blame *blame, pthread_mutex_t *mu
 	blame->thread_args.complete = blame_complete;
 	*blame_complete = 0;
 
-	if (pthread_create(&blame->thread, NULL, blame_thread,
-	    &blame->thread_args) != 0) {
-		err = got_error_from_errno();
+	errcode = pthread_create(&blame->thread, NULL, blame_thread,
+	    &blame->thread_args);
+	if (errcode) {
+		err = got_error_set_errno(errcode);
 		goto done;
 	}
 
@@ -2270,9 +2447,6 @@ open_blame_view(struct tog_view *view, char *path,
 
 	SIMPLEQ_INIT(&s->blamed_commits);
 
-	if (pthread_mutex_init(&s->mutex, NULL) != 0)
-		return got_error_from_errno();
-
 	err = got_object_qid_alloc(&s->blamed_commit, commit_id);
 	if (err)
 		return err;
@@ -2293,7 +2467,7 @@ open_blame_view(struct tog_view *view, char *path,
 	view->input = input_blame_view;
 	view->close = close_blame_view;
 
-	return run_blame(&s->blame, &s->mutex, view, &s->blame_complete,
+	return run_blame(&s->blame, view, &s->blame_complete,
 	    &s->first_displayed_line, &s->last_displayed_line,
 	    &s->selected_line, &s->done, &s->eof, s->path,
 	    s->blamed_commit->id, s->repo);
@@ -2326,17 +2500,11 @@ show_blame_view(struct tog_view *view)
 	const struct got_error *err = NULL;
 	struct tog_blame_view_state *s = &view->state.blame;
 
-	if (pthread_mutex_lock(&s->mutex) != 0)
-		return got_error_from_errno();
-
 	err = draw_blame(view, s->blamed_commit->id, s->blame.f,
 	    s->path, s->blame.lines, s->blame.nlines, s->blame_complete,
 	    s->selected_line, &s->first_displayed_line,
 	    &s->last_displayed_line, &s->eof, view->nlines);
 
-	if (pthread_mutex_unlock(&s->mutex) != 0 && err == NULL)
-		err = got_error_from_errno();
-
 	view_vborder(view);
 	return err;
 }
@@ -2351,19 +2519,10 @@ input_blame_view(struct tog_view **new_view, struct to
 	struct tog_blame_view_state *s = &view->state.blame;
 	int begin_x = 0;
 
-	if (pthread_mutex_lock(&s->mutex) != 0) {
-		err = got_error_from_errno();
-		goto done;
-	}
-
 	switch (ch) {
 		case 'q':
 			s->done = 1;
-			if (pthread_mutex_unlock(&s->mutex) != 0) {
-				err = got_error_from_errno();
-				goto done;
-			}
-			return stop_blame(&s->blame);
+			break;
 		case 'k':
 		case KEY_UP:
 			if (s->selected_line > 1)
@@ -2411,16 +2570,8 @@ input_blame_view(struct tog_view **new_view, struct to
 			if (ch == 'p' && pobj == NULL)
 				break;
 			s->done = 1;
-			if (pthread_mutex_unlock(&s->mutex) != 0) {
-				err = got_error_from_errno();
-				goto done;
-			}
 			thread_err = stop_blame(&s->blame);
 			s->done = 0;
-			if (pthread_mutex_lock(&s->mutex) != 0) {
-				err = got_error_from_errno();
-				goto done;
-			}
 			if (thread_err)
 				break;
 			id = got_object_get_id(ch == 'b' ? obj : pobj);
@@ -2435,10 +2586,8 @@ input_blame_view(struct tog_view **new_view, struct to
 				goto done;
 			SIMPLEQ_INSERT_HEAD(&s->blamed_commits,
 			    s->blamed_commit, entry);
-			err = run_blame(&s->blame, &s->mutex, view,
-			    &s->blame_complete,
-			    &s->first_displayed_line,
-			    &s->last_displayed_line,
+			err = run_blame(&s->blame, view, &s->blame_complete,
+			    &s->first_displayed_line, &s->last_displayed_line,
 			    &s->selected_line, &s->done, &s->eof,
 			    s->path, s->blamed_commit->id, s->repo);
 			if (err)
@@ -2451,26 +2600,16 @@ input_blame_view(struct tog_view **new_view, struct to
 			if (!got_object_id_cmp(first->id, s->commit_id))
 				break;
 			s->done = 1;
-			if (pthread_mutex_unlock(&s->mutex) != 0) {
-				err = got_error_from_errno();
-				goto done;
-			}
 			thread_err = stop_blame(&s->blame);
 			s->done = 0;
-			if (pthread_mutex_lock(&s->mutex) != 0) {
-				err = got_error_from_errno();
-				goto done;
-			}
 			if (thread_err)
 				break;
 			SIMPLEQ_REMOVE_HEAD(&s->blamed_commits, entry);
 			got_object_qid_free(s->blamed_commit);
 			s->blamed_commit =
 			    SIMPLEQ_FIRST(&s->blamed_commits);
-			err = run_blame(&s->blame, &s->mutex, view,
-			    &s->blame_complete,
-			    &s->first_displayed_line,
-			    &s->last_displayed_line,
+			err = run_blame(&s->blame, view, &s->blame_complete,
+			    &s->first_displayed_line, &s->last_displayed_line,
 			    &s->selected_line, &s->done, &s->eof, s->path,
 			    s->blamed_commit->id, s->repo);
 			if (err)
@@ -2549,9 +2688,6 @@ input_blame_view(struct tog_view **new_view, struct to
 		default:
 			break;
 	}
-
-	if (pthread_mutex_unlock(&s->mutex) != 0)
-		err = got_error_from_errno();
 done:
 	if (pobj)
 		got_object_close(pobj);