2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-17 13:42:14 +00:00

Merge branch 'develop' of https://github.com/boostorg/build into develop

This commit is contained in:
Edward Diener
2014-07-08 15:29:17 -04:00
2 changed files with 201 additions and 8 deletions

View File

@@ -780,31 +780,161 @@ static void read_output()
* cmdtab array, or -1.
*/
typedef struct _twh_params
{
int * active_procs;
HANDLE * active_handles;
DWORD num_active;
DWORD timeoutMillis;
} twh_params;
static int try_wait_helper( twh_params * );
static int try_wait( int const timeoutMillis )
{
#define MAX_THREADS MAXJOBS/(MAXIMUM_WAIT_OBJECTS - 1) + 1
int i;
int num_active;
int wait_api_result;
HANDLE active_handles[ MAXJOBS ];
int active_procs[ MAXJOBS ];
HANDLE active_handles[ MAXJOBS + MAX_THREADS ];
int active_procs[ MAXJOBS + MAX_THREADS ];
unsigned int num_threads;
unsigned int num_handles;
unsigned int last_chunk_size;
unsigned int last_chunk_offset;
HANDLE completed_event = INVALID_HANDLE_VALUE;
HANDLE thread_handles[MAXIMUM_WAIT_OBJECTS];
twh_params thread_params[MAX_THREADS];
int result = -1;
BOOL success;
/* Prepare a list of all active processes to wait for. */
for ( num_active = 0, i = 0; i < globs.jobs; ++i )
if ( cmdtab[ i ].pi.hProcess )
{
if ( num_active == MAXIMUM_WAIT_OBJECTS )
{
/*
* We surpassed MAXIMUM_WAIT_OBJECTS, so we need to use threads
* to wait for this set. Create an event object which will
* notify threads to stop waiting. Every handle set chunk should
* have this event as its last element.
*/
assert( completed_event == INVALID_HANDLE_VALUE );
completed_event = CreateEvent(NULL, FALSE, FALSE, NULL);
active_handles[ num_active ] = active_handles[ num_active - 1 ];
active_procs[ num_active ] = active_procs[ num_active - 1 ];
active_handles[ num_active - 1 ] = completed_event;
active_procs[ num_active - 1 ] = -1;
++num_active;
}
else if ( ( completed_event != INVALID_HANDLE_VALUE ) &&
!((num_active + 1) % MAXIMUM_WAIT_OBJECTS) )
{
active_handles[ num_active ] = completed_event;
active_procs[ num_active ] = -1;
++num_active;
}
active_handles[ num_active ] = cmdtab[ i ].pi.hProcess;
active_procs[ num_active ] = i;
++num_active;
}
/* Wait for a child to complete, or for our timeout window to expire. */
wait_api_result = WaitForMultipleObjects( num_active, active_handles,
FALSE, timeoutMillis );
assert( (num_active <= MAXIMUM_WAIT_OBJECTS) ==
(completed_event == INVALID_HANDLE_VALUE) );
if ( num_active <= MAXIMUM_WAIT_OBJECTS )
{
twh_params twh;
twh.active_procs = active_procs;
twh.active_handles = active_handles;
twh.num_active = num_active;
twh.timeoutMillis = timeoutMillis;
return try_wait_helper( &twh );
}
num_threads = num_active / MAXIMUM_WAIT_OBJECTS;
last_chunk_size = num_active % MAXIMUM_WAIT_OBJECTS;
num_handles = num_threads;
if ( last_chunk_size )
{
/* Can we fit the last chunk in the outer WFMO call? */
if ( last_chunk_size <= MAXIMUM_WAIT_OBJECTS - num_threads )
{
last_chunk_offset = num_threads * MAXIMUM_WAIT_OBJECTS;
for ( i = 0; i < last_chunk_size; ++i )
thread_handles[ i + num_threads ] =
active_handles[ i + last_chunk_offset ];
num_handles = num_threads + last_chunk_size;
}
else
{
/* We need another thread for the remainder. */
/* Add completed_event handle to the last chunk. */
active_handles[ num_active ] = completed_event;
active_procs[ num_active ] = -1;
++last_chunk_size;
++num_active;
++num_threads;
num_handles = num_threads;
}
}
assert( num_threads <= MAX_THREADS );
for ( i = 0; i < num_threads; ++i )
{
thread_params[i].active_procs = active_procs +
i * MAXIMUM_WAIT_OBJECTS;
thread_params[i].active_handles = active_handles +
i * MAXIMUM_WAIT_OBJECTS;
thread_params[i].timeoutMillis = INFINITE;
thread_params[i].num_active = MAXIMUM_WAIT_OBJECTS;
if ( ( i == num_threads - 1 ) && last_chunk_size &&
( num_handles == num_threads ) )
thread_params[i].num_active = last_chunk_size;
thread_handles[i] = CreateThread(NULL, 4 * 1024,
(LPTHREAD_START_ROUTINE)&try_wait_helper, &thread_params[i],
0, NULL);
}
wait_api_result = WaitForMultipleObjects(num_handles, thread_handles,
FALSE, timeoutMillis);
if ( ( WAIT_OBJECT_0 <= wait_api_result ) &&
( wait_api_result < WAIT_OBJECT_0 + num_active ) )
( wait_api_result < WAIT_OBJECT_0 + num_threads ) )
{
HANDLE thread_handle = thread_handles[wait_api_result - WAIT_OBJECT_0];
success = GetExitCodeThread(thread_handle, (DWORD *)&result);
assert( success );
}
else if ( ( WAIT_OBJECT_0 + num_threads <= wait_api_result ) &&
( wait_api_result < WAIT_OBJECT_0 + num_handles ) )
{
unsigned int offset = wait_api_result - num_threads - WAIT_OBJECT_0;
result = active_procs[ last_chunk_offset + offset ];
}
SetEvent(completed_event);
/* Should complete instantly. */
WaitForMultipleObjects(num_threads, thread_handles, TRUE, INFINITE);
CloseHandle(completed_event);
for ( i = 0; i < num_threads; ++i )
CloseHandle(thread_handles[i]);
return result;
#undef MAX_THREADS
}
static int try_wait_helper( twh_params * params )
{
int wait_api_result;
assert( params->num_active <= MAXIMUM_WAIT_OBJECTS );
/* Wait for a child to complete, or for our timeout window to expire. */
wait_api_result = WaitForMultipleObjects( params->num_active,
params->active_handles, FALSE, params->timeoutMillis );
if ( ( WAIT_OBJECT_0 <= wait_api_result ) &&
( wait_api_result < WAIT_OBJECT_0 + params->num_active ) )
{
/* Terminated process detected - return its index. */
return active_procs[ wait_api_result - WAIT_OBJECT_0 ];
return params->active_procs[ wait_api_result - WAIT_OBJECT_0 ];
}
/* Timeout. */

View File

@@ -32,6 +32,7 @@ import path ;
import pch ;
import property ;
import rc ;
import set ;
import toolset ;
import type ;
@@ -130,6 +131,14 @@ rule init (
# Platform specific setup command to invoke before running any of the
# msvc tools used when builing a target for a specific platform, e.g.
# when building a 32 or 64 bit executable.
#
# <rewrite-setup-scripts>
# Whether to rewrite setup scripts. New scripts will be output in
# TEMP directory and will be used instead of originals in build actions.
# Possible values:
# * on - rewrite scripts, if they do not already exist (default)
# * always - always rewrite scripts, even if they already exist
# * off - use original setup scripts
: options *
)
{
@@ -676,6 +685,58 @@ local rule auto-detect-toolset-versions ( )
}
}
# Helper rule to generate a faster alternative to MSVC setup scripts.
# We used to call MSVC setup scripts directly in every action, however in
# newer MSVC versions (10.0+) they make long-lasting registry queries
# which have a significant impact on build time.
local rule maybe-rewrite-setup ( setup-script : setup-options : version : rewrite-setup ? )
{
local result = $(setup-script)" "$(setup-options) ;
# At the moment we only know how to rewrite scripts with cmd shell.
if ( [ os.name ] in NT ) && ( $(rewrite-setup) != off )
{
setup-script-id = b2_msvc_$(version)_$(setup-script:B) ;
if $(setup-options)-is-not-empty
{
setup-script-id = $(setup-script-id)_$(setup-options) ;
}
if $(.$(setup-script-id))
{
errors.error rewriting setup script for the second time ;
}
local tmpdir = [ os.environ TEMP ] ;
local replacement = [ path.native $(tmpdir)/$(setup-script-id).cmd ] ;
if ( $(rewrite-setup) = always ) || ( ! [ path.exists $(replacement) ] )
{
local original-vars = [ SPLIT_BY_CHARACTERS [ SHELL set ] : "\n" ] ;
local new-vars = [ SPLIT_BY_CHARACTERS [ SHELL "$(setup-script) $(setup-options)>nul && set" ] : "\n" ] ;
local diff-vars = [ set.difference $(new-vars) : $(original-vars) ] ;
if $(diff-vars)
{
local target = <new-setup-script>$(replacement) ;
FILE_CONTENTS on $(target) = "SET "$(diff-vars) ;
ALWAYS $(target) ;
msvc.write-setup-script $(target) ;
UPDATE_NOW $(target) : : ignore-minus-n ;
.$(setup-script-id) = $(replacement) ;
result = $(replacement) ;
}
}
else
{
result = $(replacement) ;
}
}
return $(result) ;
}
actions write-setup-script
{
@($(STDOUT):E=$(FILE_CONTENTS:J=$(.nl))) > "$(<)"
}
# Worker rule for toolset version configuration. Takes an explicit version id or
# nothing in case it should configure the default toolset version (the first
@@ -931,7 +992,9 @@ local rule configure-really ( version ? : options * )
# Append setup options to the setup name and add the final setup
# prefix & suffix.
setup-options ?= "" ;
setup-$(c) = $(setup-prefix)$(setup-$(c):J=" ")" "$(setup-options:J=" ")$(setup-suffix) ;
local rewrite = [ feature.get-values <rewrite-setup-scripts> : $(options) ] ;
setup-$(c) = [ maybe-rewrite-setup $(setup-$(c):J=" ") : $(setup-options:J=" ") : $(version) : $(rewrite) ] ;
setup-$(c) = $(setup-prefix)$(setup-$(c))$(setup-suffix) ;
}
}