mirror of
https://github.com/boostorg/build.git
synced 2026-02-13 12:22:17 +00:00
Add "-lx" option which limits the amount of time actions can run (in seconds). Currently only implemented for Windows NT and above.
[SVN r31266]
This commit is contained in:
@@ -24,6 +24,9 @@
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h> /* do the ugly deed */
|
||||
# include <process.h>
|
||||
# if !defined( __BORLANDC__ )
|
||||
# include <tlhelp32.h>
|
||||
# endif
|
||||
|
||||
# if !defined( __BORLANDC__ ) && !defined( OS_OS2 )
|
||||
# define wait my_wait
|
||||
@@ -916,6 +919,107 @@ check_process_exit(
|
||||
return result;
|
||||
}
|
||||
|
||||
static double
|
||||
running_time(HANDLE process)
|
||||
{
|
||||
FILETIME creation, exit, kernel, user, current;
|
||||
if (GetProcessTimes(process, &creation, &exit, &kernel, &user))
|
||||
{
|
||||
/* Compute the elapsed time */
|
||||
GetSystemTimeAsFileTime(¤t);
|
||||
{
|
||||
double delta = filetime_seconds(
|
||||
add_FILETIME( current, negate_FILETIME(creation) )
|
||||
);
|
||||
return delta;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* it's just stupidly silly that one has to do this! */
|
||||
typedef struct PROCESS_BASIC_INFORMATION__ {
|
||||
LONG ExitStatus;
|
||||
PVOID PebBaseAddress;
|
||||
ULONG AffinityMask;
|
||||
LONG BasePriority;
|
||||
ULONG UniqueProcessId;
|
||||
ULONG InheritedFromUniqueProcessId;
|
||||
} PROCESS_BASIC_INFORMATION_;
|
||||
typedef LONG (__stdcall * NtQueryInformationProcess__)(
|
||||
HANDLE ProcessHandle,
|
||||
LONG ProcessInformationClass,
|
||||
PVOID ProcessInformation,
|
||||
ULONG ProcessInformationLength,
|
||||
PULONG ReturnLength);
|
||||
static NtQueryInformationProcess__ NtQueryInformationProcess_ = NULL;
|
||||
static HMODULE NTDLL_ = NULL;
|
||||
DWORD get_process_id(HANDLE process)
|
||||
{
|
||||
PROCESS_BASIC_INFORMATION_ pinfo;
|
||||
if ( ! NtQueryInformationProcess_ )
|
||||
{
|
||||
if ( ! NTDLL_ )
|
||||
{
|
||||
NTDLL_ = GetModuleHandleA("ntdll");
|
||||
}
|
||||
if ( NTDLL_ )
|
||||
{
|
||||
NtQueryInformationProcess_
|
||||
= (NtQueryInformationProcess__)GetProcAddress( NTDLL_,"NtQueryInformationProcess" );
|
||||
}
|
||||
}
|
||||
if ( NtQueryInformationProcess_ )
|
||||
{
|
||||
LONG r = (*NtQueryInformationProcess_)(
|
||||
process,/* ProcessBasicInformation == */ 0,&pinfo,sizeof(PROCESS_BASIC_INFORMATION_),NULL);
|
||||
return pinfo.UniqueProcessId;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* not really optimal, or efficient, but it's easier this way, and it's not
|
||||
like we are going to be killing thousands, or even tens or processes. */
|
||||
static void
|
||||
kill_all(DWORD pid, HANDLE process)
|
||||
{
|
||||
HANDLE process_snapshot_h = INVALID_HANDLE_VALUE;
|
||||
if ( !pid )
|
||||
{
|
||||
pid = get_process_id(process);
|
||||
}
|
||||
process_snapshot_h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
|
||||
|
||||
if (INVALID_HANDLE_VALUE != process_snapshot_h)
|
||||
{
|
||||
BOOL ok = TRUE;
|
||||
PROCESSENTRY32 pinfo;
|
||||
pinfo.dwSize = sizeof(PROCESSENTRY32);
|
||||
for (
|
||||
ok = Process32First(process_snapshot_h,&pinfo);
|
||||
TRUE == ok;
|
||||
ok = Process32Next(process_snapshot_h,&pinfo) )
|
||||
{
|
||||
if (pinfo.th32ParentProcessID == pid)
|
||||
{
|
||||
/* found a child, recurse to kill it and anything else below it */
|
||||
HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pinfo.th32ProcessID);
|
||||
if (NULL != ph)
|
||||
{
|
||||
kill_all(pinfo.th32ProcessID,ph);
|
||||
CloseHandle(ph);
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseHandle(process_snapshot_h);
|
||||
}
|
||||
/* now that the children are all dead, kill the root */
|
||||
TerminateProcess(process,-2);
|
||||
}
|
||||
|
||||
static int
|
||||
my_wait( int *status )
|
||||
{
|
||||
@@ -949,10 +1053,35 @@ my_wait( int *status )
|
||||
return -1;
|
||||
}
|
||||
|
||||
waitcode = WaitForMultipleObjects( num_active,
|
||||
active_handles,
|
||||
FALSE,
|
||||
INFINITE );
|
||||
if ( globs.timeout > 0 )
|
||||
{
|
||||
/* with a timeout we wait for a finish or a timeout, we check every second
|
||||
to see if something timed out */
|
||||
for (waitcode = WAIT_TIMEOUT; waitcode == WAIT_TIMEOUT;)
|
||||
{
|
||||
waitcode = WaitForMultipleObjects( num_active, active_handles, FALSE, 1*1000 /* 1 second */ );
|
||||
if ( waitcode == WAIT_TIMEOUT )
|
||||
{
|
||||
/* check if any jobs have surpassed the maximum run time. */
|
||||
for ( i = 0; i < num_active; ++i )
|
||||
{
|
||||
double t = running_time(active_handles[i]);
|
||||
if ( t > (double)globs.timeout )
|
||||
{
|
||||
/* we have a "runaway" job, kill it */
|
||||
kill_all(0,active_handles[i]);
|
||||
/* indicate the job "finished" so we query its status below */
|
||||
waitcode = WAIT_ABANDONED_0+i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no timeout, so just wait indefinately for something to finish */
|
||||
waitcode = WaitForMultipleObjects( num_active, active_handles, FALSE, INFINITE );
|
||||
}
|
||||
if ( waitcode != WAIT_FAILED )
|
||||
{
|
||||
if ( waitcode >= WAIT_ABANDONED_0
|
||||
|
||||
@@ -146,7 +146,8 @@ struct globs globs = {
|
||||
# else
|
||||
{ 0, 1 }, /* debug ... */
|
||||
# endif
|
||||
0 /* output commands, not run them */
|
||||
0, /* output commands, not run them */
|
||||
0 /* action timeout */
|
||||
} ;
|
||||
|
||||
/* Symbols to be defined as true for use in Jambase */
|
||||
@@ -241,7 +242,7 @@ int main( int argc, char **argv, char **arg_environ )
|
||||
|
||||
argc--, argv++;
|
||||
|
||||
if( getoptions( argc, argv, "-:d:j:f:gs:t:ano:qv", optv ) < 0 )
|
||||
if( getoptions( argc, argv, "-:l:d:j:f:gs:t:ano:qv", optv ) < 0 )
|
||||
{
|
||||
printf( "\nusage: %s [ options ] targets...\n\n", progname );
|
||||
|
||||
@@ -250,6 +251,7 @@ int main( int argc, char **argv, char **arg_environ )
|
||||
printf( "-fx Read x instead of Jambase.\n" );
|
||||
/* printf( "-g Build from newest sources first.\n" ); */
|
||||
printf( "-jx Run up to x shell commands concurrently.\n" );
|
||||
printf( "-lx Limit actions to x number of seconds after which they are stopped.\n" );
|
||||
printf( "-n Don't actually execute the updating actions.\n" );
|
||||
printf( "-ox Write the updating actions to file x.\n" );
|
||||
printf( "-q Quit quickly as soon as a target fails.\n" );
|
||||
@@ -292,6 +294,9 @@ int main( int argc, char **argv, char **arg_environ )
|
||||
if( ( s = getoptval( optv, 'g', 0 ) ) )
|
||||
globs.newestfirst = 1;
|
||||
|
||||
if( ( s = getoptval( optv, 'l', 0 ) ) )
|
||||
globs.timeout = atoi( s );
|
||||
|
||||
/* Turn on/off debugging */
|
||||
|
||||
for( n = 0; s = getoptval( optv, 'd', n ); n++ )
|
||||
@@ -423,7 +428,7 @@ int main( int argc, char **argv, char **arg_environ )
|
||||
{
|
||||
if ( arg_v[n][0] == '-' )
|
||||
{
|
||||
char *f = "-:d:j:f:gs:t:ano:qv";
|
||||
char *f = "-:l:d:j:f:gs:t:ano:qv";
|
||||
for( ; *f; f++ ) if( *f == arg_v[n][1] ) break;
|
||||
if ( f[1] == ':' && arg_v[n][2] == '\0' ) { ++n; }
|
||||
}
|
||||
|
||||
@@ -520,6 +520,7 @@ struct globs {
|
||||
int newestfirst; /* build newest sources first */
|
||||
char debug[DEBUG_MAX];
|
||||
FILE *cmdout; /* print cmds, not run them */
|
||||
long timeout; /* number of seconds to limit actions to, default 0 for no limit. */
|
||||
} ;
|
||||
|
||||
extern struct globs globs;
|
||||
|
||||
Reference in New Issue
Block a user