why does NSManagedObjectContext Queue execute on main thread? - ios

When I send a performBlock message to my MOC of type NSPrivateQueueConcurrencyType, like this:
[self.privateManagedObjectContext performBlockAndWait:^{
if ([[NSThread currentThread] isMainThread]) {
NSLog(#"executing on the main thread!!");
}
…
}];
I find that, by default, this executes on the main thread. The conditional in the above code triggers, and the Issue Navigator indicates that execution is occurring on Thread 1 in the NSManagedObject Queue.
This is very puzzling to me, because Apple tells us that "each thread must have its own entirely private managed object context." Given that an MOC of type NSMainQueueConcurrencyType will use the main thread, doesn't it violate thread confinement for an MOC of type NSPrivateQueueConcurrencyType to use the main thread?
Is the execution of my code on the main thread normal? Have I misunderstood thread confinement? I understand that a queue is not necessarily tied to a particular thread, but in this case it seems the private MOC queue should at a minimum avoid the main thread, if not have a single go-to thread. I'm having some weird bugs, so I need to figure out what's going on. Thanks!

This optimization is possible because performBlockAndWait: executes the block
synchronously, i.e. the method does not return until the block has finished.
Therefore the block will not be executed in parallel with other operations on
the main thread.
(For the same reason, dispatch_sync(queue, ...) may execute a block on the main thread
instead of a separate thread.)

Related

When can I run a synchronous code by executing DispatchQueue.main.sync { }?

I tried running the following code and it raises the following error every time:
DispatchQueue.main.sync { }
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
I found this post on stackoverflow that says to never run synchronous code on the main queue:
DispatchQueue crashing with main.sync in Swift
I had assumed that because the sync { } method is there that means there is some context that it can be used. Is there absolutely no use for executing synchronous code on the main queue?
I had assumed that because the sync { } method is there that means there is some context that it can be used.
Yes, it's there to be used when appropriate, but that doesn't mean it should be applied to the main queue.
Is there absolutely no use for executing synchronous code on the main
queue?
The sync command blocks and waits for its operation to be performed and completed on the specified queue. That queue can certainly be the main queue. But the queue that blocks cannot be the main queue! You must never say sync when you are on the main queue, as you will then be blocking the main queue which is illegal; and you must really never say DispatchQueue.main.sync when you are on the main queue, as you will be blocking the main queue forever (thereby causing the heat death of the universe).
Really the best thing to do is adopt async/await and never mention DispatchQueue again. All these concerns vanish in a puff of smoke and your code becomes safe and easy to reason about, automatically.
sync should not be used in the main queue because you are likely to block everything, the method is there for "custom" queues (for example you created a queue and YOU handle when it has to be blocked and unblocked) the main queue is a special case, and since it is not manage by you introducing a block may generate unexpected behavior (that usually translates into a crash)

is NSManagedObjectContextDidSave or save thread safe?

I am having a structure of parent/child relationship context.
The parent is main context and the child is private concurrent context.
when the child does some changes and do save. The main context (object) receives the notification and do NSManagedObjectContextDidSave.
The problem is that I am wondering if this action is thread safe? because even it's wrapped in is't own context/thread (inside mainContext.performBlock), the other thread - child concurrent thread for example, can do the fetch. Will it corrupt the data when these 2 actions happen at exact time?
performBlock : synchronously performs the block on the context's queue. and is not thread safe in case of saving multiple records.
performBlockAndWait: synchronously performs the block on the context's queue. May safely be called subroutine.

"performBlockAndWait:" block is not executed

I have a method similar to this:
- (void)handleUpdate
{
dispatch_sync(dispatch_get_main_queue(), ^{
NSArray *objectIDs = [self.objectsInMainContext valueForKeyPath:#"objectID"];
[self.privateContext performBlockAndWait: ^{
// Some processing
}];
});
}
What I called mainContext is associated to main queue, and privateContext is associated to a private queue and is child of the mainContext. This method is called from the privateContext's private queue, and it is not nil when the performBlockAndWait: call is reached, but execution does not enter the block and neither any code after this method is reached...
What could I be missing here?
Thanks in advance
EDIT: I get no error in Xcode, the breakpoints I've set in the code inside the performBlockAndWait: block and after this method call are simply not reached.
EDIT 2: I corrected the code snippet, I wasn't accessing the mainContext but an array of objects associated to mainContext.
If handleUpdate is called from the private queue as you say, then this is a classic deadlock.
You call dispatch_sync on the private queue. That means "wait until this returns before continuing on this queue." Then, inside that block, you enqueue another block that has to wait to run on the private queue and won't return until it does. The queue is now waiting on itself and will never progress.
I suspect you want dispatch_sync to be dispatch_async here.
Alright, there is at least 1 thing that is wrong in your situation:
Calling performBlockAndWait: on the main thread (via passing it to the main queue). The documentation says that performBlockAndWait:
Synchronously performs a given block on the receiver’s queue.
As a result of that call, you will get a dead lock on the main thread (as you use dispatch_sync outside.
UPDATE on the dispatch_async way:
The problem will not disappear. That's because you'll be still using the main queue (which implies the main thread in the end) and the main CD context (which also implies the main thread). The problem is that you make the main thread to wait until the job is done on the same (main) thread - just a dead lock out of the box.
You have several problems.
First, you are using GCD directly to serialize access to a NSMainQueueConcurrency MOC. You should never use anything but the CoreData synchronization mechanisms for accessing CoreData. Yes, it is safe to access a NSMainQueueConcurrencyType MOC from the main thread. However, you should never use GCD directly... especially dispatch_sync.
Second, you are using dispatch_sync which is not reentrant. It can, and will, cause deadlocks. You should hardly ever use this function. Consider it a very sharp instrument, to be used where it is the only solution to a very specific problem.
Third, you are calling performBlockAndWait on a child context, which should never be done because it can cause deadlocks. In general, you should avoid the non-asynchronous threading models unless you really know what you are doing.
Your code should be something more like this. Each performBlock call will post a block onto a message queue, which will get processed independently of any waiting thread.
- (void)handleUpdate
{
[self.mainContext performBlock:^{
NSArray *objectIDs = [self.objectsInMainContext valueForKeyPath:#"objectID"];
[self.privateContext performBlock: ^{
// Some processing with objectIDs
}];
});
}

performBlockAndWait vs dispatch_sync(dispatch_get_main_queue()

Utility.managedObjectContext().performBlockAndWait({
})
dispatch_sync(dispatch_get_main_queue(), {
})
Curious what is the difference between the two code above? context was created with .MainQueueConcurrencyType option.
If I perform blocks on the main queue, are queues executed in a FIFO order? Or can they overlap, operation mingle? I.e. (a1,a2,a3),(b1,b2,b3) can result (a1,b1,a2,a3,b2,b3)?
You are mixing two entirely different concepts here, but since it is the main thread/context/queue, your mix is masked and it "works".
Managed object context's performBlockAndWait: and performBlock: methods do not make any guarantees on which thread the block is executed, only that data accessed/mutated is safely accessed. Since your context is of main queue concurrency type, it is the exception in that it is safe to touch its objects outside of the performBlockAndWait: and performBlock: methods, on the main thread only. So when you queue your block to run on the main queue, it is guaranteed to run on the main thread, and thus your data is safe.
Block execution on the main thread is not atomic. Otherwise, what is the point of multithreading? To ensure data safety, you must performBlockAndWait: and performBlock: methods are called when accessing data. You are guaranteed that main queue scheduled blocks will run uninterrupted by other main queue scheduled blocks, and managed object context queues (background or main) are serial, so only one block will be allowed to concurrently access data.

What is NSManagedObjectContext's performBlock: used for?

In iOS 5, NSManagedObjectContext has a couple of new methods, performBlock: and performBlockAndWait:. What are these methods actually used for? What do they replace in older versions? What kind of blocks are supposed to be passed to them? How do I decide which to use? If anyone has some examples of their use it would be great.
The methods performBlock: and performBlockAndWait: are used to send messages to your NSManagedObjectContext instance if the MOC was initialized using NSPrivateQueueConcurrencyType or NSMainQueueConcurrencyType. If you do anything with one of these context types, such as setting the persistent store or saving changes, you do it in a block.
performBlock: will add the block to the backing queue and schedule it to run on its own thread. The block will return immediately. You might use this for long persist operations to the backing store.
performBlockAndWait: will also add the block to the backing queue and schedule it to run on its own thread. However, the block will not return until the block is finished executing. If you can't move on until you know whether the operation was successful, then this is your choice.
For example:
__block NSError *error = nil;
[context performBlockAndWait:^{
myManagedData.field = #"Hello";
[context save:&error];
}];
if (error) {
// handle the error.
}
Note that because I did a performBlockAndWait:, I can access the error outside the block. performBlock: would require a different approach.
From the iOS 5 core data release notes:
NSManagedObjectContext now provides structured support for concurrent operations. When you create a managed object context using initWithConcurrencyType:, you have three options for its thread (queue) association
Confinement (NSConfinementConcurrencyType).
This is the default. You promise that context will not be used by any thread other than the one on which you created it. (This is exactly the same threading requirement that you've used in previous releases.)
Private queue (NSPrivateQueueConcurrencyType).
The context creates and manages a private queue. Instead of you creating and managing a thread or queue with which a context is associated, here the context owns the queue and manages all the details for you (provided that you use the block-based methods as described below).
Main queue (NSMainQueueConcurrencyType).
The context is associated with the main queue, and as such is tied into the application’s event loop, but it is otherwise similar to a private queue-based context. You use this queue type for contexts linked to controllers and UI objects that are required to be used only on the main thread.
They allow you to access the same managedObjectContext accross threads.
I am not really sure I am correct, but this is how I use it.
You use performBlockAndWait is like "usual". You do not need it if you execute the managedObjectContext only on one thread. If you execute it on many threads then yes you will need performBlock.
So, if you're on main thread, you do not need to do performBlockAndWait for the main managedObjectContext. At least I don't and is doing fine.
However if you access that managedObjectContext on other threads then yes you will need to do performBlockAndWait.
So that's the purpose of performBlock and performBlockAndWait.
Would someone please correct me if I am wrong here. Of course if you access the context only on one thread then you can simply use the default.

Resources