Revision: 7783
http://skim-app.svn.sourceforge.net/skim-app/?rev=7783&view=rev
Author: hofman
Date: 2012-11-04 23:30:56 +0000 (Sun, 04 Nov 2012)
Log Message:
-----------
Use GCD and async task for ps/dvi conversion
Modified Paths:
--------------
trunk/SKConversionProgressController.h
trunk/SKConversionProgressController.m
Modified: trunk/SKConversionProgressController.h
===================================================================
--- trunk/SKConversionProgressController.h 2012-11-04 01:32:27 UTC (rev
7782)
+++ trunk/SKConversionProgressController.h 2012-11-04 23:30:56 UTC (rev
7783)
@@ -45,8 +45,10 @@
NSTextField *textField;
NSString *fileType;
CGPSConverterRef converter;
- volatile int32_t convertingPS;
- volatile int32_t cancelled;
+ NSTask *task;
+ NSString *outputFile;
+ NSData *outputData;
+ BOOL cancelled;
}
@property (nonatomic, retain) IBOutlet NSButton *cancelButton;
Modified: trunk/SKConversionProgressController.m
===================================================================
--- trunk/SKConversionProgressController.m 2012-11-04 01:32:27 UTC (rev
7782)
+++ trunk/SKConversionProgressController.m 2012-11-04 23:30:56 UTC (rev
7783)
@@ -80,15 +80,21 @@
static void PSConverterBeginDocumentCallback(void *info)
{
id delegate = (id)info;
- if ([delegate respondsToSelector:@selector(conversionStarted)])
- [delegate performSelectorOnMainThread:@selector(conversionStarted)
withObject:nil waitUntilDone:NO];
+ if ([delegate respondsToSelector:@selector(conversionStarted)]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [delegate conversionStarted];
+ });
+ }
}
static void PSConverterEndDocumentCallback(void *info, bool success)
{
id delegate = (id)info;
- if ([delegate respondsToSelector:@selector(conversionCompleted)])
- [delegate performSelectorOnMainThread:@selector(conversionCompleted)
withObject:nil waitUntilDone:NO];
+ if ([delegate respondsToSelector:@selector(conversionCompleted)]) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [delegate conversionCompleted];
+ });
+ }
}
CGPSConverterCallbacks SKPSConverterCallbacks = {
@@ -132,6 +138,9 @@
- (void)dealloc {
SKCFDESTROY(converter);
+ SKDESTROY(outputFile);
+ SKDESTROY(outputData);
+ SKDESTROY(task);
SKDESTROY(cancelButton);
SKDESTROY(progressBar);
SKDESTROY(textField);
@@ -150,30 +159,26 @@
- (IBAction)close:(id)sender { [self close]; }
- (IBAction)cancel:(id)sender {
- OSMemoryBarrier();
- if (cancelled == 1) {
+ if (cancelled == YES) {
[self converterWasStopped];
} else {
- OSAtomicCompareAndSwap32Barrier(0, 1, (int32_t *)&cancelled);
- if (convertingPS)
+ cancelled = YES;
+ if (converter)
CGPSConverterAbort(converter);
+ else if ([task isRunning])
+ [task terminate];
}
}
-- (NSInteger)runModalSelector:(SEL)selector withObject:(NSDictionary *)info {
+- (NSInteger)runModalBlock:(void(^)(void))block {
NSModalSession session = [NSApp beginModalSessionForWindow:[self window]];
- BOOL didDetach = NO;
NSInteger rv = 0;
+ // we run this inside the modal session since the thread could end before
runModalForWindow starts
+ block();
+
while (YES) {
-
- // we run this inside the modal session since the thread could end
before runModalForWindow starts
- if (NO == didDetach) {
- [NSThread detachNewThreadSelector:selector toTarget:self
withObject:info];
- didDetach = YES;
- }
-
rv = [NSApp runModalSession:session];
if (rv != NSRunContinuesResponse)
break;
@@ -186,13 +191,6 @@
return rv;
}
-
-- (void)stopModalOnMainThread:(BOOL)success {
- NSInteger val = (success ? SKConversionSucceeded : SKConversionFailed);
- NSInvocation *invocation = [NSInvocation invocationWithTarget:NSApp
selector:@selector(stopModalWithCode:)];
- [invocation setArgument:&val atIndex:2];
- [invocation performSelectorOnMainThread:@selector(invoke) withObject:nil
waitUntilDone:NO];
-}
- (void)converterWasStopped {
NSBeep();
@@ -217,60 +215,52 @@
SKAutoSizeButtons([NSArray arrayWithObjects:cancelButton, nil], YES);
}
-- (BOOL)shouldKeepRunning {
- OSMemoryBarrier();
- return cancelled == 0;
-}
-
#pragma mark PostScript
-- (void)doPSConversionWithInfo:(NSDictionary *)info {
- NSAutoreleasePool *pool = [NSAutoreleasePool new];
+- (void)convertPostScriptData:(NSData *)psData {
+ // pass self as info
+ converter = CGPSConverterCreate((void *)self, &SKPSConverterCallbacks,
NULL);
+ NSAssert(converter != NULL, @"unable to create PS converter");
- CGDataProviderRef provider = (void *)[info objectForKey:PROVIDER_KEY];
- CGDataConsumerRef consumer = (void *)[info objectForKey:CONSUMER_KEY];
- Boolean success = CGPSConverterConvert(converter, provider, consumer,
NULL);
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0), ^{
+ CFMutableDataRef pdfData =
CFDataCreateMutable(CFGetAllocator((CFDataRef)psData), 0);
+ CGDataProviderRef provider =
CGDataProviderCreateWithCFData((CFDataRef)psData);
+ CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData(pdfData);
+
+ Boolean success = CGPSConverterConvert(converter, provider, consumer,
NULL);
+
+ if (success)
+ outputData = [(NSData *)pdfData copy];
+
+ CGDataProviderRelease(provider);
+ CGDataConsumerRelease(consumer);
+ CFRelease(pdfData);
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [NSApp stopModalWithCode:success ? SKConversionSucceeded :
SKConversionFailed];
+ });
+ });
- [self stopModalOnMainThread:success];
-
- [pool release];
-}
+}
- (NSData *)newPDFDataWithPostScriptData:(NSData *)psData error:(NSError
**)outError {
NSAssert(NULL == converter, @"attempted to reenter
SKConversionProgressController, but this is not supported");
fileType = SKPostScriptDocumentType;
+ cancelled = NO;
- convertingPS = 1;
- cancelled = 0;
+ NSInteger rv = [self runModalBlock:^{
+ [self convertPostScriptData:psData];
+ }];
- // pass self as info
- converter = CGPSConverterCreate((void *)self, &SKPSConverterCallbacks,
NULL);
- NSAssert(converter != NULL, @"unable to create PS converter");
-
- CGDataProviderRef provider =
CGDataProviderCreateWithCFData((CFDataRef)psData);
-
- CFMutableDataRef pdfData =
CFDataCreateMutable(CFGetAllocator((CFDataRef)psData), 0);
- CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData(pdfData);
-
- NSDictionary *dictionary = [NSDictionary
dictionaryWithObjectsAndKeys:(id)provider, PROVIDER_KEY, (id)consumer,
CONSUMER_KEY, nil];
-
- NSInteger rv = [self runModalSelector:@selector(doPSConversionWithInfo:)
withObject:dictionary];
-
- CGDataProviderRelease(provider);
- CGDataConsumerRelease(consumer);
-
- if (rv != SKConversionSucceeded) {
- SKCFDESTROY(pdfData);
- if (outError) {
- if ([self shouldKeepRunning])
- *outError = [NSError
readFileErrorWithLocalizedDescription:NSLocalizedString(@"Unable to load file",
@"Error description")];
- else
- *outError = [NSError
userCancelledErrorWithUnderlyingError:nil];
- }
+ if (rv != SKConversionSucceeded && outError) {
+ if (cancelled)
+ *outError = [NSError userCancelledErrorWithUnderlyingError:nil];
+ else
+ *outError = [NSError
readFileErrorWithLocalizedDescription:NSLocalizedString(@"Unable to load file",
@"Error description")];
}
- return (NSData *)pdfData;
+ return [outputData retain];
}
#pragma mark DVI and XDV
@@ -319,96 +309,77 @@
return xdvToolPath;
}
-- (void)doDVIConversionWithInfo:(NSDictionary *)info {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+- (void)taskFinished:(NSNotification *)notification {
+ NSData *outData = nil;
+ BOOL success = [[notification object] terminationStatus] == 0 &&
+ [[NSFileManager defaultManager]
fileExistsAtPath:outputFile] &&
+ cancelled == NO;
- NSFileManager *fm = [[[NSFileManager alloc] init] autorelease];
- NSURL *dviURL = [info objectForKey:INPUTURL_KEY];
- NSString *dviFile = [dviURL path];
- NSString *commandPath = [info objectForKey:TOOLPATH_KEY];
- NSString *commandName = [commandPath lastPathComponent];
- NSURL *tmpDirURL = [fm URLForDirectory:NSItemReplacementDirectory
inDomain:NSUserDomainMask appropriateForURL:dviURL create:YES error:NULL];
- BOOL outputPS = [commandName isEqualToString:@"dvips"];
- NSString *outFile = [[tmpDirURL URLByAppendingPathComponent:[dviURL
lastPathComponentReplacingPathExtension:outputPS ? @"ps" : @"pdf"]] path];
- NSArray *arguments = [commandName isEqualToString:@"dvipdf"] ? [NSArray
arrayWithObjects:dviFile, outFile, nil] : [NSArray arrayWithObjects:@"-o",
outFile, dviFile, nil];
- BOOL success = NO;
+ SKDESTROY(task);
- if ([self shouldKeepRunning] && [fm fileExistsAtPath:dviFile]) {
- NSTask *task = [NSTask launchedTaskWithLaunchPath:commandPath
arguments:arguments currentDirectoryPath:[dviFile
stringByDeletingLastPathComponent]];
- if (task) {
- [self performSelectorOnMainThread:@selector(conversionStarted)
withObject:nil waitUntilDone:NO];
-
- while ([task isRunning] && [self shouldKeepRunning])
- [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
- if ([task isRunning])
- [task terminate];
- else if ([self shouldKeepRunning])
- success = ([task terminationStatus] == 0);
- }
- }
+ if (success)
+ outData = [NSData dataWithContentsOfFile:outputFile];
- NSData *outData = success ? [NSData dataWithContentsOfFile:outFile] : nil;
- NSMutableData *pdfData = [info objectForKey:PDFDATA_KEY];
-
- if (outputPS && success) {
- NSAssert(NULL != converter, @"PS converter was not created");
-
- CGDataProviderRef provider =
CGDataProviderCreateWithCFData((CFDataRef)outData);
- CGDataConsumerRef consumer =
CGDataConsumerCreateWithCFData((CFMutableDataRef)pdfData);
-
- NSDictionary *dictionary = [NSDictionary
dictionaryWithObjectsAndKeys:(id)provider, PROVIDER_KEY, (id)consumer,
CONSUMER_KEY, nil];
-
- OSAtomicCompareAndSwap32Barrier(0, 1, (int32_t *)&convertingPS);
-
- [self doPSConversionWithInfo:dictionary];
-
- CGDataProviderRelease(provider);
- CGDataConsumerRelease(consumer);
+ if ([[outputFile pathExtension] isCaseInsensitiveEqual:@"ps"]) {
+ [self convertPostScriptData:outData];
} else {
if (success)
- [pdfData setData:outData];
-
- [self performSelectorOnMainThread:@selector(conversionCompleted)
withObject:nil waitUntilDone:NO];
-
- [self stopModalOnMainThread:success];
+ outputData = [outData retain];
+ [self conversionCompleted];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [NSApp stopModalWithCode:success ? SKConversionSucceeded :
SKConversionFailed];
+ });
}
-
- [fm removeItemAtURL:tmpDirURL error:NULL];
-
- [pool release];
}
- (NSData *)newPDFDataWithDVIAtURL:(NSURL *)dviURL toolPath:(NSString
*)toolPath fileType:(NSString *)aFileType error:(NSError **)outError {
NSAssert(NULL == converter, @"attempted to reenter
SKConversionProgressController, but this is not supported");
- NSMutableData *pdfData = nil;
-
fileType = aFileType;
- convertingPS = 0;
- cancelled = 0;
- pdfData = [[NSMutableData alloc] init];
+ cancelled = NO;
- if ([[toolPath lastPathComponent] isEqualToString:@"dvips"]) {
- // create this on the main thread for thread safety, as we may access
this ivar from cancel:
- converter = CGPSConverterCreate((void *)self, &SKPSConverterCallbacks,
NULL);
- NSAssert(converter != NULL, @"unable to create PS converter");
- }
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSString *dviFile = [dviURL path];
+ NSString *commandName = [toolPath lastPathComponent];
+ NSURL *tmpDirURL = [fm URLForDirectory:NSItemReplacementDirectory
inDomain:NSUserDomainMask appropriateForURL:dviURL create:YES error:NULL];
+ BOOL outputPS = [commandName isEqualToString:@"dvips"];
+ NSString *outFile = [[tmpDirURL URLByAppendingPathComponent:[dviURL
lastPathComponentReplacingPathExtension:outputPS ? @"ps" : @"pdf"]] path];
+ NSArray *arguments = [commandName isEqualToString:@"dvipdf"] ? [NSArray
arrayWithObjects:dviFile, outFile, nil] : [NSArray arrayWithObjects:@"-o",
outFile, dviFile, nil];
- NSDictionary *dictionary = [NSDictionary
dictionaryWithObjectsAndKeys:dviURL, INPUTURL_KEY, pdfData, PDFDATA_KEY,
toolPath, TOOLPATH_KEY, nil];
+ task = [[NSTask alloc] init];
+ [task setLaunchPath:toolPath];
+ [task setArguments:arguments];
+ [task setCurrentDirectoryPath:[dviFile stringByDeletingLastPathComponent]];
+ [task setStandardOutput:[NSFileHandle fileHandleWithNullDevice]];
+ [task setStandardError:[NSFileHandle fileHandleWithNullDevice]];
- NSInteger rv = [self runModalSelector:@selector(doDVIConversionWithInfo:)
withObject:dictionary];
+ [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(taskFinished:) name:NSTaskDidTerminateNotification
object:task];
- if (rv != SKConversionSucceeded) {
- SKDESTROY(pdfData);
- if (outError) {
- if ([self shouldKeepRunning])
- *outError = [NSError
readFileErrorWithLocalizedDescription:NSLocalizedString(@"Unable to load file",
@"Error description")];
- else
- *outError = [NSError
userCancelledErrorWithUnderlyingError:nil];
+ outputFile = [outFile retain];
+
+ NSInteger rv = [self runModalBlock:^{
+ @try {
+ [task launch];
+ [self conversionStarted];
}
+ @catch(id exception) {
+ SKDESTROY(task);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [NSApp stopModalWithCode:SKConversionFailed];
+ });
+ }
+ }];
+
+ [fm removeItemAtURL:tmpDirURL error:NULL];
+
+ if (rv != SKConversionSucceeded && outError) {
+ if (cancelled)
+ *outError = [NSError userCancelledErrorWithUnderlyingError:nil];
+ else
+ *outError = [NSError
readFileErrorWithLocalizedDescription:NSLocalizedString(@"Unable to load file",
@"Error description")];
}
- return pdfData;
+ return [outputData retain];
}
@end
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
------------------------------------------------------------------------------
LogMeIn Central: Instant, anywhere, Remote PC access and management.
Stay in control, update software, and manage PCs from one command center
Diagnose problems and improve visibility into emerging IT issues
Automate, monitor and manage. Do more in less time with Central
http://p.sf.net/sfu/logmein12331_d2d
_______________________________________________
Skim-app-commit mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/skim-app-commit