Skip to content

Instantly share code, notes, and snippets.

@tjw
Created February 7, 2013 01:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tjw/4727528 to your computer and use it in GitHub Desktop.
Save tjw/4727528 to your computer and use it in GitHub Desktop.
Test case for Radar 13167947.
#import <Foundation/Foundation.h>
/*
clang -fobjc-arc -O2 -Wall write-file-by-merging.m -o write-file-by-merging -framework Foundation
echo qq > $HOME/Desktop/qq.txt
open -e $HOME/Desktop/qq.txt
then:
date > /tmp/d; ./write-file-by-merging /tmp/d $HOME/Desktop/qq.txt
TextEdit doesn't show the updated contents.
This is *very* sensitive to timing or filesystem access in the _checkURL() function:
* If either of the #if blocks in _pokeFile() are changed from #if 1 to #if 0, it works.
* If the sleep(1) is uncommented right before the NSFileManager -replaceItemAtURL:... call (inside the coordinator block), it works.
This happens on a variety of machines at Omni, but most are Retina MacBook Pro. System profile attached to Radar.
*/
BOOL _pokeFile(NSURL *fileURL, NSError **outError)
{
#if 1
NSError *error = nil;
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:outError];
if (!attributes) {
NSLog(@"Error getting attributes of %@: %@", fileURL, error);
if (outError)
*outError = error;
return NO;
}
#endif
#if 1
NSData *fileData = [[NSData alloc] initWithContentsOfURL:fileURL options:NSDataReadingMappedIfSafe|NSDataReadingUncached error:outError];
if (!fileData)
abort();
#endif
return YES;
}
static BOOL _checkURL(NSURL *localDocumentURL, NSFileCoordinator *coordinator, NSFileCoordinatorReadingOptions options)
{
__block BOOL success;
__block NSError *error;
[coordinator coordinateReadingItemAtURL:localDocumentURL options:options error:&error byAccessor:
^(NSURL *newReadingURL) {
// We don't actually check anything in this sample app.
success = _pokeFile(newReadingURL, &error);
}];
return success;
}
static BOOL _writeFileByMerging(NSURL *sourceURL, NSURL *destURL, NSError **outError)
{
// We treat 'sourceURL' as if it doesn't need file coordination (it is our private copy).
NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
__block NSError *error;
__block BOOL success = NO;
[coordinator prepareForReadingItemsAtURLs:nil options:0
writingItemsAtURLs:@[destURL] options:0/*don't know what we are going to do yet*/
error:&error byAccessor:
^(void (^completionHandler)(void)){
// Make cleanup easier...
if (!completionHandler)
completionHandler = ^{};
// Do a read of the source to see if it has changed, w/o claiming we'll do any write above.
__block BOOL validateSuccess = NO;
[coordinator coordinateReadingItemAtURL:destURL options:0 error:&error byAccessor:^(NSURL *newURL) {
validateSuccess = _checkURL(destURL, coordinator, 0);
}];
if (!validateSuccess) {
NSLog(@"destination changed");
completionHandler();
return;
}
[coordinator coordinateWritingItemAtURL:destURL options:NSFileCoordinatorWritingForMerging error:&error byAccessor:^(NSURL *newURL) {
//sleep(1);
if (![[NSFileManager defaultManager] replaceItemAtURL:newURL withItemAtURL:sourceURL backupItemName:nil options:0 resultingItemURL:NULL error:&error])
return;
success = YES;
}];
completionHandler();
success = YES;
}];
if (!success && outError)
*outError = error;
return success;
}
int main(int argc, char *argv[])
{
if (argc != 3) {
fprintf(stderr, "usage: %s src dest\n", argv[0]);
exit(1);
}
@autoreleasepool {
NSURL *sourceURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:argv[1]]];
NSURL *destURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:argv[2]]];
NSError *error;
if (!_writeFileByMerging(sourceURL, destURL, &error))
NSLog(@"Error: %@", error);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment