I hence wonder, when threads are waiting for I/O operations, e.g., user input, network I/O, disk I/O, etc, is it guaranteed that they'll yield the CPU resources to others?
Not that I know of, at least, when the thread is doing non-blocking I/O or polling, such as repeatedly checking whether I/O is ready, it may remain active and consume CPU resources.
If the answer is not, is there any way forcing them to do so? I noticed The scheme shall be able to be achieved by C, low-level hacking is acceptable.
If your OS is UNIX, maybe sched_yield is what you are looking for, provided that you are well aware of under what scenarios the thread is supposed to yield.