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!