6

To be honest, I don't know if there might be a solution to my question but I'd like to catch, in Swift, when a context switch is happening.

I was imaging a func which takes a long time in order to be completed such as a write operation on a remote server and I was thinking if there might be a way to understand when (which line at least) the thread which is executing that task is performing a context switch because another task waiting for a long time has to be executed.

Sorry if for you might seem a stupid question or if I made mistakes whilst trying to explain the above

EDIT:

I'm talking about context switches that are happening automatically requested by the scheduler.. So imagine again we are in the middle of this long function which does tons of operations and the scheduler gave this task an amount of seconds, for example 10 seconds in order to make it complete. If the process runs out of time and doesnt end the task, it will be suspended and for example the thread will execute another task of another process. When it ends, scheduler might think to give another try to the suspended job and the execution will be resumed starting where it has been suspended ( so will read the value from PC register and will keep going on )

ndPPPhz
  • 315
  • 1
  • 15
  • Pretty unclear what you are asking. – Sulthan Dec 12 '18 at 21:33
  • If `ThreadA` is currently executing a task of my app/process that is in `executing state` for a long time, the scheduler might stop that process in favour of another process. In this scenario, the process state will be saved somewhere in memory in order to be resumed later on. I'd like to know if it's possible to catch when this is happening for an iOS app – ndPPPhz Dec 12 '18 at 22:02
  • 1
    @ndPPPhz Please [edit] your question instead of posting clarifications in comments. – rmaddy Dec 12 '18 at 22:30

2 Answers2

8

You can absolutely get what you've asked for, but I have a feeling it's going to be much less useful to you than you may believe. The vast (vast, vast!) majority of context switches are going to occur at points that you probably would think of as "uninteresting." lstat64(), mach_vm_map_trap(), mach_msg_trap(), mach_port_insert_member_trap(), kevent_id(), the list goes on. Most threads spend most of their time deep in the OS stack. "A write operation on a remote server" isn't going to block after it takes some long period of time. It's going to proactively block itself because it knows it's going to take a (mind-bogglingly) long period of time.

Even so, you can certainly explore this with Instruments. Just choose the System Trace profile and it'll show you all the threads and the system cores and how your threads and all the other threads on the device are getting scheduled, every system call, etc etc etc. It's a huge amount of information, so you usually only profile for a few seconds at a time. But it'll look something like this:

Instruments with System Trace

This is useful information if you're at the point where context switches are a major bottleneck. This might happen if you're dealing with excessive lock contention, or if you're thrashing your L1 cache because you keep getting interrupted by some other thread. So if you have some thread that you expect to stay running pretty continuously, and it's getting blocked, this is really valuable information. Or if you have two threads that you think should work back and forth smoothly, but they seem to be fighting (switching rapidly), then that's something you could work on. (But this is rarely one of the first places you'd look for performance tuning unless you're working on quite low-level code.)

From your description, I think you may have the wrong idea about the scheduler. Nothing in the scheduler is going to be on the order of 10 seconds. In the scheduler world, milliseconds are a lot of time. You should be thinking about things that take microseconds and even nanoseconds. If you're working on code that assumes fetching data from RAM is free, then you're on the wrong time scale. A network call is so ludicrously slow that you can basically estimate it as "forever." At the context-switch level you're looking at events like:

00:00.770.247   Virtual memory Zero Fill took 10.50 µs  small_free_list_add_ptr ← (31 other frames) 
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 1
    Thank you so MUCH for your so exhaustive answer. Moreover, having a rough idea of what's going on using Instruments, it's already a big help for me in order to investigate better how the context switch is happening on iOS. To be fair, I was aware that the order of magnitude of seconds was too enough for any scheduler but it was just to explain better what I meant! Again, thank you so much. I will investigate and let you know – ndPPPhz Dec 12 '18 at 23:02
2

I think its a kinda cool question but not clear.The answer is all about my understanding on your question. Normally if you implement C++ or C program for context switching, consider you write code with mutexes or semaphores.In these sections processes or threads work in critical section and sometimes there executing the context switching manually or interruption way. In iOS there are same identical implementation on concurrency such as DispatchSemaphore.(Actually mutex is a Semaphore that working with lock system.) You can read the documentation from here.

For starting to this, this is the Semaphore class definition in Swift.

class DispatchSemaphore : DispatchObject

You can init it with int value such as

let semaphore = DispatchSemaphore(value: intValue)

And if you want to use mutex variant you can easily use lock variable. Such as

 var lock = Lock()

When you lock the thread with correct implementation, you will be in critical section and you can switch it with unlock or etc.

It's obviously look similar with POSIX pthread_lock_t

You can handle the context switching within the lock or semaphore critical section like that

lock.lock()
// critical section
// handle the calculation, if its longer unlock, or let it do its job
lock.unlock()


semaphore.wait(timeout: DispatchTime.distantFuture)
// critical section
// handle the calculation, if its too long signal to exit it in here, or let it do its job
// if it is a normal job, it will be signal for exit already.
semaphore.signal()

The handled answer is contains context switching in threads.

Addition to my question, when you ask about the context switching, its natural means is that changing the threads or processes basically. So when you get the data from background thread and then implement it for the UITableView' you probably will do reloadData method call in DispatchQueue.main.async . Its a common usage of context switching.

eemrah
  • 1,603
  • 3
  • 19
  • 37
  • First of all, thank you for your answer. I think you got what I meant but I'm talking about context switches that are happening automatically requested by the scheduler.. So imagine again we are in the middle of this long function which does tons of operations and the scheduler gave this task an amount of seconds, for example 10 seconds in order to make it complete. If the process runs out of time and doesnt end the task, it will be suspended and for example the thread will execute another task of another process. When it ends, scheduler might think to give another try to the suspended job – ndPPPhz Dec 12 '18 at 22:29
  • and the execution will be resumed starting where it has been suspended ( so will read the value from PC register and will keep going on ) – ndPPPhz Dec 12 '18 at 22:31