2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 /* CFXMLPreferencesDomain.c
24 Copyright 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Chris Parker
28 #if !defined(__WIN32__)
30 #include <CoreFoundation/CFPreferences.h>
31 #include <CoreFoundation/CFURLAccess.h>
32 #include <CoreFoundation/CFPropertyList.h>
33 #include <CoreFoundation/CFNumber.h>
34 #include <CoreFoundation/CFDate.h>
35 #include "CFInternal.h"
40 #include <mach/mach.h>
41 #include <mach/mach_syscalls.h>
43 Boolean
__CFPreferencesShouldWriteXML(void);
46 CFMutableDictionaryRef _domainDict
; // Current value of the domain dictionary
47 CFMutableArrayRef _dirtyKeys
; // The array of keys which must be synchronized
48 CFAbsoluteTime _lastReadTime
; // The last time we synchronized with the disk
49 CFSpinLock_t _lock
; // Lock for accessing fields in the domain
50 Boolean _isWorldReadable
; // HACK - this is because we have no good way to propogate the kCFPreferencesAnyUser information from the upper level CFPreferences routines REW, 1/13/00
52 } _CFXMLPreferencesDomain
;
54 static void *createXMLDomain(CFAllocatorRef allocator
, CFTypeRef context
);
55 static void freeXMLDomain(CFAllocatorRef allocator
, CFTypeRef context
, void *tDomain
);
56 static CFTypeRef
fetchXMLValue(CFTypeRef context
, void *xmlDomain
, CFStringRef key
);
57 static void writeXMLValue(CFTypeRef context
, void *xmlDomain
, CFStringRef key
, CFTypeRef value
);
58 static Boolean
synchronizeXMLDomain(CFTypeRef context
, void *xmlDomain
);
59 static void getXMLKeysAndValues(CFAllocatorRef alloc
, CFTypeRef context
, void *xmlDomain
, void **buf
[], CFIndex
*numKeyValuePairs
);
60 static CFDictionaryRef
copyXMLDomainDictionary(CFTypeRef context
, void *domain
);
61 static void setXMLDomainIsWorldReadable(CFTypeRef context
, void *domain
, Boolean isWorldReadable
);
63 __private_extern__
const _CFPreferencesDomainCallBacks __kCFXMLPropertyListDomainCallBacks
= {createXMLDomain
, freeXMLDomain
, fetchXMLValue
, writeXMLValue
, synchronizeXMLDomain
, getXMLKeysAndValues
, copyXMLDomainDictionary
, setXMLDomainIsWorldReadable
};
65 // Directly ripped from Foundation....
66 static void __CFMilliSleep(uint32_t msecs
) {
67 #if defined(__WIN32__)
68 SleepEx(msecs
, false);
69 #elif defined(__svr4__) || defined(__hpux__)
70 sleep((msecs
+ 900) / 1000);
71 #elif defined(__MACH__)
72 struct timespec input
;
73 input
.tv_sec
= msecs
/ 1000;
74 input
.tv_nsec
= (msecs
- input
.tv_sec
* 1000) * 1000000;
75 nanosleep(&input
, NULL
);
77 #error Dont know how to define sleep for this platform
81 static CFSpinLock_t _propDictLock
= 0; // Annoying that we need this, but otherwise we have a multithreading risk
83 CF_INLINE CFDictionaryRef
URLPropertyDictForPOSIXMode(SInt32 mode
) {
84 static CFMutableDictionaryRef _propertyDict
= NULL
;
85 CFNumberRef num
= CFNumberCreate(__CFPreferencesAllocator(), kCFNumberSInt32Type
, &mode
);
86 __CFSpinLock(&_propDictLock
);
88 _propertyDict
= CFDictionaryCreateMutable(__CFPreferencesAllocator(), 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
90 CFDictionarySetValue(_propertyDict
, kCFURLFilePOSIXMode
, num
);
95 CF_INLINE
void URLPropertyDictRelease(void) {
96 __CFSpinUnlock(&_propDictLock
);
99 // Asssumes caller already knows the directory doesn't exist.
100 static Boolean
_createDirectory(CFURLRef dirURL
, Boolean worldReadable
) {
101 CFAllocatorRef alloc
= __CFPreferencesAllocator();
102 CFURLRef parentURL
= CFURLCreateCopyDeletingLastPathComponent(alloc
, dirURL
);
103 CFBooleanRef val
= CFURLCreatePropertyFromResource(alloc
, parentURL
, kCFURLFileExists
, NULL
);
104 Boolean parentExists
= (val
&& CFBooleanGetValue(val
));
107 if (val
) CFRelease(val
);
109 CFStringRef path
= CFURLCopyPath(parentURL
);
110 if (!CFEqual(path
, CFSTR("/"))) {
111 _createDirectory(parentURL
, worldReadable
);
112 val
= CFURLCreatePropertyFromResource(alloc
, parentURL
, kCFURLFileExists
, NULL
);
113 parentExists
= (val
&& CFBooleanGetValue(val
));
114 if (val
) CFRelease(val
);
118 if (parentURL
) CFRelease(parentURL
);
119 if (!parentExists
) return false;
121 mode
= worldReadable
? S_IRWXU
|S_IRWXG
|S_IROTH
|S_IXOTH
: S_IRWXU
;
123 result
= CFURLWriteDataAndPropertiesToResource(dirURL
, (CFDataRef
)dirURL
, URLPropertyDictForPOSIXMode(mode
), NULL
);
124 URLPropertyDictRelease();
129 /* XML - context is the CFURL where the property list is stored on disk; domain is an _CFXMLPreferencesDomain */
130 static void *createXMLDomain(CFAllocatorRef allocator
, CFTypeRef context
) {
131 _CFXMLPreferencesDomain
*domain
= CFAllocatorAllocate(allocator
, sizeof(_CFXMLPreferencesDomain
), 0);
132 domain
->_lastReadTime
= 0.0;
133 domain
->_domainDict
= NULL
;
134 domain
->_dirtyKeys
= CFArrayCreateMutable(allocator
, 0, & kCFTypeArrayCallBacks
);
136 domain
->_isWorldReadable
= false;
140 static void freeXMLDomain(CFAllocatorRef allocator
, CFTypeRef context
, void *tDomain
) {
141 _CFXMLPreferencesDomain
*domain
= (_CFXMLPreferencesDomain
*)tDomain
;
142 if (domain
->_domainDict
) CFRelease(domain
->_domainDict
);
143 if (domain
->_dirtyKeys
) CFRelease(domain
->_dirtyKeys
);
144 CFAllocatorDeallocate(allocator
, domain
);
147 // Assumes the domain has already been locked
148 static void _loadXMLDomainIfStale(CFURLRef url
, _CFXMLPreferencesDomain
*domain
) {
149 CFAllocatorRef alloc
= __CFPreferencesAllocator();
151 if (domain
->_domainDict
) {
153 CFAbsoluteTime modTime
;
154 CFURLRef testURL
= url
;
156 if (CFDictionaryGetCount(domain
->_domainDict
) == 0) {
157 // domain never existed; check the parent directory, not the child
158 testURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR(".."), kCFURLPOSIXPathStyle
, true, url
);
161 modDate
= (CFDateRef
)CFURLCreatePropertyFromResource(alloc
, testURL
, kCFURLFileLastModificationTime
, NULL
);
162 modTime
= modDate
? CFDateGetAbsoluteTime(modDate
) : 0.0;
164 // free before possible return. we can test non-NULL of modDate but don't depend on contents after this.
165 if (testURL
!= url
) CFRelease(testURL
);
166 if (modDate
) CFRelease(modDate
);
168 if (modDate
!= NULL
&& modTime
< domain
->_lastReadTime
) { // We're up-to-date
174 // We're out-of-date; destroy domainDict and reload
175 if (domain
->_domainDict
) {
176 CFRelease(domain
->_domainDict
);
177 domain
->_domainDict
= NULL
;
180 // We no longer lock on read; instead, we assume parse failures are because someone else is writing the file, and just try to parse again. If we fail 3 times in a row, we assume the file is corrupted. REW, 7/13/99
182 for (idx
= 0; idx
< 3; idx
++) {
184 if (!CFURLCreateDataAndPropertiesFromResource(alloc
, url
, &data
, NULL
, NULL
, NULL
) || !data
) {
185 // Either a file system error (so we can't read the file), or an empty (or perhaps non-existant) file
186 domain
->_domainDict
= CFDictionaryCreateMutable(alloc
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
189 CFTypeRef pList
= CFPropertyListCreateFromXMLData(alloc
, data
, kCFPropertyListImmutable
, NULL
);
191 if (pList
&& CFGetTypeID(pList
) == CFDictionaryGetTypeID()) {
192 domain
->_domainDict
= CFDictionaryCreateMutableCopy(alloc
, 0, (CFDictionaryRef
)pList
);
198 // Assume the file is being written; sleep for a short time (to allow the write to complete) then re-read
202 if (!domain
->_domainDict
) {
203 // Failed to ever load
204 domain
->_domainDict
= CFDictionaryCreateMutable(alloc
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
206 domain
->_lastReadTime
= CFAbsoluteTimeGetCurrent();
209 static CFTypeRef
fetchXMLValue(CFTypeRef context
, void *xmlDomain
, CFStringRef key
) {
210 _CFXMLPreferencesDomain
*domain
= (_CFXMLPreferencesDomain
*)xmlDomain
;
213 // Never reload if we've looked at the file system within the last 5 seconds.
214 __CFSpinLock(&domain
->_lock
);
215 if (domain
->_domainDict
== NULL
) _loadXMLDomainIfStale((CFURLRef
)context
, domain
);
216 result
= CFDictionaryGetValue(domain
->_domainDict
, key
);
217 if (result
) CFRetain(result
);
218 __CFSpinUnlock(&domain
->_lock
);
224 #if defined(__MACH__)
225 #include <sys/fcntl.h>
227 // appends a unique 8.3 path name to the directory name specified in fn,
228 // atomically determining uniqueness and opening the file. The file
229 // descriptor is returned by reference in the second parameter. 0 is
230 // returned on success, -1 on failure.
231 // We don't currently handle the case where the directory name is very
232 // long and adding an 8.3 name makes the path too long.
233 static int __CFmkstemp83(char *fn
, char *prefix
, int mode
, int *fd
) {
234 static CFSpinLock_t counter_lock
= 0;
235 static unsigned int extension_counter
= 0;
236 int origlen
= strlen(fn
);
237 char idbuf
[6], extbuf
[6], prebuf
[5];
238 uint16_t pid
, origpid
, ext
, origext
;
240 __CFSpinLock(&counter_lock
);
241 ext
= extension_counter
++;
242 if (0xFFF < extension_counter
) extension_counter
= 0;
243 __CFSpinUnlock(&counter_lock
);
247 const char *s2
= prefix
;
249 for (; (*s1
= *s2
) && (n
< 4); s1
++, s2
++, n
++);
252 if (0 < origlen
&& fn
[origlen
- 1] != '/')
254 pid
= getpid() & 0xFFFF;
256 snprintf(idbuf
, 6, "%04x", pid
);
257 snprintf(extbuf
, 6, ".%03x", ext
);
263 *fd
= open(fn
, O_CREAT
|O_EXCL
|O_RDWR
, mode
);
266 if (EEXIST
!= thread_errno())
268 ext
= (ext
+ 1) & 0xFFF;
269 if (origext
== ext
) {
270 // bump the number and start over with extension
271 pid
= (pid
+ 1) & 0xFFFF;
273 return -1; // 2^28 file names tried! errno == EEXIST
274 snprintf(idbuf
, 6, "%04x", pid
);
276 snprintf(extbuf
, 6, ".%03x", ext
);
286 /* __CFWriteBytesToFileWithAtomicity is a "safe save" facility. Write the bytes using the specified mode on the file to the provided URL. If the atomic flag is true, try to do it in a fashion that will enable a safe save.
288 static Boolean
__CFWriteBytesToFileWithAtomicity(CFURLRef url
, const void *bytes
, int length
, SInt32 mode
, Boolean atomic
) {
290 char auxPath
[CFMaxPathSize
+ 16];
291 char cpath
[CFMaxPathSize
];
292 uid_t owner
= getuid();
293 gid_t group
= getgid();
294 Boolean writingFileAsRoot
= ((getuid() != geteuid()) && (geteuid() == 0));
296 if (!CFURLGetFileSystemRepresentation(url
, true, cpath
, CFMaxPathSize
)) {
300 if (-1 == mode
|| writingFileAsRoot
) {
302 if (0 == stat(cpath
, &statBuf
)) {
303 mode
= statBuf
.st_mode
;
304 owner
= statBuf
.st_uid
;
305 group
= statBuf
.st_gid
;
308 if (writingFileAsRoot
&& (0 == strncmp(cpath
, "/Library/Preferences", 20))) {
316 CFURLRef dir
= CFURLCreateCopyDeletingLastPathComponent(NULL
, url
);
317 CFURLRef tempFile
= CFURLCreateCopyAppendingPathComponent(NULL
, dir
, CFSTR("cf#XXXXX"), false);
319 if (!CFURLGetFileSystemRepresentation(tempFile
, true, auxPath
, CFMaxPathSize
)) {
324 fd
= mkstemp(auxPath
);
326 fd
= open(cpath
, O_WRONLY
|O_CREAT
|O_TRUNC
, mode
);
329 if (fd
< 0) return false;
331 if (length
&& (write(fd
, bytes
, length
) != length
|| fsync(fd
) < 0)) {
332 int saveerr
= thread_errno();
336 thread_set_errno(saveerr
);
343 // preserve the mode as passed in originally
344 chmod(auxPath
, mode
);
346 if (0 != rename(auxPath
, cpath
)) {
351 // If the file was renamed successfully and we wrote it as root we need to reset the owner & group as they were.
352 if (writingFileAsRoot
) {
353 chown(cpath
, owner
, group
);
360 // domain should already be locked.
361 static Boolean
_writeXMLFile(CFURLRef url
, CFMutableDictionaryRef dict
, Boolean isWorldReadable
, Boolean
*tryAgain
) {
362 Boolean success
= false;
363 CFAllocatorRef alloc
= __CFPreferencesAllocator();
365 if (CFDictionaryGetCount(dict
) == 0) {
367 CFBooleanRef val
= CFURLCreatePropertyFromResource(alloc
, url
, kCFURLFileExists
, NULL
);
368 if (val
&& CFBooleanGetValue(val
)) {
369 success
= CFURLDestroyResource(url
, NULL
);
373 if (val
) CFRelease(val
);
375 CFPropertyListFormat desiredFormat
= __CFPreferencesShouldWriteXML() ? kCFPropertyListXMLFormat_v1_0
: kCFPropertyListBinaryFormat_v1_0
;
376 CFWriteStreamRef binStream
= CFWriteStreamCreateWithAllocatedBuffers(alloc
, alloc
);
377 CFWriteStreamOpen(binStream
);
378 CFPropertyListWriteToStream(dict
, binStream
, desiredFormat
, NULL
);
379 CFWriteStreamClose(binStream
);
380 CFDataRef data
= CFWriteStreamCopyProperty(binStream
, kCFStreamPropertyDataWritten
);
381 CFRelease(binStream
);
384 mode
= isWorldReadable
? S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IROTH
: S_IRUSR
|S_IWUSR
;
385 #if 1 && defined(__MACH__)
386 { // Try quick atomic way first, then fallback to slower ways and error cases
387 CFStringRef scheme
= CFURLCopyScheme(url
);
392 } else if (CFStringCompare(scheme
, CFSTR("file"), 0) == kCFCompareEqualTo
) {
393 SInt32 length
= CFDataGetLength(data
);
394 const void *bytes
= (0 == length
) ? (const void *)"" : CFDataGetBytePtr(data
);
395 Boolean atomicWriteSuccess
= __CFWriteBytesToFileWithAtomicity(url
, bytes
, length
, mode
, true);
396 if (atomicWriteSuccess
) {
402 if (!atomicWriteSuccess
&& thread_errno() == ENOSPC
) {
412 success
= CFURLWriteDataAndPropertiesToResource(url
, data
, URLPropertyDictForPOSIXMode(mode
), NULL
);
413 URLPropertyDictRelease();
416 if (!CFURLCreateDataAndPropertiesFromResource(alloc
, url
, &readData
, NULL
, NULL
, NULL
) || !CFEqual(readData
, data
)) {
420 if (readData
) CFRelease(readData
);
422 CFBooleanRef val
= CFURLCreatePropertyFromResource(alloc
, url
, kCFURLFileExists
, NULL
);
423 if (!val
|| !CFBooleanGetValue(val
)) {
424 CFURLRef tmpURL
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("."), kCFURLPOSIXPathStyle
, true, url
); // Just "." because url is not a directory URL
425 CFURLRef parentURL
= tmpURL
? CFURLCopyAbsoluteURL(tmpURL
) : NULL
;
426 if (tmpURL
) CFRelease(tmpURL
);
427 if (val
) CFRelease(val
);
428 val
= CFURLCreatePropertyFromResource(alloc
, parentURL
, kCFURLFileExists
, NULL
);
429 if ((!val
|| !CFBooleanGetValue(val
)) && _createDirectory(parentURL
, isWorldReadable
)) {
430 // parent directory didn't exist; now it does; try again to write
431 success
= CFURLWriteDataAndPropertiesToResource(url
, data
, URLPropertyDictForPOSIXMode(mode
), NULL
);
432 URLPropertyDictRelease();
435 if (!CFURLCreateDataAndPropertiesFromResource(alloc
, url
, &rdData
, NULL
, NULL
, NULL
) || !CFEqual(rdData
, data
)) {
439 if (rdData
) CFRelease(rdData
);
443 if (parentURL
) CFRelease(parentURL
);
445 if (val
) CFRelease(val
);
449 // ??? This should never happen
450 CFLog(__kCFLogAssertion
, CFSTR("Could not generate XML data for property list"));
457 static void writeXMLValue(CFTypeRef context
, void *xmlDomain
, CFStringRef key
, CFTypeRef value
) {
458 _CFXMLPreferencesDomain
*domain
= (_CFXMLPreferencesDomain
*)xmlDomain
;
459 const void *existing
= NULL
;
461 __CFSpinLock(&domain
->_lock
);
462 if (domain
->_domainDict
== NULL
) {
463 _loadXMLDomainIfStale((CFURLRef
)context
, domain
);
466 // check to see if the value is the same
467 // if (1) the key is present AND value is !NULL and equal to existing, do nothing, or
468 // if (2) the key is not present AND value is NULL, do nothing
469 // these things are no-ops, and should not dirty the domain
470 if (CFDictionaryGetValueIfPresent(domain
->_domainDict
, key
, &existing
)) {
471 if (NULL
!= value
&& (existing
== value
|| CFEqual(existing
, value
))) {
472 __CFSpinUnlock(&domain
->_lock
);
477 __CFSpinUnlock(&domain
->_lock
);
482 // We must append first so key gets another retain (in case we're
483 // about to remove it from the dictionary, and that's the sole reference)
484 // This should be a set not an array.
485 if (!CFArrayContainsValue(domain
->_dirtyKeys
, CFRangeMake(0, CFArrayGetCount(domain
->_dirtyKeys
)), key
)) {
486 CFArrayAppendValue(domain
->_dirtyKeys
, key
);
489 // Must copy for two reasons - we don't want mutable objects in the cache, and we don't want objects allocated from a different allocator in the cache.
490 CFTypeRef newValue
= CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), value
, kCFPropertyListImmutable
);
491 CFDictionarySetValue(domain
->_domainDict
, key
, newValue
);
494 CFDictionaryRemoveValue(domain
->_domainDict
, key
);
496 __CFSpinUnlock(&domain
->_lock
);
499 static void getXMLKeysAndValues(CFAllocatorRef alloc
, CFTypeRef context
, void *xmlDomain
, void **buf
[], CFIndex
*numKeyValuePairs
) {
500 _CFXMLPreferencesDomain
*domain
= (_CFXMLPreferencesDomain
*)xmlDomain
;
502 __CFSpinLock(&domain
->_lock
);
503 if (!domain
->_domainDict
) {
504 _loadXMLDomainIfStale((CFURLRef
)context
, domain
);
506 count
= CFDictionaryGetCount(domain
->_domainDict
);
509 if (count
<= *numKeyValuePairs
) {
510 values
= *buf
+ count
;
511 CFDictionaryGetKeysAndValues(domain
->_domainDict
, (const void **)*buf
, (const void **)values
);
512 } else if (alloc
!= kCFAllocatorNull
) {
513 *buf
= CFAllocatorReallocate(alloc
, (*buf
? *buf
: NULL
), count
* 2 * sizeof(void *), 0);
515 values
= *buf
+ count
;
516 CFDictionaryGetKeysAndValues(domain
->_domainDict
, (const void **)*buf
, (const void **)values
);
520 *numKeyValuePairs
= count
;
521 __CFSpinUnlock(&domain
->_lock
);
524 static CFDictionaryRef
copyXMLDomainDictionary(CFTypeRef context
, void *xmlDomain
) {
525 _CFXMLPreferencesDomain
*domain
= (_CFXMLPreferencesDomain
*)xmlDomain
;
526 CFDictionaryRef result
;
528 __CFSpinLock(&domain
->_lock
);
529 if(!domain
->_domainDict
) {
530 _loadXMLDomainIfStale((CFURLRef
)context
, domain
);
533 result
= (CFDictionaryRef
)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), domain
->_domainDict
, kCFPropertyListImmutable
);
535 __CFSpinUnlock(&domain
->_lock
);
540 static void setXMLDomainIsWorldReadable(CFTypeRef context
, void *domain
, Boolean isWorldReadable
) {
541 ((_CFXMLPreferencesDomain
*)domain
)->_isWorldReadable
= isWorldReadable
;
544 static Boolean
synchronizeXMLDomain(CFTypeRef context
, void *xmlDomain
) {
545 _CFXMLPreferencesDomain
*domain
= (_CFXMLPreferencesDomain
*)xmlDomain
;
546 CFMutableDictionaryRef cachedDict
;
547 CFMutableArrayRef changedKeys
;
549 Boolean success
, tryAgain
;
551 __CFSpinLock(&domain
->_lock
);
552 cachedDict
= domain
->_domainDict
;
553 changedKeys
= domain
->_dirtyKeys
;
554 count
= CFArrayGetCount(changedKeys
);
557 // no changes were made to this domain; just remove it from the cache to guarantee it will be taken from disk next access
559 CFRelease(cachedDict
);
560 domain
->_domainDict
= NULL
;
562 __CFSpinUnlock(&domain
->_lock
);
566 domain
->_domainDict
= NULL
; // This forces a reload. Note that we now have a retain on cachedDict
568 _loadXMLDomainIfStale((CFURLRef
)context
, domain
);
569 // now cachedDict holds our changes; domain->_domainDict has the latest version from the disk
570 for (idx
= 0; idx
< count
; idx
++) {
571 CFStringRef key
= CFArrayGetValueAtIndex(changedKeys
, idx
);
572 CFTypeRef value
= CFDictionaryGetValue(cachedDict
, key
);
574 CFDictionarySetValue(domain
->_domainDict
, key
, value
);
576 CFDictionaryRemoveValue(domain
->_domainDict
, key
);
578 success
= _writeXMLFile((CFURLRef
)context
, domain
->_domainDict
, domain
->_isWorldReadable
, &tryAgain
);
580 __CFMilliSleep(((__CFReadTSR() & 0xf) + 1) * 50);
583 CFRelease(cachedDict
);
585 CFArrayRemoveAllValues(domain
->_dirtyKeys
);
587 domain
->_lastReadTime
= CFAbsoluteTimeGetCurrent();
588 __CFSpinUnlock(&domain
->_lock
);
592 #endif /* !defined(__WIN32__) */