How does libuv and the operating system actually schedule timers like setTimeout and setInterval in Node.js? I see that no CPU is used by the node process until a timer fires. Does this mean the OS schedules the timer, and wakes up the Node process when the timer is fired? If so, how does an OS schedule a timer and how exactly does the hardware execute it?
Timer callbacks are executed as part of the NodeJS event loop. When you call setTimeout
or setInterval
, libuv (the C library which implements the NodeJS event loop) creates a timer in a 'min heap' data structure which is called the timers heap. In this data structure, it keeps track of the timestamp that each timer expires at.
At the start of each fresh iteration of the event loop, libuv calls uv__update_time
which in turn calls a syscall to get the current time and updates the current loop time up to a millisecond precision. (https://github.com/nodejs/node/blob/master/deps/uv/src/unix/core.c#L375)
Right after that step comes the first major phase of the event loop iteration, which is timers phase. At this phase, libuv calls uv__run_timers
to process all the callbacks of expired timers. During this phase, libuv traverses the timers heap to identify expired timers based on the 'loop time' it just updated using uv__update_time
. Then it invokes the callbacks of all those expired timers.
Following is a redacted snippet from the event loop implementation in NodeJS to highlight what I just described.
while (r != 0 && loop->stop_flag == 0) {
uv__update_time(loop);
uv__run_timers(loop);
// ...redacted for brevity...
r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
break;
}
I wrote a series of articles on NodeJS event loop some time back. I hope this article from that series would be helpful. https://blog.insiderattack.net/timers-immediates-and-process-nexttick-nodejs-event-loop-part-2-2c53fd511bb3
Node uses libuv underneath to take care of this. While setTimeout has some internal managing of its own, it ends up using the uv_timer_t facility provided by libuv.
Lets assume that the only thing the event loop is doing is the timer. libuv will calculate the poll timeout, which will actually be the timer's due time (in this example). Then the event loop will block for i/o by using the appropriate syscall (epoll_wait, kevent, etc). At that point it's up to the kernel to decide what to do, but the current thread of execution is blocked until the kernel wakes it up again, so there is no used CPU here, because nothing is happening.
Once the timeout expires, the aforementioned syscall will return, and libuv will process the due timers and i/o.
© 2022 - 2024 — McMap. All rights reserved.