From db6c3d7a79bc3f7c50ddb49491fe3f78f0087a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20Ivan=C4=8Di=C4=87?= Date: Tue, 14 Jan 2014 11:14:03 +0100 Subject: [PATCH] Support more than 64 parallel jobs (-j). Windows WaitForMultipleObjects() can wait on max. 64 handles. This limitation is overcome by splitting the handle set into parts which are of adequate size, and spawning a thread which does the waiting. --- src/engine/execnt.c | 120 +++++++++++++++++++++++++++++++++++++++++--- src/engine/jam.h | 2 +- 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/src/engine/execnt.c b/src/engine/execnt.c index ca89bd0f0..89eca9ce6 100644 --- a/src/engine/execnt.c +++ b/src/engine/execnt.c @@ -780,31 +780,137 @@ 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 last_chunk_size; + HANDLE completed_event = INVALID_HANDLE_VALUE; + HANDLE thread_handles[MAX_THREADS]; + 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 which will notify + * threads to stop waiting. Every wait set 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; + if ( last_chunk_size ) + { + /* The last chunk does not have event handle, so add it now. */ + active_handles[ num_active ] = completed_event; + active_procs[ num_active ] = -1; + ++num_active; + ++num_threads; + ++last_chunk_size; + } + + 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 ) + 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_threads, 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 ); + } + 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. */ diff --git a/src/engine/jam.h b/src/engine/jam.h index 86ad0e86b..497a5bfb1 100644 --- a/src/engine/jam.h +++ b/src/engine/jam.h @@ -409,7 +409,7 @@ #define MAXSYM 1024 /* longest symbol in the environment */ #define MAXJPATH 1024 /* longest filename */ -#define MAXJOBS 64 /* internally enforced -j limit */ +#define MAXJOBS 256 /* internally enforced -j limit */ #define MAXARGC 32 /* words in $(JAMSHELL) */ /* Jam private definitions below. */