This may be a very simple question. I’m curious how blocking calls are implemented. Specifically, how do they block? Is this just thread.sleep?
2
Specifically, I was implementing a socket class in C# and one of the
.Net socket methods blocks until data is received. I was just curious
how that works.
There are several ways to implement a blocking call. The obvious way to do it is to return when the work is done See Robert Harvey’s Answer.
In the case where there is no work to be done (e.g., waiting for a signal or for input), there are several choices:
- Spinlock. Basically, code like
while(signal not found){}
. Spinlocks have almost no overhead and return faster than other methods, but burn CPU until they return. They are useful if the signal you are waiting for is going to return fast or if giving up a timeslice is to be avoided, but are generally a bad idea in high-level code. - Locks, Mutexes, etc. In C#, these are accessed with things like
lock(obj){}
(cause other threads to block) andManualResetEvent
(block until another thread signals). Generally, there are implemented at the kernel or hardware level. E.g., C# lock is implemented on x86 machines using the assemblylock cmpxchg
assembly instruction (see Junfeng Zhang’s blog).
0
Methods don’t return
control to the caller until their work is completed, that’s all. In your socket class example, the method doesn’t return
the data until it actually receives it.
In most cases, where you’re seeing something like Thread.Sleep(1000)
in a code example, you are not seeing blocking per se, but merely simulating work.
Where Thread.Sleep
is being used legitimately, it is relinquishing control to other threads, with an implicit statement: “I can wait awhile before you return control to me.” Thread.Sleep
is not required; the operating system will normally preempt the thread for short periods of time anyway, if work on other threads also needs to be completed.
1
In most system calls on most OS’s, your blocking call ultimately causes some request to go out to be executed by the kernel while the calling thread is suspended by the scheduler/dispatcher until that work is complete. Typically this work is some kind of IO request to peripheral hardware (ie network card/hard disk/etc). The peripheral hardware can use a processor interrupt to signal the CPU/kernel that some piece of work is complete (or some other piece of info about the work). Once the work is complete, the kernel wakes up the calling thread and allows execution to continue from where it was waiting. The hardware/kernel together have prepared any outputs (buffers/errors etc) for the user-facing calling function and these outputs are handed back to the caller.
The specific details of how this happens are going to be very different depending on the underlying OS. For a deep dive into how Windows works, I recommend: Scheduling, Thread Context, and IRQL.
1
Simple answer: a blocking call will either perform an I/O request and wait for it to be completed, or it will use some form of “mutual exclusion object” such as a semaphore. Or, it might wait for a signal. In any case, the process or thread drops out of the dispatcher’s run-list until the waited-for thing happens.