What does isConcurrent mean for NSOperation running from NSOperationQueue? - ios

Because NSOperationQueue always run tasks on a new thread,
I'm confused about the role of isConcurrent when NSOperation runs from NSOperationQueue.
If i have two subclasses of NSOperation, both running an async processes, both are launched from NSOperationQueue and in both I override isCancelled, isExecuting, isFinished and isReady.
What will be the difference if at one I override isConcurrent to always return YES and the other always to return NO.
Who actually calls isConcurrent? How the logic change if it is NO or YES.

It's a legacy method, used before OS X v10.6 and before iOS 4, i.e. before the introduction of GCD for NSOperationQueue dispatch.
From the doc
Operation queues usually provide the threads used to run their operations. In OS X v10.6 and later, operation queues use the libdispatch library (also known as Grand Central Dispatch) to initiate the execution of their operations. As a result, operations are always executed on a separate thread, regardless of whether they are designated as concurrent or non-concurrent operations. In OS X v10.5, however, operations are executed on separate threads only if their isConcurrent method returns NO. If that method returns YES, the operation object is expected to create its own thread (or start some asynchronous operation); the queue does not provide a thread for it.
If you're running OS X >= 10.6 or iOS >= 4, you can safely ignore it.
As a confirmation of this fact, from the doc of isConcurrent
In OS X v10.6 and later, operation queues ignore the value returned by this method and always start operations on a separate thread.

I have added a more concise answer now, left this for the time being as it was part of a discussion...
Ok, I was quite happy with my use of the isConcurrent method until today !
I read the sentence:
In OS X v10.6 and later, operation queues ignore the value returned by
this method and always start operations on a separate thread.
As a warning relating to QA1712, pointing out that for concurrent operations the start method can now be called on a different thread to the one that queued the operation, which is a change in 10.6 and iOS 4.
I didn't read this as indicating that the isConcurrent method is ignored completely by the queue and has no purpose at all, just that it no longer effects the thread on which start is called. I may have misunderstood this.
I also misunderstood the original question as a more general one about concurrent operations and the isConcurrent flag, and the accepted answer as effectively saying
The isConcurrent flag can be ignored since 10.6 and iOS 4
I'm not sure this is correct.
If I understand the original question now, to paraphrase:
Given a correctly built concurrent NSOperation does the isConcurrent flag itself actually alter the execution of the operation at all?
I guess it's hard to say for all possible setups, but we can say that:
It's not deprecated. It's normal for Apple to deprecate methods
that are no longer useful.
The documentation consistently refers to the method being a required
override.
Perhaps isConcurrent is effectively deprecated but as it's only a single BOOL flag perhaps it's not worth the effort to deprecate it in the docs. Or perhaps it does nothing now but Apple have kept it for possible future use and expect you to override it as described.
I created a quick test project with an NSOperation that overrode isConcurrent and main only, isConcurrent was not called at any stage. It was a very simple test though. I assume you may have tested it also? I assumed perhaps if the NSOperationQueue didn't call it NSOperation's default implementation of start might.
So where does that leave us? Obviously it's no trouble to implement it and return YES to satisfy the documented requirements. However from my perspective I think it's too much of a leap to go from the caveat regarding 10.6 and iOS 4.0 to say that it can be safely ignored now.
My Original Answer...
isConcurrent is not a legacy method and is not ignored by NSOperationQueue. The documentation as quoted in the other answers is a little unclear and easily misunderstood.
isConcurrent = YES means the operation provides its own means of concurrency. Or to put it another way, the operation "isAlreadyConcurrent" and doesn't need the NSOperationQueue to provide and manage the concurrency. As NSOperationQueue is no longer providing the concurrency you need to tell it when the operation isFinished or if it isCancelled (etc) hence the need to override these methods.
A common example is an NSOperation that manages an NSURLConnection. NSURLConnection has it's own mechanism for running in the background, so doesn't need to be made concurrent by NSOperationQueue.
An obvious question is then: "Why put an already concurrent operation into an NSOperationQueue?" It's so the operation can benefit from the other features of the NSOperationQueue like dependencies etc.
The misleading part of the documentation is referring only to what thread the start method of an NSOperation is called on. The change caused a problem discussed in QA1712.

Paraphrasing the original question to:
Given a correctly built concurrent NSOperation does the value returned by isConcurrent actually alter the execution of the operation at all?
Whilst we could try to understand how the isConcurrent flag is used by NSOperation, NSOperationQueue, or other parts of the operating system, it would be a mistake to rely on any information we discovered.
The purpose of the flag is described only as:
Return YES if the operation runs asynchronously with respect to the current thread or NO if the operation runs synchronously on whatever thread started it.
As long as you correctly respond, it should not be important who calls the method or how it effects any logic inside the Apple frameworks.
Additional Notes:
The documentation does make reference to a change in the way that NSOperationQueue used this value before and after OSX 10.6 and iOS 4.0.
In OS X v10.6, operation queues ignore the value returned by isConcurrent and always call the start method of your operation from a separate thread. In OS X v10.5, however, operation queues create a thread only if isConcurrent returns NO. ...
This change caused an issue described in QA1712.
This commentary is commonly interpreted to imply that the isConcurrent flag is no longer used after 10.6 and iOS 4, and can be ignored. I do not agree with this interpretation as the method has not been formally deprecated and is still documented as a required override for concurrent operations.
Also the fact that it's use has changed in the past is a reminder that it could change again in the future, so we should respond correctly even if we suspect it does not currently have any effect.

Related

NSOperation, start vs main

According to Apple document on NSOperation, we have to override main method for non-concurrent operations and start method for concurrent operations. But why?
First, keep in mind that "concurrent" and "non-concurrent" have somewhat specialized meanings in NSOperation that tend to confuse people (and are used synonymously with "asynchronous/synchronous"). "Concurrent" means "the operation will manage its own concurrency and state." "Non-concurrent" means "the operation expects something else, usually a queue, to manage its concurrency, and wants default state handling."
start does all the default state handling. Part of that is that it sets isExecuting, then calls main and when main returns, it clears isExecuting and sets isFinished. Since you're handling your own state, you don't want that (you don't want exiting main to finish the operation). So you need to implement your own start and not call super. Now, you could still have a main method if you wanted, but since you're already overriding start (and that's the thing the calls main), most people just put all the code in start.
As a general rule, don't use concurrent operations. They are seldom what you mean. They definitely don't mean "things that run in the background." Both kinds of operations can run in the background (and neither has to run in the background). The question is whether you want default system behavior (non-concurrent), or whether you want to handle everything yourself (concurrent).
If your idea of handling it yourself is "spin up an NSThread," you're almost certainly doing it wrong (unless you're doing this to interface with a C/C++ library that requires it). If it's creating a queue, you're probably doing it wrong (NSOperation has all kinds of features to avoid this). If it's almost anything that looks like "manually handling doing things in the background," you're probably doing it wrong. The default (non-concurrent) behavior is almost certainly better than what you're going to do.
Where concurrent operations can be helpful is in cases that the API you're using already handles concurrency for you. A non-concurrent operation ends when main returns. So what if your operation wraps an async thing like NSURLConnection? One way to handle that is to use a dispatch group and then call dispatch_wait at the end of your main so it doesn't return until everything's done. That's ok. I do it all the time. But it blocks a thread that wouldn't otherwise be blocked, which wastes some resources and in some elaborate corner cases could lead to deadlock (really elaborate. Apple claims it's possible and they've seen it, but I've never been able to get it to happen even on purpose).
So another way you could do it is to define yourself as a concurrent operation, and set isFinished by hand in your NSURLConnection delegate methods. Similar situations happen if you're wrapping other async interfaces like Dispatch I/O, and concurrent operations can be more efficient for that.
(In theory, concurrent operations can also be useful when you want to run an operation without using a queue. I can kind of imagine some very convoluted cases where this makes sense, but it's a stretch, and if you're in that boat, I assume you know what you're doing.)
But if you have any question at all, just use the default non-conurrent behavior. You can almost always get the behavior you want that way with little hassle (especially if you use a dispatch group), and then you don't have to wrap your brain around the somewhat confusing explanation of "concurrent" in the docs.
I would assume that concurrent vs. non-concurrent is not just a flag somewhere but a very substantial difference. By having two different methods, it is made absolutely sure that you don't use a concurrent operation where you should use a non-concurrent one or vice versa.
If you get it wrong, your code will absolutely not work because of this design. That's what you want, because you immediately fix it. If there was one method only, then using concurrent instead of non-concurrent would lead to very subtle errors that might be very hard to find. And non-concurrent instead of concurrent will lead to performance problems that you also might miss.

is synchronous in separate thread the same as asynchronous

Recently, i am learning concurrency in swift. According to apple's document in NSOperation class reference :
When you add an operation to an operation queue, the queue ignores the value of the asynchronous property and always calls the start method from a separate thread. Therefore, if you always run operations by adding them to an operation queue, there is no reason to make them asynchronous.
does it mean synchronous in a separate thread is the same as asynchronous? and when i do the test with the following code, the operation indeed doesn't block the current main thread.
let operationQueue = NSOperationQueue()
let operation = NSBlockOperation(){
//do some task here
}
operationQueue.addOperation(operation)
so if it is true, then why should we create concurrency subclass of NSOperation?
Oh, NSOperation. Such a bizarre history you have.
NSOperation is relatively old (in iOS terms; fairly modern in ObjC terms). It was added in OS X 10.5. Before OS X 10.6/iOS 4, there were no NSBlockOperation objects. There were no blocks at all. So the only way to make an operation was to subclass or use NSInvocationOperation. Both approaches are cumbersome, but were still easier and more powerful than the older approach of using NSThread directly.
(This was right at the time when multi-core became a thing. 10.5 was famous for adding Core Animation which was I believe the first major preemptive multitasking framework in Cocoa. Before 10.5, most things were done with the runloop and cooperative multitasking, which is actually very efficient and effective for single-core systems. But it doesn't scale well to multi-core systems. Tools like NSOperation were provided to help us write better multi-core code, but GCD was so much more powerful that it completely dominated how multitasking code is written in Cocoa.)
When you subclass NSOperation, you needed to tell the system whether your operation is asynchronous. This isn't a request to run you asynchronously. This is a promise that your start method will not block. It's up to your start method to make sure the operation really is asynchronous.
This is only necessary in the case that your NSOperation is being started manually, and even then it was often not needed. If you put it onto an NSOperationQueue (and you really should always do that), this property is irrelevant. I remember it creating a lot of confusion at the time.
It's become even more irrelevant since the introduction of blocks. It is almost always much easier to use an NSBlockOperation (or dispatch_async) than to subclass NSOperation, which was always a bit tricky to get quite right.
Just in case you haven't already read it, if you want to study Cocoa concurrency, you definitely want to start with the Concurrency Programming Guide.
Asynchronous is always defined relative to the thread that makes a request. So a request is asynchronous relative to thread A if thread A makes a request that runs in thread B such that thread A is able to do other work while thread B is running the request.
If thread B in turn farms out the request to thread C such that thread B is able to do other work while thread C is running the request then that second request is an asynchronous relative to thread B.
It doesn't make sense to just keep farming out the same element of work asynchronously over and over again, of course. But assume the work delegated by thread A to thread B described above can be split up into multiple smaller elements of work. It would be reasonable for thread B to invoke those smaller elements of work asynchronously on threads C, D, etc. This might happen if B provides a service to A such that A doesn't want/need to know the details of how the work gets done; it just wants the work done asynchronously. B knows the details and can decide if/how to accomplish the work via smaller parallel units.

Why doesn't NSThread's cancel method call pthread_cancel?

Apple's documentation, and all the open source implementations I can find, are in agreement that thread cancellation should be handled entirely by the user. That is, [thread cancel] just sets a BOOL property on the receiver. It's then up to the user's implementation to check [NSThread currentThread].isCancelled periodically and, if YES, prematurely return from the thread's main method.
Okay, fair enough. But why not instead rely on pthread_cancel, which has dozens of builtin cancellation points already implemented? Surely this would yield more responsive thread cancellation. NSThread could easily be extended to have a cancellationBlock property, or some other mechanism for user-defined cancellation behavior that NSThread itself could dispatch via pthread_cleanup_pop.
Did Apple think this would be too complicated for people to use or something? The rest of the NSThread API is basically 1:1 with the pthreads API, so I'm curious why a different (honestly naive) path was chosen here.

dispatch_async and [NSURLConnection sendSynchronousRequest]

There are various questions around this topic, and lots of advice saying NOT to use sendSynchronousRequest within dispatch_async, because it blocks the thread, and GCD will spawn lots of new worker threads to service all the synchronous URL requests.
Nobody seems to have a definitive answer as to what iOS 5, [NSURLConnection sendAsynchronousRequest:queue:completionHandler:] does behind the scenes.
One post I read states that it 'might' optimise, and it 'might' use the run loop - but certainly won't create a new thread for each request.
When I pause my debugger when using sendAsynchronousRequest:queue:completionHandler, the stack trace looks like this:
..now it appears that sendAsynchronousRequest:queue:completionHandler, is actually calling sendSynchronousRequest, and I still have tons of threads created when I use the async method instead of the sync method.
Yes, there are other benefits to using the async call, which I don't want to discuss in this post.
All I'm interested in is performance / thread / system usage, and if i'm worse off using the sync call inside dispatch_async instead of using the async call.
I don't need advice on using ios4 async calls either, this is purely for educational purposes.
Does anyone have any insightful answers to this?
Thanks
This is actually open source. http://libdispatch.macosforge.org/
It is very unlikely that you will be able to manage the worker threads more efficiently than Apple's implementation. In this context "asynchronous" doesn't mean select/poll, it just means that the call will return immediately. So it is not surprising that the implementation is spawning threads.
As the previous answer stated, both GCD (libdispatch) and the libblocksruntime are open source. Internally, iOS/OS X manage a global pool of pthreads, plus any app-specific pools you create in your user code. Since there's a 1:N mapping between OS threads and dispatch tasks, you don't have to (and shouldn't) worry about thread creation and disposal under the hood. To that end, a GCD task doesn't use any time in an app's run loop after invocation as it's punted to a background thread.
Most kinds of NSURL operations are I/O-bound; there's no amount of concurrency voodo that can disguise this, but if Apple's own async implementation uses its synchronous counterpart in the background, it probably suggests it's quite highly optimized already. Contrary to what the previous answer said, libdispatch does use scalable I/O internally (kqueue on OS X/iOS/BSD), and if you hand-rolled something yourself you'd have to deal with file-descriptor readiness yourself to yield similar performance.
While possible, the return on time invested is probably marginal. Stick with Apple's implementation and stop worrying about threads!

Which tasks are more suitable to NSOperation than GCD? [duplicate]

This question already has answers here:
NSOperation vs Grand Central Dispatch
(9 answers)
Closed 9 years ago.
Which tasks would be better suited to using NSOperation as opposed to using GCD when programming for the iPhone?
To me they seem to do the same thing. I can't see the strengths and weaknesses one has over the other.
NSOperation is built on top of GCD, so the question is more about whether you use NSOperation or pass a block directly to GCD.
An NSOperation is bulky and needs more boiler-plate codes to set it up, but it has a lot more functionality. You can create the same NSOperation subclass in various parts of your code and put them into the queue and run it.
Passing a block to GCD by e.g. dispatch_async is quick and disposable. You typically don't reuse a block anywhere else; you just set up a block which is executed only at that point of the code, passes it to the GCD or other APIs, and quickly go on.
So each has its merits.
Apparently, NSOperationQueue is built on GCD as of iOS 4; the docs just haven't been updated. Check this posting by an Apple employee here: https://devforums.apple.com/message/352770 (You may need to create an account) So, you should follow Mike Abdullah's advice and use the simplest API for the task at hand. dispatch_async is lower level, usually C-type stuff (but not limited to), and is good for one-shot and sequential type deals (fire this block on this queue, FTW). NSOperationQueues are higher level, Objective-C stuff, and are good if you are adding a lot of operations at various points in your code, and/or need to manage concurrency, priorities and dependencies. At least that's how I use them.
As always with such questions, use the simplest API available. Measure if it's a performance problem and then reevaluate if needed.
One thing that I don't believe has been mentioned here is that NSOperations can be cancelled during execution, whereas a block is guaranteed to complete once execution has begun. Having said that, a GCD queue can be suspended (dispatch_suspend()), so that any blocks following the currently executing blocks will not be executed.

Resources