0

Note, related but not the same: iPhone - Grand Central Dispatch main thread

I've failed at this question many times, so here's source code:

While on the main thread

dispatch_async( dispatch_get_main_queue(), ^{ NSString * str = @"Interrupt myself to do something."} );

I'm just curious, when a thread switches, it stores its registers in Thread Local Storage, switches context, runs from its new spot in the Program Counter (which I assume is within a copy of the program that simply uses a different stack and register), then it "goes back" to the main thread.

When it interrupts itself, I'm just wondering what decides when it should, and what happens to the Thread Local stuff.

I've read up on this a little, but I'm still wrapping my head around the fact that programs are not continuous. They're just "something to do in small chunks when the OS decides to run a chunk of a process, or its chunks (threads).

I am self-taught, which might add to my lack of register/asm knowledge that may be standard to any scholar.

Thanks. The code should help, this is iOS specific, but I believe the answer/question is related to any language going from main-to-main.

Since every past attempt has resulted in lengthy answers that ignore the reason I'm asking this, I will iterate one last time....

This is for the SAME thread. Main-to-main. Does it really just stop itself, move the program counter elsewhere, go, then end at the block? Also don't these things usually change at branches (if/for and blocks too).

Pointing me in the right direction works too, but like I said, previously the question was misread.

Community
  • 1
  • 1
Stephen J
  • 2,367
  • 2
  • 25
  • 31
  • Are you sure that you're not talking about `dispatch_sync`? With `dispatch_async`, the block will just go to the end of the queue; there's no interrupting involved. On the same thread, iirc `dispatch_sync` will just run the code in place. – sapi Aug 21 '14 at 23:59
  • Right, but even though it won't interrupt the current function, it will eventually interrupt its thread (which is what's puzzling). – Stephen J Aug 22 '14 at 00:11

2 Answers2

1

It is hard to answer your question specifically without having access to the internals of GCD, but generically, the answer is no, simply adding a unit of work to a dispatch queue will not immediately interrupt the executing code.

As you suggest context switches are expensive, not only in terms of state saving & restoration but also the processor will need to dump the instruction pipeline resulting in wasted cycles.

Typically the operating system will keep executing the current task until it suspends (e.g. waits on a network or other IO operation) or perhaps is interrupted by some external event (pressing the home key on the phone), but there are also time limits to prevent a runaway task from locking the whole device (This is pre-emptive multi-tasking, as opposed to co-operative multitasking where the task needs to relinquish the CPU)

With dispatch_async there is no guarantee of when the code will execute in relation to the current code block. The code block may not even be next in the queue - other threads may have added other units of work to the queue before this one.

Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Thanks. So it just treats it like anything else queued, but with no changes to the stack or register? – Stephen J Aug 22 '14 at 00:10
  • 1
    No, the block has its own context. https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW3 – Paulw11 Aug 22 '14 at 00:14
0

I think the thing that's confusing you is the use of dispatch_async( dispatch_get_main_queue()), which submits code to run on a queue on the main thread.


Using dispatch_async on the main queue:

When you call dispatch_async( dispatch_get_main_queue()), it adds a unit of work to the main queue, which runs it's jobs from the main thread.

If you run this call from the main thread, the results are the same. The work gets added to the main queue for later processing.

When you make this call from the main thread, the system doesn't check the main queue for work to do until your code returns.

Think of this as a one-cook kitchen. As the cook works, he puts trays of dishes in the dishwashing area. He doesn't stop to do dishes until he gets to a breaking point in what he's currently doing. At that point he takes a tray of dishes, loads it into the dishwasher, and then goes back to cooking.

The cook knows that he has to check for dishes each time he gets to a breaking point, and then completes a dishwashing task before returning to cooking.


Using dispatch_async on a background queue:

A dispatch_async call to a background queue is like a 2-person kitchen. There is a dishwasher working at the same time. The cook puts a tray of dishes into the dishwashing station (the queue) and the dishwasher (the other thread) picks up that task as soon as it's finished with it's previous tasks, while the cook continues to work on cooking.

The above assumes a machine with multiple processors, which is the norm these days. Each processor can do work at the same time without having to juggle multiple tasks.

If you are running on a single-core system with preemptive multitasking, submitting tasks to separate threads/background queues has the same effect as if there were multiple processors, but now the OS has to do a juggling act. There's only one person in the kitchen, but he wears multiple hats. The person is doing the cook job, and the OS shouts "Switch!" The cook jots down notes on what he was doing (saves state) and then jumps into the dish-pit and starts washing dishes, and keeps washing dishes until the OS yells "Switch!" again, and the worker again saves state, switches to the next role, and picks up that role (cook) where it was left off.

Multi-tasking is more costly on a single-core system because each time the worker switches roles, it has to save the current state, then load the saved state for the other role, and continue. Those context switches take time.

Community
  • 1
  • 1
Duncan C
  • 128,072
  • 22
  • 173
  • 272