@@ -1968,11 +1968,224 @@ static int checkout_create_the_new__single(
1968
1968
return 0 ;
1969
1969
}
1970
1970
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
+
1971
2182
static int checkout_create_the_new (
1972
2183
unsigned int * actions ,
1973
2184
checkout_data * data )
1974
2185
{
1975
2186
#ifdef GIT_THREADS
2187
+ if (git__online_cpus () > 1 )
2188
+ return checkout_create_the_new__parallel (actions , data );
1976
2189
#endif
1977
2190
return checkout_create_the_new__single (actions , data );
1978
2191
}
0 commit comments