Rewrite launching to make the LaunchInfo instances control most of the work and to use individual timers for each instance with the aim of simplifying launch and shutdown logic and alarms

This commit is contained in:
Richard Frith-Macdonald 2020-06-24 13:26:31 +01:00
parent 48d84025c4
commit 9934bbbdf2
7 changed files with 1787 additions and 1100 deletions

View file

@ -51,6 +51,7 @@ inner_main()
defs = [NSDictionary dictionaryWithObjectsAndKeys: defs = [NSDictionary dictionaryWithObjectsAndKeys:
@"NO", @"Daemon", @"NO", @"Daemon",
@"YES", @"Transient",
#if defined(EC_REGISTRATION_DOMAIN) #if defined(EC_REGISTRATION_DOMAIN)
EC_REGISTRATION_DOMAIN EC_REGISTRATION_DOMAIN
#endif #endif

View file

@ -42,34 +42,39 @@
id<CmdClient> theServer; id<CmdClient> theServer;
id obj; /* The proxy object of client. */ id obj; /* The proxy object of client. */
NSString *name; /* The name of the client. */ NSString *name; /* The name of the client. */
NSDate *lastUnanswered; /* Last unanswered ping. */ NSDate *outstanding; /* Time of outstanding ping. */
NSDate *delayed; /* When we had a ping delay. */
NSDate *recovered; /* Time of recovery from delay. */
unsigned fwdSequence; /* Last ping sent TO client. */ unsigned fwdSequence; /* Last ping sent TO client. */
unsigned revSequence; /* Last gnip sent BY client. */ unsigned revSequence; /* Last gnip sent BY client. */
NSMutableSet *files; /* Want update info for these. */ NSMutableSet *files; /* Want update info for these. */
NSData *config; /* Config info for client. */ NSData *config; /* Config info for client. */
BOOL pingOk; /* Can remote end accept ping? */
BOOL transient; /* Is this a transient client? */ BOOL transient; /* Is this a transient client? */
BOOL unregistered; /* Has client unregistered? */ BOOL unregistered; /* Has client unregistered? */
int processIdentifier; /* Process ID if known (or 0). */ int processIdentifier; /* Process ID if known (or 0). */
} }
- (NSComparisonResult) compare: (EcClientI*)other; - (NSComparisonResult) compare: (EcClientI*)other;
- (NSData*) config; - (NSData*) config;
- (NSDate*) delayed;
- (NSMutableSet*) files; - (NSMutableSet*) files;
- (BOOL) gnip: (unsigned)seq; - (BOOL) gnip: (unsigned)seq;
- (id) initFor: (id)obj - (id) initFor: (id)obj
name: (NSString*)n name: (NSString*)n
with: (id<CmdClient>)svr; with: (id<CmdClient>)svr;
- (NSDate*) lastUnanswered; - (NSDate*) outstanding;
- (NSString*) name; - (NSString*) name;
- (id) obj; - (id) obj;
- (void) ping; - (void) ping;
- (int) processIdentifier; - (int) processIdentifier;
- (NSDate*) recovered;
- (void) setConfig: (NSData*)c; - (void) setConfig: (NSData*)c;
- (void) setName: (NSString*)n; - (void) setName: (NSString*)n;
- (void) setObj: (id)o; - (void) setObj: (id)o;
- (void) setProcessIdentifier: (int)p; - (void) setProcessIdentifier: (int)p;
- (void) setServer: (id<CmdClient>)s;
- (void) setTransient: (BOOL)flag; - (void) setTransient: (BOOL)flag;
- (void) setUnregistered: (BOOL)flag; - (void) setUnregistered: (BOOL)flag;
- (BOOL) transient; - (BOOL) transient;
- (BOOL) unregistered;
@end @end

View file

@ -71,7 +71,9 @@
} }
RELEASE(c); RELEASE(c);
} }
DESTROY(lastUnanswered); DESTROY(outstanding);
DESTROY(delayed);
DESTROY(recovered);
DESTROY(config); DESTROY(config);
DESTROY(files); DESTROY(files);
DESTROY(name); DESTROY(name);
@ -79,6 +81,11 @@
[super dealloc]; [super dealloc];
} }
- (NSDate*) delayed
{
return delayed;
}
- (NSMutableSet*) files - (NSMutableSet*) files
{ {
return files; return files;
@ -95,9 +102,16 @@
} }
} }
revSequence = s; revSequence = s;
if (nil == recovered && nil != delayed)
{
/* We were in a sequence of delayed pings, so we need to record
* that we have ended that sequence.
*/
ASSIGN(recovered, [NSDate date]);
}
DESTROY(outstanding);
if (revSequence == fwdSequence) if (revSequence == fwdSequence)
{ {
DESTROY(lastUnanswered);
return YES; /* up to date */ return YES; /* up to date */
} }
else else
@ -113,20 +127,17 @@
self = [super init]; self = [super init];
if (self != nil) if (self != nil)
{ {
theServer = s;
files = [NSMutableSet new]; files = [NSMutableSet new];
[self setObj: o]; [self setObj: o];
[self setName: n]; [self setName: n];
pingOk = [o respondsToSelector: @selector(cmdPing:sequence:extra:)]; [self setServer: s];
if (pingOk == NO)
NSLog(@"Warning - %@ is an old server ... it can't be pinged", n);
} }
return self; return self;
} }
- (NSDate*) lastUnanswered - (NSDate*) outstanding
{ {
return lastUnanswered; return outstanding;
} }
- (NSString*) name - (NSString*) name
@ -141,13 +152,9 @@
- (void) ping - (void) ping
{ {
if (pingOk == NO)
{
return;
}
if (fwdSequence == revSequence) if (fwdSequence == revSequence)
{ {
lastUnanswered = [[NSDate date] retain]; ASSIGN(outstanding, [NSDate date]);
NS_DURING NS_DURING
{ {
[obj cmdPing: theServer sequence: ++fwdSequence extra: nil]; [obj cmdPing: theServer sequence: ++fwdSequence extra: nil];
@ -160,6 +167,23 @@
} }
else else
{ {
if (recovered != nil)
{
/* The connection had recovered from a late ping response,
* but now we have another delayed ping, so we discard the
* information about the previous delay/recovery sequence
* in order to start another.
*/
DESTROY(recovered);
DESTROY(delayed);
}
if (nil == delayed)
{
/* This ping is the first one delayed, so we need to record
* the timestamp at which the delay started.
*/
ASSIGN(delayed, outstanding);
}
NSLog(@"Ping to %@ when one is already in progress.", name); NSLog(@"Ping to %@ when one is already in progress.", name);
} }
} }
@ -169,6 +193,11 @@
return processIdentifier; return processIdentifier;
} }
- (NSDate*) recovered
{
return recovered;
}
- (void) setConfig: (NSData*)c - (void) setConfig: (NSData*)c
{ {
ASSIGN(config, c); ASSIGN(config, c);
@ -189,6 +218,11 @@
processIdentifier = p; processIdentifier = p;
} }
- (void) setServer: (id<CmdClient>)s
{
theServer = s;
}
- (void) setTransient: (BOOL)flag - (void) setTransient: (BOOL)flag
{ {
transient = flag ? YES : NO; transient = flag ? YES : NO;
@ -206,6 +240,11 @@
{ {
return transient; return transient;
} }
- (BOOL) unregistered
{
return unregistered;
}
@end @end

File diff suppressed because it is too large Load diff

View file

@ -2395,7 +2395,7 @@ static NSString* cmdWord(NSArray* a, unsigned int pos)
while (count-- > 0) while (count-- > 0)
{ {
EcClientI *r = [commands objectAtIndex: count]; EcClientI *r = [commands objectAtIndex: count];
NSDate *d = [r lastUnanswered]; NSDate *d = [r outstanding];
if (d != nil && [d timeIntervalSinceDate: now] < -pingDelay) if (d != nil && [d timeIntervalSinceDate: now] < -pingDelay)
{ {
@ -2424,7 +2424,7 @@ static NSString* cmdWord(NSArray* a, unsigned int pos)
while (count-- > 0) while (count-- > 0)
{ {
EcClientI *r = [consoles objectAtIndex: count]; EcClientI *r = [consoles objectAtIndex: count];
NSDate *d = [r lastUnanswered]; NSDate *d = [r outstanding];
if (d != nil && [d timeIntervalSinceDate: now] < -pingDelay) if (d != nil && [d timeIntervalSinceDate: now] < -pingDelay)
{ {

View file

@ -97,7 +97,7 @@ typedef enum {
@end @end
/** The CmdConfig protocol is needed by objects that send and receive /** The CmdConfig protocol is needed by objects that send and receive
* configuarion information. * configuration information.
*/ */
@protocol CmdConfig @protocol CmdConfig
- (oneway void) requestConfigFor: (id<CmdConfig>)c; - (oneway void) requestConfigFor: (id<CmdConfig>)c;

View file

@ -2779,6 +2779,8 @@ static BOOL ecDidAwaken = NO;
[ecLock lock]; [ecLock lock];
if (0.0 == beganQuitting) if (0.0 == beganQuitting)
{ {
NSLog(@"-[%@ ecQuit: %@ for: %ld]", NSStringFromClass([self class]),
reason, (long)status);
ASSIGN(ecQuitReason, reason); ASSIGN(ecQuitReason, reason);
ecQuitStatus = status; ecQuitStatus = status;
} }
@ -3718,7 +3720,8 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
- (int) ecRun - (int) ecRun
{ {
CREATE_AUTORELEASE_POOL(arp); CREATE_AUTORELEASE_POOL(arp);
NSConnection *c; NSSocketPortNameServer *ns;
NSString *name;
NSRunLoop *loop; NSRunLoop *loop;
NSDate *future; NSDate *future;
@ -3730,17 +3733,14 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
return 1; return 1;
} }
NSAssert(nil == EcProcConnection, NSGenericException); NSAssert(nil != EcProcConnection, NSGenericException);
c = [[NSConnection alloc] initWithReceivePort: (NSPort*)[NSSocketPort port] ns = [NSSocketPortNameServer sharedInstance];
sendPort: nil]; name = [self cmdName];
[c setRootObject: self]; if ([EcProcConnection registerName: name withNameServer: ns] == NO)
if ([c registerName: [self cmdName]
withNameServer: [NSSocketPortNameServer sharedInstance]] == NO)
{ {
EcAlarm *a; EcAlarm *a;
DESTROY(c); DESTROY(EcProcConnection);
NSLog(@"Unable to register with name server. Perhaps a copy of this process is already running (or is hung or blocked waiting for a database query etc), or perhaps an old version was killed and is still registered. Check the state of any running process and and check the process registration with gdomap."); NSLog(@"Unable to register with name server. Perhaps a copy of this process is already running (or is hung or blocked waiting for a database query etc), or perhaps an old version was killed and is still registered. Check the state of any running process and and check the process registration with gdomap.");
a = [EcAlarm alarmForManagedObject: nil a = [EcAlarm alarmForManagedObject: nil
@ -3774,16 +3774,21 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
[self alarm: a]; [self alarm: a];
} }
[c setDelegate: self]; [EcProcConnection setDelegate: self];
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
addObserver: self addObserver: self
selector: @selector(cmdConnectionBecameInvalid:) selector: @selector(cmdConnectionBecameInvalid:)
name: NSConnectionDidDieNotification name: NSConnectionDidDieNotification
object: c]; object: EcProcConnection];
EcProcConnection = c;
[self _connectionRegistered]; [self _connectionRegistered];
/* Now that startup has completed and we are registered under our normal
* name, we should remove the name used during startup.
*/
name = [name stringByAppendingString: @" (starting)"];
[ns removePortForName: name];
/* Called to permit subclasses to initialise before entering run loop. /* Called to permit subclasses to initialise before entering run loop.
*/ */
[self ecAwaken]; [self ecAwaken];
@ -5232,6 +5237,8 @@ With two parameters ('maximum' and a number),\n\
[ecLock lock]; [ecLock lock];
if (0.0 == beganQuitting) if (0.0 == beganQuitting)
{ {
NSLog(@"-[%@ cmdQuit: %ld]", NSStringFromClass([self class]),
(long)status);
ASSIGN(ecQuitReason, nil); ASSIGN(ecQuitReason, nil);
ecQuitStatus = status; ecQuitStatus = status;
} }
@ -5280,6 +5287,47 @@ With two parameters ('maximum' and a number),\n\
return self; return self;
} }
- (BOOL) ecPrepareUnique
{
NSSocketPortNameServer *ns = [NSSocketPortNameServer sharedInstance];
NSString *name = [self cmdName];
NSString *prep = [name stringByAppendingString: @" (starting)"];
NSPort *p;
/* First try registering as a non-functional process using our unique
* name with th suffix '(starting)' to prevent other instances trying
* to start up at the same time.
*/
p = (NSPort*)[NSSocketPort port];
EcProcConnection = [[NSConnection alloc] initWithReceivePort: p
sendPort: nil];
[EcProcConnection setRootObject: self];
if ([EcProcConnection registerName: prep withNameServer: ns] == NO)
{
p = [ns portForName: prep onHost: @""];
DESTROY(EcProcConnection);
NSLog(@"There is already a process: %@, on %@", prep, p);
return NO;
}
/* Now check to see if there is an instance already running.
*/
p = [ns portForName: name onHost: @""];
if (nil != p)
{
[ns removePortForName: prep];
DESTROY(EcProcConnection);
NSLog(@"There is already a process: %@, on %@", name, p);
return NO;
}
/* Yippee ... there is no other copy of this process running and we have
* grabbed the name of a process starting up, so no other process can
* conflict with our startup.
*/
return YES;
}
- (id) initWithDefaults: (NSDictionary*) defs - (id) initWithDefaults: (NSDictionary*) defs
{ {
[ecLock lock]; [ecLock lock];
@ -5386,6 +5434,17 @@ With two parameters ('maximum' and a number),\n\
return nil; return nil;
} }
cmdIsTransient = [cmdDefs boolForKey: @"Transient"];
if (NO == cmdIsTransient)
{
if (NO == [self ecPrepareUnique])
{
RELEASE(self);
[ecLock unlock];
return nil;
}
}
for (i = 0; i < 32; i++) for (i = 0; i < 32; i++)
{ {
switch (i) switch (i)
@ -5457,6 +5516,7 @@ With two parameters ('maximum' and a number),\n\
hdl = [self cmdLogFile: cmdDebugName]; hdl = [self cmdLogFile: cmdDebugName];
if (hdl == nil) if (hdl == nil)
{ {
DESTROY(EcProcConnection);
[ecLock unlock]; [ecLock unlock];
exit(1); exit(1);
} }
@ -5478,8 +5538,6 @@ With two parameters ('maximum' and a number),\n\
[self cmdDefaultsChanged: nil]; [self cmdDefaultsChanged: nil];
cmdIsTransient = [cmdDefs boolForKey: @"Transient"];
if ([cmdDefs objectForKey: @"CmdInterval"] != nil) if ([cmdDefs objectForKey: @"CmdInterval"] != nil)
{ {
[self setCmdInterval: [cmdDefs floatForKey: @"CmdInterval"]]; [self setCmdInterval: [cmdDefs floatForKey: @"CmdInterval"]];