User defaults changes to cope better with slow systems

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@39564 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2016-03-18 12:40:08 +00:00
parent 9090e0790f
commit 72b3c03ad3
2 changed files with 250 additions and 119 deletions

View file

@ -1,3 +1,8 @@
2016-03-18 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSUserDefaults.m: Wait longer for locks on slow systems
Improve tracking of changes in persistent domains.
2016-03-17 Richard Frith-Macdonald <rfm@gnu.org>
* Headers/GNUstepBase/GSXML.h:
@ -19,7 +24,7 @@
2016-03-14 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSUserDefaults.m: Log is we break the lock.
* Source/NSUserDefaults.m: Log if we break the lock.
* Source/NSDistributedLock.m: Unlock if dealloc'ed while locked.
2016-03-12 Richard Frith-Macdonald <rfm@gnu.org>

View file

@ -116,19 +116,23 @@ static BOOL flags[GSUserDefaultMaxFlag] = { 0 };
@interface GSPersistentDomain : NSObject
{
NSString *name;
NSString *path;
NSUserDefaults *owner;
NSDate *updated;
@public
BOOL modified;
NSMutableDictionary *contents;
NSMutableSet *added;
NSMutableSet *modified;
NSMutableSet *removed;
BOOL loaded;
}
- (NSMutableDictionary*) contents;
- (NSDictionary*) contents;
- (void) empty;
- (id) initWithName: (NSString*)n
owner: (NSUserDefaults*)o;
- (NSString*) name;
- (void) setContents: (NSDictionary*)domain;
- (id) objectForKey: (NSString*)aKey;
- (BOOL) setObject: (id)anObject forKey: (NSString*)aKey;
- (BOOL) setContents: (NSDictionary*)domain;
- (BOOL) synchronize;
- (NSDate*) updated;
@end
static NSString *
@ -1373,7 +1377,7 @@ newLanguages(NSArray *oldNames)
NSDictionary *td;
pd = (*pImp)(_persDomains, objectForKeySel, dN);
if (pd != nil && (object = [pd->contents objectForKey: defaultName]))
if (pd != nil && (object = [pd objectForKey: defaultName]))
break;
td = (*tImp)(_tempDomains, objectForKeySel, dN);
if (td != nil && (object = [td objectForKey: defaultName]))
@ -1400,12 +1404,8 @@ newLanguages(NSArray *oldNames)
if (nil != pd)
{
id obj = [pd->contents objectForKey: defaultName];
if (nil != obj)
if ([pd setObject: nil forKey: defaultName])
{
pd->modified = YES;
[pd->contents removeObjectForKey: defaultName];
[self _changePersistentDomain: processName];
}
}
@ -1540,9 +1540,10 @@ static BOOL isPlistObject(id o)
[_persDomains setObject: pd forKey: processName];
[pd release];
}
pd->modified = YES;
[pd->contents setObject: value forKey: defaultName];
[self _changePersistentDomain: processName];
if ([pd setObject: value forKey: defaultName])
{
[self _changePersistentDomain: processName];
}
[_lock unlock];
}
NS_HANDLER
@ -1690,14 +1691,8 @@ static BOOL isPlistObject(id o)
pd = [_persDomains objectForKey: domainName];
if (nil != pd)
{
if (YES == [domainName isEqualToString: NSGlobalDomain])
{
/* Don't remove the global domain, just its contents.
*/
[pd->contents removeAllObjects];
pd->modified = YES;
}
else
[pd empty];
if (NO == [domainName isEqualToString: NSGlobalDomain])
{
/* Remove the domain entirely.
*/
@ -2054,7 +2049,7 @@ static BOOL isPlistObject(id o)
pd = (*pImp)(_persDomains, objectForKeySel, obj);
if (nil != pd)
{
dict = pd->contents;
dict = [pd contents];
}
else
{
@ -2477,9 +2472,9 @@ static BOOL isLocked = NO;
@implementation GSPersistentDomain
- (NSMutableDictionary*) contents
- (NSDictionary*) contents
{
if (nil == updated)
if (NO == loaded)
{
[self synchronize];
}
@ -2488,20 +2483,48 @@ static BOOL isLocked = NO;
- (void) dealloc
{
DESTROY(added);
DESTROY(removed);
DESTROY(modified);
DESTROY(contents);
DESTROY(updated);
DESTROY(name);
DESTROY(path);
[super dealloc];
}
- (void) empty
{
if (NO == loaded)
{
[self synchronize];
}
if ([contents count] > 0)
{
NSEnumerator *e;
NSString *k;
e = [[contents allKeys] objectEnumerator];
while (nil != (k = [e nextObject]))
{
[self setObject: nil forKey: k];
}
[self synchronize];
}
}
- (id) initWithName: (NSString*)n
owner: (NSUserDefaults*)o
{
if (nil != (self = [super init]))
{
name = [n copy];
owner = o; // Not retained
name = [n copy];
path = RETAIN([[[owner _directory] stringByAppendingPathComponent: name]
stringByAppendingPathExtension: @"plist"]);
contents = [NSMutableDictionary new];
added = [NSMutableSet new];
removed = [NSMutableSet new];
modified = [NSMutableSet new];
}
return self;
}
@ -2511,113 +2534,216 @@ static BOOL isLocked = NO;
return name;
}
- (void) setContents: (NSDictionary*)domain
- (id) objectForKey: (NSString*)aKey
{
return [contents objectForKey: aKey];
}
- (BOOL) setContents: (NSDictionary*)domain
{
BOOL changed = NO;
if (NO == [contents isEqual: domain])
{
NSMutableDictionary *m = [domain mutableCopy];
NSEnumerator *e;
NSString *k;
if (nil == m)
e = [[contents allKeys] objectEnumerator];
while (nil != (k = [e nextObject]))
{
m = [NSMutableDictionary new];
if ([domain objectForKey: k] == nil)
{
[self setObject: nil forKey: k];
}
}
[contents release];
contents = m;
updated = [NSDate new];
modified = YES;
e = [domain keyEnumerator];
while (nil != (k = [e nextObject]))
{
[self setObject: [domain objectForKey: k] forKey: k];
}
changed = YES;
}
return changed;
}
- (BOOL) setObject: (id)anObject forKey: (NSString*)aKey
{
if (nil == anObject)
{
if (nil == [contents objectForKey: aKey])
{
return NO;
}
if ([added member: aKey])
{
[added removeObject: aKey];
}
else if ([modified member: aKey])
{
[modified removeObject: aKey];
[removed addObject: aKey];
}
else
{
[removed addObject: aKey];
}
[contents removeObjectForKey: aKey];
return YES;
}
else
{
id old = [contents objectForKey: aKey];
if ([anObject isEqual: old])
{
return NO;
}
if ([removed member: aKey])
{
[modified addObject: aKey];
[removed removeObject: aKey];
}
else if (nil == [modified member: aKey] && nil == [added member: aKey])
{
if (nil == old)
{
[added addObject: aKey];
}
else
{
[modified addObject: aKey];
}
}
[contents setObject: anObject forKey: aKey];
return YES;
}
}
- (BOOL) synchronize
{
BOOL wasLocked;
BOOL hadChange = NO; // Have we read a change from disk?
BOOL isLocked = NO;
BOOL wasLocked = NO;
BOOL shouldLock = NO;
BOOL defaultsChanged = NO;
BOOL hasLocalChanges = NO;
if (NO == [owner _lockDefaultsFile: &wasLocked])
if ([removed count] || [added count] || [modified count])
{
hadChange = NO;
wasLocked = NO;
hasLocalChanges = YES;
}
else
if (YES == hasLocalChanges && NO == [owner _readOnly])
{
NSString *path;
path = [[[owner _directory] stringByAppendingPathComponent: name]
stringByAppendingPathExtension: @"plist"];
if (YES == modified && NO == [owner _readOnly])
{
NSDate *mod;
BOOL result;
mod = [NSDate date];
if (0 == [contents count])
{
/* Remove empty defaults dictionary.
*/
result = writeDictionary(nil, path);
}
else
{
/* Write dictionary to file.
*/
result = writeDictionary(contents, path);
}
if (YES == result)
{
ASSIGN(updated, mod);
modified = NO;
}
}
else
{
NSFileManager *mgr = [NSFileManager defaultManager];
NSDate *mod;
/* If the database was modified since the last refresh
* we need to read it.
*/
mod = [[mgr fileAttributesAtPath: path traverseLink: YES]
objectForKey: NSFileModificationDate];
if (nil == updated
|| (nil != mod && [updated laterDate: mod] != updated))
{
ASSIGN(updated, mod);
if (nil != updated)
{
NSData *data;
data = [NSData dataWithContentsOfFile: path];
if (nil != data)
{
id o;
o = [NSPropertyListSerialization
propertyListWithData: data
options: NSPropertyListImmutable
format: 0
error: 0];
if ([o isKindOfClass: [NSDictionary class]])
{
[contents release];
contents = [o mutableCopy];
}
}
}
hadChange = YES;
}
}
if (NO == wasLocked)
{
[owner _unlockDefaultsFile];
}
shouldLock = YES;
}
return hadChange;
}
if (YES == shouldLock && YES == [owner _lockDefaultsFile: &wasLocked])
{
isLocked = YES;
}
NS_DURING
{
NSFileManager *mgr;
NSMutableDictionary *disk;
- (NSDate*) updated
{
return updated;
mgr = [NSFileManager defaultManager];
disk = nil;
if (YES == [mgr isReadableFileAtPath: path])
{
NSData *data;
data = [NSData dataWithContentsOfFile: path];
if (nil != data)
{
id o;
o = [NSPropertyListSerialization
propertyListWithData: data
options: NSPropertyListImmutable
format: 0
error: 0];
if ([o isKindOfClass: [NSDictionary class]])
{
disk = AUTORELEASE([o mutableCopy]);
}
}
}
if (nil == disk)
{
disk = [NSMutableDictionary dictionary];
}
loaded = YES;
if (NO == [contents isEqual: disk])
{
defaultsChanged = YES;
if (YES == hasLocalChanges)
{
NSEnumerator *e;
NSString *k;
e = [removed objectEnumerator];
while (nil != (k = [e nextObject]))
{
[disk removeObjectForKey: k];
}
e = [added objectEnumerator];
while (nil != (k = [e nextObject]))
{
[disk setObject: [contents objectForKey: k] forKey: k];
}
e = [modified objectEnumerator];
while (nil != (k = [e nextObject]))
{
[disk setObject: [contents objectForKey: k] forKey: k];
}
}
ASSIGN(contents, disk);
}
if (YES == hasLocalChanges)
{
BOOL written = NO;
if (NO == [owner _readOnly])
{
if (YES == isLocked)
{
if (0 == [contents count])
{
/* Remove empty defaults dictionary.
*/
written = writeDictionary(nil, path);
}
else
{
/* Write dictionary to file.
*/
written = writeDictionary(contents, path);
}
}
}
if (YES == written)
{
[added removeAllObjects];
[removed removeAllObjects];
[modified removeAllObjects];
}
}
if (YES == isLocked && NO == wasLocked)
{
isLocked = NO;
[owner _unlockDefaultsFile];
}
}
NS_HANDLER
{
fprintf(stderr, "problem synchronising defaults domain '%s': %s\n",
[name UTF8String], [[localException description] UTF8String]);
if (YES == isLocked && NO == wasLocked)
{
[owner _unlockDefaultsFile];
}
}
NS_ENDHANDLER
return defaultsChanged;
}
@end