Am 20.09.2008 um 00:51 schrieb John Love <[EMAIL PROTECTED]>:

No sooner do I say "Solved" that I determine "Not Solved".

I am no longer crashing in part due to the fact that I've removed
NSApplescript from the thread; however, the evidence is still
insurmountable that the calculation is not running in a background
Thread, but in the main thread.  Because it's in the foreground, I
lose control of my application until the Thread finishes.

I'm betting it is not running at all, see below.


If you have already answered my problem, I apologize in advance for
the noise.

Some of this repeats what I've already posted, but I am going big-time
overboard in an attempt to be complete:

By way of background:
1) a IBOutlet of MyDocument = MyDocController
2) a IBOutlet of MyDocController = MyCalculationController

To continue:
3) In MyDocument's windowControllerDidLoadNib I call MyDocController's
MakeNewFile to which I pass the (NSURL*)theFile which is the file I
just double-clicked on Cocoa's built-in open dialog.  (If theFile =
nil) then I open up a blank document)  If theFile is not nil, then I
call MyDocController's startCalculation.
4) MyDocController's startCalculation calls MyCalculationController's
startCalculation.

Now, the threading "fun" begins all within MyCalculationController:
6) Its Interface looks like:
@interface MyCalculationController:NSObject {
        NSOperationQueue      *itsQueue;
        NSInvocationOperation *itsOp;

It seems kind of pointless to allow only a single operation for the queue.


        int                   itsCalcStatus;
}

7) Its -init looks like:
- (id) init {   
        if (self = [super init]) {
                itsCalcStatus = kNoError;  // an enumerated constant
                itsQueue      = [[NSOperationQueue alloc] init];
                itsOp         = nil;
        }       
        return self;
}

8) Its -dealloc looks like:
- (void) dealloc {
        [itsQueue release];
        // [itsOp release];   // each Operation released after it is finished
        [super dealloc];        
}

9) Its -startCalculation looks like:
- (void) startCalculation {             
        int row;        
        // itsCalcStatus = kNoError;   // set by -init  
        for (row=1; row < 10000; row++) {    
                if (itsCalcStatus == kSpreadsheetStopped)  break;
                if (itsCalcStatus != kNoError)  break;
                
                itsCalcStatus = kSpreadsheetCalculating;
                [self startOperation];   // start new thread
                // if running in background, this will have no effect:
                [itsQueue waitUntilAllOperationsAreFinished];

See the documentation:
"waitUntilAllOperationsAreFinished
Blocks the current thread until all of the receiver’s queued and executing operations finish executing."

<http://developer.apple.com/documentation/Cocoa/Reference/ NSOperationQueue_class/Reference/Reference.html#//apple_ref/occ/instm/ NSOperationQueue/waitUntilAllOperationsAreFinished>

So if -startCalculation is executed on the main thread (which seems to be the case AFAICT) then the main thread will block until the operation is done. In that case the whole concept of using NSOperation becomes just so much overhead without any practical benefit.

I would expect you to queue an NSOperation in the loop and if you really need to wait until all of them are done executing then you could wait after the loop. Note though, that you would still be blocking the main thread. But at least you would be taking advantage of NSOperationQueues ability to schedule multiple operations in parallel depending on available system resources.

But maybe even that will not be necessary. If it isn't then you need to restructure your code to continue after some sort of notification that all of your operations are done. You could keep a counter which is initialized to the number of rows and decremented whenever an operation is done. Make sure to access this counter only with proper locks in place (see @synchronized() etc.). When the counter reaches 0 then fire a notification to your main thread or trigger the next step in your workflow by calling one of the performSelectorOnMainThread: methods. Or in keeping with the NSOperation model you could fire off a special NSOperation which "watches" your counter periodically until the counter reaches 0 and have the rest of your workflow in another NSOperation that is dependent on this operation.



BTW: I don't see how you are communicating what the (background) operation is actually supposed to do. The local variable row is not accessible to the code running in the NSOperation.



        }
        
        // After the for-loop completes, itsCalcStatus =
        // kSpreadsheetCalculating, kSpreadsheetStopped, kNoExcelApp, or
kNoWorkbook
        // So ...
        if (itsCalcStatus == kSpreadsheetCalculating) {
                // no errors
                [self finishCalculation];
        }
        else {
                // either stopped or an un-recoverable error = kNoExcelApp,
kNoWorkbook
                [self stopOperation];
                // this MyDocument is done and the only user option is to close 
this
doc
                // and open a new doc, beginning with a fresh Queue.
                [itsQueue release];
        }
}

10) The methods called within -startCalculation look like:

- (void) startOperation {
        itsOp = [[NSInvocationOperation alloc] initWithTarget:self
        
selector:@selector(calculateWorksheetRow:)

This is the wrong selector signature. Your method is defined below to not take a parameter so its selector signature does not end with a colon. Use @selector(calculateWorksheetRow) or redefine the method. As it stands your method would never be called.


                                               object:nil];
        [itsQueue addOperation:itsOp];

Adding the object to the queue should make the queue responsible for keeping it around (i.e. retaining and releasing it). So you can probably release it right here. That would avoid needing to have access to the operation object in -finishOperation and partly in - stopOperation.


}

- (void) finishOperation {
        [itsOp release];  // -calculateWorksheetRow completely finished

Not needed, see above.


}

- (void) stopOperation {
        [itsOp cancel];

OK, cancel does not really stop the operation. It only sets a flag which the code can check using -isCancelled. In your case you could either use [itsQueue cancelAllOperations] or, if you have other types of operations in the same queue set a flag yourself that can be checked. (Watch out for proper synchronization!)

Either way you would not need itsOp at this point.


        [itsOp release];

Not needed, see above.


}

11) ultimately called from MyDocument's -(IBAction) stopDocument:
(id)sender:
- (void) stopCalculation {
        itsCalcStatus = kSpreadsheetStopped;   // -startCalculation breaks
from its for-loop

That would only work if -startCalculation was running on a second thread while the UI is actually running. AFAICT that is not the case or am I missing something?

In any case accessing the same variable from multiple threads without synchronization is dangerous! All sorts of weird things might happen even if this is probably one of the more harmless cases.


}

12) Finally, my -calculateWorksheetRow looks like:
- (void) calculateWorksheetRow {
        // column after column after column ...

I didn't follow this complete discussion thread but I kind of read between the lines that your operation is actually calling a different process (Microsoft Excel) using AppleScript? If so you will need to rethink your strategy I think. To my knowledge AppleScript is not thread safe (from the documentation of NSAppleScript: "Important: You should access NSAppleScript only from the main thread.") and running multiple AppleScripts targeting the same app in parallel (which would be the reason for using an NSOperationQueue in the first place) will at best not gain you any advantage and at worst crash yourself and/or Excel.


        [self finishOperation];
}

Again, if this is too wordy, I really apoligize for all the noise.

John Love



HTH
Mike
--
Mike Fischer     Softwareentwicklung, EDV-Beratung
                                Schulung, Vertrieb
Note:             I read this list in digest mode!
      Send me a private copy for faster responses.

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]

Reply via email to