File coordination is not mutual exclusive using the same presenter

Originator:graeter
Number:rdar://16325626 Date Originated:
Status:Open Resolved:
Product:OS X Product Version:10.9.2
Classification:Critical Reproducible:Always
 
File Coordination is *not* mutual exlusive, if:

- coordinating the same file
- both coordinators run on different threads simultaneously
- both coordinators are initialized with the same file presenter

This **should** be either fixed or at least documented. Developers may mistakenly rely on mutual exclusion of file access within the same process on different threads.


You can verify this issue using the following example. It spawns two threads simultaneously locking the same file. The expected behavior is a deadlock, since one presenter locks the file forever ("while(1);"). However, due to this bug, no deadlock occurs: The first coordinator will lock the file forever by intend. The second coordinator will still succeed to lock the file, because it uses the same presenter. If you're using 'nil' as presenters, the program works correctly.

------
#import <Foundation/Foundation.h>

@interface MyPresenter : NSObject <NSFilePresenter>
{
	NSURL *_url;
	NSOperationQueue *_queue;
}

- (id)initWithURL:(NSURL *)url;

@end

@implementation MyPresenter

- (id)initWithURL:(NSURL *)url
{
	self = [self init];
	
	if (self) {
		_url = url;
		_queue = [NSOperationQueue new];
		_queue.maxConcurrentOperationCount = 1;
		
		[NSFileCoordinator addFilePresenter: self];
	}
	
	return self;
}

- (NSURL *)presentedItemURL
{
	return _url;
}

- (NSOperationQueue *)presentedItemOperationQueue
{
	return _queue;
}

@end

int main(int argc, const char * argv[])
{
	@autoreleasepool {
		// Create a new file
		NSURL *url = [NSURL fileURLWithPath: @"test.txt"];
		[@"foo" writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:NULL];
		
		// Setup a presenter on it
		MyPresenter *presenter = [[MyPresenter alloc] initWithURL: url];
		
		// Coordinate file access after two seconds. Should block forever, since the file is locked.
		dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
			[[[NSFileCoordinator alloc] initWithFilePresenter:presenter] coordinateWritingItemAtURL:url options:0 error:NULL byAccessor:^(NSURL *newURL) {
				// This should never be reached, since the file is already locked
				NSLog(@"This should never be reached!");
				exit(-1);
			}];
		});

		// Coordinate the file and block it forever. Thus other coordinators should *never* proceed
		[[[NSFileCoordinator alloc] initWithFilePresenter:presenter] coordinateWritingItemAtURL:url options:0 error:NULL byAccessor:^(NSURL *newURL) {
			while(1);
		}];
	}
	
    return 0;
}

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!