improve wait time for termination

This commit is contained in:
Richard Frith-Macdonald 2019-05-15 13:02:10 +01:00
parent 1f0f34db93
commit d415fd6479
4 changed files with 142 additions and 55 deletions

View file

@ -1,8 +1,15 @@
2019-05-14 Richard Frith-Macdonald <rfm@gnu.org>
* EcProcess.h:
* EcCommand.m: Replace -terminate with -terminate: method.
* Terminate.m: Extend termination to control the time allowed for the
graceful shutdown (default to 30 seconds).
2019-05-14 Richard Frith-Macdonald <rfm@gnu.org>
* EcProcess.h:
* EcCommand.m: New method to return count of active clients.
* terminate.m: New help output and option to wait until Command
* Terminate.m: New help output and option to wait until Command
server shuts down (printing out number of active clients).
2019-05-09 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -400,6 +400,7 @@ static NSMutableDictionary *launchInfo = nil;
NSMutableDictionary *launching;
unsigned pingPosition;
NSTimer *terminating;
NSDate *terminateBy;
NSDate *lastUnanswered;
unsigned fwdSequence;
unsigned revSequence;
@ -462,7 +463,8 @@ static NSMutableDictionary *launchInfo = nil;
transient: (BOOL)t;
- (void) reply: (NSString*) msg to: (NSString*)n from: (NSString*)c;
- (NSArray*) restartAll: (NSString*)from;
- (void) terminate;
- (void) terminate: (NSDate*)by;
- (void) _terminate: (NSTimer*)t;
- (void) timedOut: (NSTimer*)t;
- (void) _timedOut: (NSTimer*)t;
- (void) timeoutSoon;
@ -2087,6 +2089,7 @@ static NSMutableDictionary *launchInfo = nil;
RELEASE(launchOrder);
RELEASE(environment);
RELEASE(lastUnanswered);
RELEASE(terminateBy);
[super dealloc];
}
@ -3302,49 +3305,83 @@ static NSMutableDictionary *launchInfo = nil;
* Tell all our clients to quit, and wait for them to do so.
* If called while already terminating ... force immediate shutdown.
*/
- (void) terminate: (NSTimer*)t
- (void) _terminate: (NSTimer*)t
{
if (nil == terminating)
NSTimeInterval ti = [terminateBy timeIntervalSinceNow];
if ([clients count] == 0 && [launching count] == 0)
{
[self information: @"Handling shutdown.\n"
[self information: @"Final shutdown."
from: nil
to: nil
type: LT_CONSOLE];
[terminating invalidate];
terminating = nil;
[self cmdQuit: tStatus];
}
if (nil == terminating)
else if (ti <= 0.0)
{
terminating = [NSTimer scheduledTimerWithTimeInterval: 10.0
target: self selector: @selector(terminate:)
userInfo: [NSDate new]
repeats: YES];
[[self cmdLogFile: logname] puts: @"Final shutdown.\n"];
[terminating invalidate];
terminating = nil;
[self killAll];
[self cmdQuit: tStatus];
}
[self quitAll];
if (t != nil)
else
{
NSDate *when = (NSDate*)[t userInfo];
if ([when timeIntervalSinceNow] < -30.0)
{
[[self cmdLogFile: logname]
puts: @"Final shutdown.\n"];
[terminating invalidate];
terminating = nil;
[self killAll];
[self cmdQuit: tStatus];
}
[self quitAll];
terminating = [NSTimer scheduledTimerWithTimeInterval: ti
target: self
selector: _cmd
userInfo: nil
repeats: NO];
}
}
- (void) terminate
- (void) terminate: (NSDate*)by
{
NSTimeInterval ti = 30.0;
if (nil != terminateBy)
{
NSString *msg;
msg = [NSString stringWithFormat: @"Terminate requested,"
@" but already terminating by %@", terminateBy];
[self information: msg
from: nil
to: nil
type: LT_CONSOLE];
return;
}
if (nil != by)
{
ti = [by timeIntervalSinceNow];
if (ti < 0.5)
{
ti = 0.5;
by = nil;
}
else if (ti > 900.0)
{
ti = 900.0;
by = nil;
}
}
if (nil == by)
{
by = [NSDate dateWithTimeIntervalSinceNow: ti];
}
ASSIGN(terminateBy, by);
[self information: @"Terminate initiated.\n"
from: nil
to: nil
type: LT_CONSOLE];
[self terminate: nil];
terminating = [NSTimer scheduledTimerWithTimeInterval: 0.01
target: self
selector: @selector(_terminate:)
userInfo: nil
repeats: NO];
}
- (void) timedOut: (NSTimer*)t

View file

@ -139,10 +139,11 @@ typedef enum {
from: (NSString*)c;
/** Shut down the Command server and all its clients.<br />
* Clients which fail to shut down gracefully within 30 seconds
* may be killed.
* Clients which fail to shut down gracefully before the specified timestamp
* will be forcibly killed. The timestamp is constrained to be at least half
* a second in the future and not more than 15 minutes in the future.
*/
- (oneway void) terminate;
- (oneway void) terminate: (NSDate*)byDate;
/** This is meant to be used remotely by all sorts of software running
* on the machine and which is *not* a full Command client (ie, not a

View file

@ -55,6 +55,7 @@ main()
NSString *name;
id proxy;
BOOL any = NO;
int res = 0;
[EcProcess class]; // Force linker to provide library
@ -70,13 +71,21 @@ main()
|| [[[NSProcessInfo processInfo] arguments] containsObject: @"--Help"]
|| [[[NSProcessInfo processInfo] arguments] containsObject: @"--help"])
{
printf("Terminate processes and Command server\n");
printf("Terminate the Command server and its client processes.\n");
printf(" -CommandHost N\tuse alternative Command server host.\n");
printf(" -CommandName N\tuse alternative Command server name.\n");
printf(" -Wait YES\tWait until termination completes.\n");
printf(" -Wait seconds\tWait with completion time limit.\n");
printf(" -WellKnownHostNames '{...}'\tprovide a host name map.\n");
printf("\n");
printf(" By default a 30 second shutdown is requested and the\n");
printf(" command finishes without waiting for it to complete.\n");
printf(" Possible exit statuses are:\n");
printf(" 0 termination requested (completed if -Wait was used).\n");
printf(" 1 termination had not completed by end of -Wait timeout.\n");
printf(" 2 the Command server was not found (maybe not running).\n");
printf(" 3 this help was provided and no termination was requested.\n");
fflush(stdout);
exit(0);
exit(3);
}
dict = [defs dictionaryForKey: @"WellKnownHostNames"];
@ -116,29 +125,53 @@ main()
if (nil == proxy)
{
NSLog(@"Unable to contact %@ on %@", name, host);
res = 2;
}
else
{
NSConnection *c = [proxy connectionForProxy];
unsigned active = [proxy activeCount];
NSTimeInterval seconds = [defs doubleForKey: @"Wait"];
NSDate *by;
[(id<Command>)proxy terminate];
if ([defs boolForKey: @"Wait"])
if (isnan(seconds) || 0.0 == seconds)
{
NS_DURING
by = nil;
}
else if (seconds < 0.5)
{
seconds = 0.5;
}
else if (seconds > 900.0)
{
seconds = 900.0;
}
by = [NSDate dateWithTimeIntervalSinceNow: seconds];
[(id<Command>)proxy terminate: by];
if (nil == [defs objectForKey: @"Wait"])
{
[c invalidate]; // No waiting
}
else
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
/* Allow a second more than the requested shutdown time,
* so minor timing differences do not cause us to report
* the shutdown as having failed.
*/
while ([c isValid] && [by timeIntervalSinceNow] > -1.0)
{
NSConnection *c = [proxy connectionForProxy];
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSDate *delay;
while ([c isValid])
[pool release];
pool = [NSAutoreleasePool new];
delay = [NSDate dateWithTimeIntervalSinceNow: 0.2];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: delay];
if ([c isValid])
{
NSDate *delay;
[pool release];
pool = [NSAutoreleasePool new];
delay = [NSDate dateWithTimeIntervalSinceNow: 0.2];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: delay];
if ([c isValid])
NS_DURING
{
unsigned remaining = [proxy activeCount];
@ -149,17 +182,26 @@ main()
fflush(stdout);
}
}
NS_HANDLER
{
/* An exception could occur if we lost the connection
* while trying to check the active count. In that
* case we can assume the Command server terminated.
*/
[c invalidate];
active = 0;
}
NS_ENDHANDLER
}
[pool release];
}
NS_HANDLER
{
NSLog(@"%@", localException);
}
NS_ENDHANDLER
[pool release];
}
if (YES == [c isValid])
{
res = 1; // Command did not shut down in time.
}
}
RELEASE(arp);
return 0;
return res;
}