Author: rfm
Date: Fri Mar 18 13:40:08 2016
New Revision: 39564

URL: http://svn.gna.org/viewcvs/gnustep?rev=39564&view=rev
Log:
User defaults changes to cope better with slow systems

Modified:
    libs/base/trunk/ChangeLog
    libs/base/trunk/Source/NSUserDefaults.m

Modified: libs/base/trunk/ChangeLog
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/ChangeLog?rev=39564&r1=39563&r2=39564&view=diff
==============================================================================
--- libs/base/trunk/ChangeLog   (original)
+++ libs/base/trunk/ChangeLog   Fri Mar 18 13:40:08 2016
@@ -1,3 +1,8 @@
+2016-03-18  Richard Frith-Macdonald <[email protected]>
+
+       * Source/NSUserDefaults.m: Wait longer for locks on slow systems
+       Improve tracking of changes in persistent domains.
+
 2016-03-17  Richard Frith-Macdonald <[email protected]>
 
         * Headers/GNUstepBase/GSXML.h:
@@ -19,7 +24,7 @@
 
 2016-03-14  Richard Frith-Macdonald <[email protected]>
 
-       * 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 <[email protected]>

Modified: libs/base/trunk/Source/NSUserDefaults.m
URL: 
http://svn.gna.org/viewcvs/gnustep/libs/base/trunk/Source/NSUserDefaults.m?rev=39564&r1=39563&r2=39564&view=diff
==============================================================================
--- libs/base/trunk/Source/NSUserDefaults.m     (original)
+++ libs/base/trunk/Source/NSUserDefaults.m     Fri Mar 18 13:40:08 2016
@@ -116,19 +116,23 @@
 @interface     GSPersistentDomain : NSObject
 {
   NSString             *name;
+  NSString             *path;
   NSUserDefaults       *owner;
-  NSDate               *updated;
-@public
-  BOOL                 modified;
   NSMutableDictionary  *contents;
-}
-- (NSMutableDictionary*) contents;
+  NSMutableSet          *added;
+  NSMutableSet          *modified;
+  NSMutableSet          *removed;
+  BOOL                  loaded;
+}
+- (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 @@
           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 @@
 
       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 @@
           [_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 @@
       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 @@
              pd = (*pImp)(_persDomains, objectForKeySel, obj);
              if (nil != pd)
                {
-                 dict = pd->contents;
+                 dict = [pd contents];
                }
              else
                {
@@ -2477,9 +2472,9 @@
 
 @implementation        GSPersistentDomain
 
-- (NSMutableDictionary*) contents
-{
-  if (nil == updated)
+- (NSDictionary*) contents
+{
+  if (NO == loaded)
     {
       [self synchronize];
     }
@@ -2488,10 +2483,33 @@
 
 - (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
@@ -2499,9 +2517,14 @@
 {
   if (nil != (self = [super init]))
     {
+      owner = o;       // Not retained
       name = [n copy];
-      owner = o;       // Not retained
+      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 @@
   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];
-
-      if (nil == m)
-       {
-         m = [NSMutableDictionary new];
-       }
-      [contents release];
-      contents = m;
-      updated = [NSDate new];
-      modified = YES;
+      NSEnumerator      *e;
+      NSString          *k;
+
+      e = [[contents allKeys] objectEnumerator];
+      while (nil != (k = [e nextObject]))
+       {
+         if ([domain objectForKey: k] == nil)
+            {
+              [self setObject: nil forKey: k];
+            }
+       }
+      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?
-
-  if (NO == [owner _lockDefaultsFile: &wasLocked])
-    {
-      hadChange = NO;
-      wasLocked = NO;
-    }
-  else
-    {
-      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];
-       }
-    }
-  return hadChange;
-}
-
-- (NSDate*) updated
-{
-  return updated;
+  BOOL  isLocked = NO;
+  BOOL  wasLocked = NO;
+  BOOL  shouldLock = NO;
+  BOOL  defaultsChanged = NO;
+  BOOL  hasLocalChanges = NO;
+
+  if ([removed count] || [added count] || [modified count])
+    {
+      hasLocalChanges = YES;
+    }
+  if (YES == hasLocalChanges && NO == [owner _readOnly])
+    {
+      shouldLock = YES;
+    }
+  if (YES == shouldLock && YES == [owner _lockDefaultsFile: &wasLocked])
+    {
+      isLocked = YES;
+    }
+  NS_DURING
+    {
+      NSFileManager            *mgr;
+      NSMutableDictionary       *disk;
+
+      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


_______________________________________________
Gnustep-cvs mailing list
[email protected]
https://mail.gna.org/listinfo/gnustep-cvs

Reply via email to