Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 31f30a4

Browse files
committed
thread checkout: add threading to checkout_create_the_new
1 parent 3220782 commit 31f30a4

File tree

1 file changed

+213
-0
lines changed

1 file changed

+213
-0
lines changed

src/checkout.c

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,11 +1968,224 @@ static int checkout_create_the_new__single(
19681968
return 0;
19691969
}
19701970

1971+
#ifdef GIT_THREADS
1972+
1973+
static int paths_cmp(const void *a, const void *b) { return git__strcmp((char*)a, (char*)b); }
1974+
1975+
typedef struct {
1976+
int error;
1977+
size_t index;
1978+
bool skipped;
1979+
} checkout_progress_pair;
1980+
1981+
typedef struct {
1982+
git_thread thread;
1983+
const unsigned int *actions;
1984+
checkout_data *cd;
1985+
1986+
git_cond *cond;
1987+
git_mutex *mutex;
1988+
1989+
git_atomic32 *delta_index;
1990+
git_atomic32 *error;
1991+
git_vector *progress_pairs;
1992+
} thread_params;
1993+
1994+
static void *checkout_create_the_new__thread(void *arg)
1995+
{
1996+
thread_params *worker = arg;
1997+
size_t i;
1998+
checkout_buffers *buffers = git__malloc(sizeof(checkout_buffers));
1999+
2000+
// TODO if the thread fails to allocate, signal and have the parent thread check the return value
2001+
// TODO deduplicate this setup with checkout_data_init
2002+
git_buf_init(&buffers->target_path, 0);
2003+
git_buf_init(&buffers->tmp, 0);
2004+
git_tlsdata_set(worker->cd->buffers, buffers);
2005+
git_buf_puts(&buffers->target_path, worker->cd->opts.target_directory);
2006+
git_path_to_dir(&buffers->target_path);
2007+
buffers->target_len = git_buf_len(&buffers->target_path);
2008+
2009+
while ((i = git_atomic32_add(worker->delta_index, 1)) <
2010+
git_vector_length(&worker->cd->diff->deltas)) {
2011+
checkout_progress_pair *progress_pair;
2012+
git_diff_delta *delta = git_vector_get(&worker->cd->diff->deltas, i);
2013+
2014+
if (delta == NULL || git_atomic32_get(worker->error) != 0)
2015+
return NULL;
2016+
2017+
progress_pair = (checkout_progress_pair *)git__malloc(
2018+
sizeof(checkout_progress_pair));
2019+
if (progress_pair == NULL) {
2020+
git_atomic32_set(worker->error, -1);
2021+
git_cond_signal(worker->cond);
2022+
return NULL;
2023+
}
2024+
2025+
/* We skip symlink operations, because we handle them
2026+
* in the main thread to avoid a symlink security flaw.
2027+
*/
2028+
if (!S_ISLNK(delta->new_file.mode) &&
2029+
worker->actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) {
2030+
/* We will retry failed operations in the calling thread to handle
2031+
* the case where might encounter a file locking error due to
2032+
* multithreading and name collisions.
2033+
*/
2034+
progress_pair->index = i;
2035+
progress_pair->error = checkout_blob(worker->cd, &delta->new_file);
2036+
progress_pair->skipped = false;
2037+
} else {
2038+
progress_pair->index = i;
2039+
progress_pair->error = 0;
2040+
progress_pair->skipped = true;
2041+
}
2042+
2043+
git_mutex_lock(worker->mutex);
2044+
git_vector_insert(worker->progress_pairs, progress_pair);
2045+
git_cond_signal(worker->cond);
2046+
git_mutex_unlock(worker->mutex);
2047+
}
2048+
2049+
return NULL;
2050+
}
2051+
2052+
static int checkout_create_the_new__parallel(
2053+
unsigned int *actions,
2054+
checkout_data *data)
2055+
{
2056+
thread_params *p;
2057+
size_t i, num_threads = git__online_cpus(), last_index = 0, current_index = 0,
2058+
num_deltas = git_vector_length(&data->diff->deltas);
2059+
int ret;
2060+
checkout_progress_pair *progress_pair;
2061+
git_atomic32 delta_index, error;
2062+
git_diff_delta *delta;
2063+
git_vector errored_pairs, progress_pairs;
2064+
git_cond cond;
2065+
git_mutex mutex;
2066+
2067+
if (
2068+
(ret = git_vector_init(&progress_pairs, num_deltas, paths_cmp)) < 0 ||
2069+
(ret = git_vector_init(&errored_pairs, num_deltas, paths_cmp)) < 0)
2070+
return ret;
2071+
2072+
p = git__mallocarray(num_threads, sizeof(*p));
2073+
GIT_ERROR_CHECK_ALLOC(p);
2074+
2075+
git_cond_init(&cond);
2076+
git_mutex_init(&mutex);
2077+
git_mutex_lock(&mutex);
2078+
2079+
git_atomic32_set(&delta_index, -1);
2080+
git_atomic32_set(&error, 0);
2081+
2082+
/* Initialize worker threads */
2083+
for (i = 0; i < num_threads; ++i) {
2084+
p[i].actions = actions;
2085+
p[i].cd = data;
2086+
p[i].cond = &cond;
2087+
p[i].mutex = &mutex;
2088+
p[i].error = &error;
2089+
p[i].delta_index = &delta_index;
2090+
p[i].progress_pairs = &progress_pairs;
2091+
}
2092+
2093+
/* Start worker threads */
2094+
for (i = 0; i < num_threads; ++i) {
2095+
ret = git_thread_create(&p[i].thread, checkout_create_the_new__thread, &p[i]);
2096+
2097+
/* On error, we will cleanly exit any started worker threads,
2098+
* and then return with our error code */
2099+
if (ret) {
2100+
git_atomic32_set(&error, -1);
2101+
git_error_set(GIT_ERROR_THREAD, "unable to create thread");
2102+
git_mutex_unlock(&mutex);
2103+
/* Only clean up the number of threads we have started */
2104+
num_threads = i;
2105+
ret = -1;
2106+
goto cleanup;
2107+
}
2108+
}
2109+
2110+
while (last_index < num_deltas) {
2111+
if ((ret = git_atomic32_get(&error)) != 0) {
2112+
git_mutex_unlock(&mutex);
2113+
goto cleanup;
2114+
}
2115+
2116+
current_index = git_vector_length(&progress_pairs);
2117+
2118+
if (last_index == current_index) {
2119+
git_cond_wait(&cond, &mutex);
2120+
current_index = git_vector_length(&progress_pairs);
2121+
}
2122+
2123+
git_mutex_unlock(&mutex);
2124+
2125+
for (; last_index < current_index; ++last_index) {
2126+
progress_pair = git_vector_get(&progress_pairs,
2127+
last_index);
2128+
delta = git_vector_get(&data->diff->deltas, last_index);
2129+
2130+
if (progress_pair->skipped)
2131+
continue;
2132+
2133+
/* We will retry errored checkouts synchronously after all the workers
2134+
* complete
2135+
*/
2136+
if (progress_pair->error < 0) {
2137+
git_vector_insert(&errored_pairs, progress_pair);
2138+
continue;
2139+
}
2140+
2141+
data->completed_steps++;
2142+
report_progress(data, delta->new_file.path);
2143+
}
2144+
git_mutex_lock(&mutex);
2145+
}
2146+
2147+
git_mutex_unlock(&mutex);
2148+
2149+
git_vector_foreach(&errored_pairs, i, progress_pair) {
2150+
delta = git_vector_get(&data->diff->deltas, progress_pair->index);
2151+
if ((ret = checkout_create_the_new_perform(data, actions[progress_pair->index],
2152+
delta, NO_SYMLINKS)) < 0)
2153+
goto cleanup;
2154+
}
2155+
2156+
/* After we create everything else, we need to create all the symlinks
2157+
* to ensure that we don't accidentally write data through symlinks into
2158+
* the .git directory.
2159+
*/
2160+
git_vector_foreach(&data->diff->deltas, i, delta) {
2161+
if ((ret = checkout_create_the_new_perform(data, actions[i], delta,
2162+
SYMLINKS_ONLY)) < 0)
2163+
goto cleanup;
2164+
}
2165+
2166+
cleanup:
2167+
for (i = 0; i < num_threads; ++i) {
2168+
git_thread_join(&p[i].thread, NULL);
2169+
}
2170+
2171+
git__free(p);
2172+
git_vector_free(&errored_pairs);
2173+
git_vector_free_deep(&progress_pairs);
2174+
git_cond_free(&cond);
2175+
git_mutex_free(&mutex);
2176+
2177+
return ret;
2178+
}
2179+
2180+
#endif
2181+
19712182
static int checkout_create_the_new(
19722183
unsigned int *actions,
19732184
checkout_data *data)
19742185
{
19752186
#ifdef GIT_THREADS
2187+
if (git__online_cpus() > 1)
2188+
return checkout_create_the_new__parallel(actions, data);
19762189
#endif
19772190
return checkout_create_the_new__single(actions, data);
19782191
}

0 commit comments

Comments
 (0)