What happens between a timer interrupt and a thread actually running
A timer interrupt does not run your thread. It only gives the kernel a chance to decide whether your thread should run next. The gap between the two is where most scheduling confusion lives. People picture the interrupt waking the thread directly. It does not. It wakes the scheduler. The scheduler wakes the thread.
The interrupt itself
A hardware timer fires on a fixed interval. On x86 the local APIC timer is the usual source. The CPU stops the current code, looks up the interrupt vector in the IDT, and jumps to the handler. It saves just enough state to come back. Instruction pointer, flags, stack pointer, a few registers.
The handler runs in interrupt context. That is the first thing to get right. It is not running inside your thread. It has no thread of its own. It borrows the CPU for a short job and must give it back fast. It cannot sleep. It cannot wait on a lock held by the code it just interrupted. It cannot call anything that might block.
What it does is small. It acknowledges the timer so the next tick can fire. It updates the kernel's idea of time. It calls into the scheduler tick, which updates how long the current thread has run and whether its time slice is spent. Then it returns.
The decision
The tick may set a flag that says the scheduler needs to run. It does not switch threads by itself. The actual switch happens at a safe point, usually when the handler returns, or when the current thread reaches a point where it can be preempted.
The scheduler then asks a plain question. Who should run now. It looks at the runnable threads, their priorities, how long each has waited, and whether the current thread still has slice left. A thread that was waiting on disk or a socket may be runnable now. A higher priority thread may have woken up since the last tick. The current thread may keep the CPU if nothing better is waiting.
This is also where the delay hides. The interrupt fired on time. The scheduler ran on time. But your thread still did not run, because another thread won the decision. The timer did its job. The scheduler did its job. Your thread simply lost this round.
The switch
When the scheduler picks a new thread, it does a context switch. It saves the outgoing thread's registers and stack pointer into that thread's saved state. It loads the incoming thread's stack pointer and registers. If the two threads live in different address spaces, it loads a new CR3 so the page tables change too. Then it returns from the interrupt, and the return lands in the new thread, at the instruction where that thread was last stopped.
From the thread's point of view, nothing happened. It was stopped mid-instruction at some earlier point, and now it continues as if no time passed. It never sees the interrupt, the handler, or the scheduler. The whole handoff is invisible to it.
That invisibility is the point. A thread cannot tell whether it was stopped for one tick or one thousand. It only sees its own code running. So when a thread is late, the thread itself has no useful story about why.
What fills the gap
The time between the interrupt and your thread running is scheduling latency, and it has a few usual sources. The handler and the scheduler take real time. A higher priority thread may run first. The kernel may hold interrupts off in a short critical section, so the tick waits. A thread may be picked and then block at once on a lock, which sends the CPU to yet another thread.
Each of these is ordinary. None of them means the timer is broken or the thread is slow. They mean the CPU was busy doing the job it was told to do first.
How I read a late thread
When a thread runs late, I do not start by looking at the thread's code. I look at its state at the time it was late. Was it runnable and waiting for the CPU, or was it blocked on something else. How many runnable threads were ahead of it. Whether interrupts were off for a long stretch. Whether a higher priority thread was eating the CPU.
CPU time and wall time answer different questions. A thread with low CPU time and high wall time was not slow. It was waiting. The fix for a waiting thread is almost never to make the thread faster. It is to change what it waits on, or to change who else is allowed to run ahead of it.
The timer is a heartbeat, not a guarantee. It gives the kernel a regular chance to reconsider. What happens after that chance is a decision, a switch, and a queue. If you want to know why a thread ran when it did, follow those three, in that order.
Top comments (0)