My question is about nodejs event loop
Consider this code
(async () => {
let val = 1
const promise = new Promise(async resolve => {
resolve()
await new Promise(async r => {
setTimeout(r)
})
await promise
val = 2
})
await promise
await new Promise(resolve => setTimeout(resolve))
console.log(val)
})()
With node 10.20.1 (latest version of node 10)
for ((i = 0; i < 30; i++)); do /opt/node-v10.20.1-linux-x64/bin/node race-timeout.js; done
With node 12.0.0 (the first version of node 12)
for ((i = 0; i < 30; i++)); do /opt/node-v12.0.0-linux-x64/bin/node race-timeout.js; done
The result of node 10
1
2
2
1
1
2
2
1
2
1
1
1
1
1
2
1
1
2
1
2
1
1
2
2
1
2
1
1
2
1
The result of node 12
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
So far, I have known that node is a single-thread language.
Everything is well-determined and executed in an exact order except when there is an intervention of the poll phase.
The above code does not include any undetermined factors (like IO, network, ...).
I expected that the result should be the same. However, with node v10, it is not.
What is the random factor in node v10?
It is all explained here.
In a nutshell, before v.11 calling Promise.resolve().then( fn1 ); setTimeout( fn2, 0 ); could lead to fn2 being pushed to the queue of timers belonging to the current event loop iteration, and thus, fn2 would fire before the event loop even entered the nextTicks queue (a.k.a "microtask-queue").
According to one comment there, the discrepancy came from the fact "the timers could get set on a different ms and thus expire 1ms apart", thus pushing fn2 to a next event loop iteration and leaving the nextTicks queue being processed in between.
In other words, v10 should most of the time output 1, except when the main job was calling setTimeout in the next ms, for instance if the main job was called #ms 12.9, and that it took more than 0.1ms to get to the setTimeout call, then this setTimeout's 0 value would actually match the next timer queue (#ms 13), and not the current one (#ms 12).
In next versions, nextTicks are ran after each immediates and timers, hence we are sure the Promise's callback will get executed right after the job that did execute it, just like in browsers.
Related
According to the documentation, a delay suspends a coroutine but doesn't block a thread:
Delay is a special suspending function. It suspends the coroutine for a specific time. Suspending a coroutine does not block the underlying thread, but allows other coroutines to run and use the underlying thread for their code.
However when running this code:
fun main() {
println("1")
GlobalScope.launch {
doSomething()
delay(1000L)
println("3")
}
GlobalScope.launch {
delay(1000L)
println("4")
}
println("5")
}
suspend fun doSomething() {
delay(500L)
println("2")
delay(1000L)
}
the output given is:
1
5
2
4
3
According to my understanding, it should have shown 1 5 4 2 3.
That's because doSomething() is being called in the first coroutine and so the delay is more as compared to the second coroutine and the second coroutine is called asynchronously. Where is my misunderstanding?
Also with this piece of code, both the values are printed together after 2 seconds:
fun main() {
GlobalScope.launch {
val value1 = doSomething1()
val value2 = doSomething2()
println(value1)
println(value2)
}
}
suspend fun doSomething1(): String {
delay(1000L)
return "This is the answer1"
}
suspend fun doSomething2(): String {
delay(1000L)
return "This is the answer2"
}
Why is this?
TL;DR
No, delays in different jobs don't affect each other.
For your first problem, clearly 1 and 5 are first and last so we can ignore those. So for the rest of them we have:
Print
Delays
Total delay
2
500
500
3
500, 1000, 1000
2500
4
1000
1000
So the order would be 2, 4, 3 which you've verified with the output.
If you want to see a bit more how the coroutines are running, we can consider what's happening with each job at roughly each time step:
Time
Job 1
Job 2
0
delay(500L)
delay(1000L)
500
print("2"); delay(1000L)
-
1000
-
print("4")
1500
delay(1000L)
-
2000
-
-
2500
print("3")
-
To address your second problem, you can just write it as one block of code:
GlobalScope.launch {
delay(1000L)
val value1 = "This is the answer1"
delay(1000L)
val value2 = "This is the answer2"
println(value1)
println(value2)
}
Clearly the print statements would run one after another. Just because there was a delay in obtaining the values, doesn't mean there is a delay between printing them. The behaviour that I think you're expecting would occur if the print statements were in the two doSomething methods.
so the delay is more as compared to the second coroutine
No, the first delay is: 500L,
and the second delay is: 1000L
Node 11.0.0 adds queueMicrotasks as experimental. The doc says it is similar to process.nextTick but the queue is managed by V8 instead of Node.js. What would be the use case to use queueMicrotasks instead of process.nextTick? Will there be any performance gain using one over another?
Queues
We can find different queues (shown in check priority order after function/script execution):
nextTick
microTasks
timers (expired)
immediate
How the queues are checked?
First, nextTick queue is checked to get tasks for its execution, once it is exhausted, the microTasks queue is checked. After finishing the tasks in the microtask queue the process of checking
nextTick and microTasks queues are repeated until the queues have been emptied.
The next queue to check is the timers one and in the end the immediate queue.
Differences
It is the same, in the way of both of them are to execute a task just after the execution of the current function or script.
They have different queues. The nextTick's queue is managed by node and the microtask one is managed by v8.
What it means?
The nextTick queue is checked first after the current function/script execution, and then the microTask one.
There is no performance gain, the difference is that the nextTick queue will be checked first after the function/script execution and that must be taken into account.
If you never use nextTick and only use queueMicrotask you will have the same behavior of just using nextTick (taking into account that your tasks will be placed in a queue with other microtasks).
The use case could be to execute tasks before any microtask, for example, before a promise's then and/or catch. It is worth noting that promises use microtask so, any callback added to then/catch will be added to the microtask queue and its execution will be performed when the nextTick queue is empty.
Example
After the execution of this code:
function task1() {
console.log('promise1 resolved');
}
function task2() {
console.log('promise2 resolved');
process.nextTick(task10);
}
function task3() {
console.log('promise3 resolved');
}
function task4() {
console.log('immediate 1');
}
function task5() {
console.log('tick 1');
}
function task6() {
console.log('tick 2');
}
function task7() {
console.log('microtask 1');
}
function task8() {
console.log('timeout');
}
function task9() {
console.log('immediate 2');
}
function task10() {
console.log('tick3');
queueMicrotask(task11);
}
function task11() {
console.log('microtask 2');
}
Promise.resolve().then(task1);
Promise.resolve().then(task2);
Promise.resolve().then(task3);
setImmediate(task4);
process.nextTick(task5);
process.nextTick(task6);
queueMicrotask(task7);
setTimeout(task8, 0);
setImmediate(task9);
Execution
nextTick: task5 | task6
microTasks: task1 | task2 | task3 | task7
timers: task8
immediate: task4 | task9
Step 1: execute all tasks in nextTick queue
nextTick: EMPTY
microTasks: task1 | task2 | task3 | task7
timers: task8
immediate: task4 | task9
output:
tick 1
tick 2
Step 2: execute all tasks in microTasks queue
nextTick: task10
microTasks: EMPTY
timers: task8
immediate: task4 | task9
output:
tick 1
tick 2
promise 1 resolved
promise 2 resolved
promise 3 resolved
microtask 1
Step 3: execute all tasks in nextTick queue (there is a new task added by the execution of microtask (task2))
nextTick: EMPTY
microTasks: task11
timers: task8
immediate: task4 | task9
output:
tick 1
tick 2
promise 1 resolved
promise 2 resolved
promise 3 resolved
microtask 1
tick 3
Step 4: execute all tasks in microTasks queue (there is a new task added by the execution of task10)
nextTick: EMPTY
microTasks: EMPTY
timers: task8
immediate: task4 | task9
output:
tick 1
tick 2
promise 1 resolved
promise 2 resolved
promise 3 resolved
microtask 1
tick 3
microtask 2
Step 5: No more tasks in nextTick and microTasks queues, next execute timers queue.
nextTick: EMPTY
microTasks: EMPTY
timers: EMPTY
immediate: task4 | task9
output:
tick 1
tick 2
promise 1 resolved
promise 2 resolved
promise 3 resolved
microtask 1
tick 3
microtask 2
timeout
Step 6: No more tasks in (expired) timers queue, execute tasks in immediate queue
nextTick: EMPTY
microTasks: EMPTY
timers: EMPTY
immediate: EMPTY
output:
tick 1
tick 2
promise 1 resolved
promise 2 resolved
promise 3 resolved
microtask 1
tick 3
microtask 2
timeout
immediate 1
immediate 2
As we can see there is no performance reason to choose one or another; the choice depends on our needs and what needs to be done and when.
Imagine this code:
let i = 1;
queueMicrotask(() => console.log(i));
process.nextTick(() => i++);
The output will be 2 due to the nextTick queue getting checked first.
but if you do
let i = 1;
queueMicrotask(() => console.log(i));
process.nextTick(() => queueMicrotask(() =>i++));
You will get 1.
With examples, I want to make you see that the use-cases come from your needs of what and when you need to perform a task. And the important thing is taking into account that then/catch callbacks in a promise are microtasks and will be executed after nextTick tasks; this is important for avoiding errors (as shown in the previous example).
The code below executes a long running function (a sleep to keep it simple), then calls back itself again using a setTimeout. I am using nodejs 5.1.0
var start_time = Math.ceil(new Date() /1000)
function sleep(delay) {
var start = new Date().getTime();
while (new Date().getTime() < start + delay);
}
function iteration(){
console.log("sleeping at :", Math.ceil(new Date()/1000 - start_time));
sleep(10000); // sleep 10 secs
console.log("waking up at:", Math.ceil(new Date()/1000 - start_time));
setTimeout(iteration, 0);
}
iteration();
At the moment where setTimeout is called node is not doing anything (eventloop empty in theory?) so I expect that callback to be executed immediately. In other terms, I expect the program to sleep immediately after it wakes up.
However here is what happens:
>iteration();
sleeping at : 0
waking up at: 10
undefined
> sleeping at : 10 //Expected: Slept immediately after waking up
waking up at: 20
sleeping at : 30 //Not expected: Slept 10 secs after waking up
waking up at: 40
sleeping at : 50 //Not expected: Slept 10 secs after waking up
waking up at: 60
sleeping at : 70 //Not expected. Slept 10 secs after waking up
waking up at: 80
sleeping at : 90 //Not expected: Slept 10 secs after waking up
waking up at: 100
sleeping at : 110 //Not expected: Slept 10 secs after waking up
As you can see, the program sleeps immediately after it wakes up the first time (which is what I expect), but not after that. In fact, it waits exactly another 10 seconds between waking up and sleeping.
If I use setImmediate things work fine. Any idea what I am missing?
EDIT:
Notes:
1- In my real code, I have a complex long algorithm and not a simple sleep function. The code above just reproduces the problem in a simpler use case.
That means I can't use setTimeout(iteraton, 10000)
2- The reason why I use setTimeout with a delay of 0 is because my code blocks nodejs for so long that other smaller tasks get delayed. By doing a setTimeout, nodejs gets backs to the event loop to execute those small tasks before continuing to work on my main long running code
3- I completely understand that setInterval(function, 0) will run as soon as possible and not immediately after, however that does not explain why it runs with 10 seconds delay given that nodejs is not doing anything else
4- My code in Chrome runs without any problems. Nodejs bug?
First look at the node.js documenation:
It is important to note that your callback will probably not be called
in exactly delay milliseconds - Node.js makes no guarantees about the
exact timing of when the callback will fire, nor of the ordering
things will fire in. The callback will be called as close as possible
to the time specified.
To make your function more async you could make something like:
function iteration(callback){
console.log("sleeping at :", Math.ceil(new Date()/1000));
sleep(10000); // sleep 10 secs
console.log("waking up at:", Math.ceil(new Date()/1000));
return setTimeout(callback(iteration), 0);
}
iteration(iteration);
Next problem is your sleep function. You have a while loop blocking all your node.js processes to make something the setTimeout function of node.js does for you.
function sleep(delay) {
var start = new Date().getTime();
while (new Date().getTime() < start + delay);
}
That what happens now is that your sleep function is blocking the process. It is not only happening when you call the function directly it is also happening, when the scope of the function is initialised after the async call of setTimeout, by node.js.
And that is not a bug. It has to be like that, because inside your while loop could be something your iteration functions has to know. Specially when it is called after a setTimeout. So first the sleep function is checked and as soon as possible the function is called.
Edited answer after question change
We have following kernel parameters:
sysctl_sched_min_granularity = 0.75
sysctl_sched_latency = 6 ms
sched_nr_latency = 8
What I understand (I don't know if correctly), parameter sysctl_sched_latency says, that all tasks in runqueue should be executed in time 6ms.
So if the task arrives in time X the task should be executed not later than X + 6 ms at least once.
The function check_preempt_tick is executed periodically by task_tick_fair.
At the beginning of this function we checks whether delta_exec is not greater then ideal_runtime:
ideal_runtime = sched_slice(cfs_rq, curr);
delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
if (delta_exec > ideal_runtime)
For instance:
if we have 4 tasks (the same priority) ideal_runtime should returns 1.5 ms for the first task.
Let assume that this task has executed for 1.5 ms and exit.
So we have 3 tasks in the queue right now.
The second calculation for task2 is like that:
we have 3 tasks in a queue so time of ideal_runtime should be 2 ms.
Task2 has run for 2 ms and exit.
Again
task3 calculates time and we have
2 tasks in the queue
so it should run for 3ms.
So in summary
task4 will be executed after 1.5 ms (task1) + 2ms (task2) + 3ms (task3)
so sysctl_sched_latency is exceeded (which is 6 ms).
How CFS does ensure that all task in queue are executed in sysctl_sched_latency time, when queue can change dynamically all time?
Thanks,
I have a queue of 1000 work items and a n-proc machine (assume n =
4).The main thread spawns n (=4) worker threads at a time ( 25 outer
iterations) and waits for all threads to complete before processing
the next n (=4) items until the entire queue is processed
for(i= 0 to queue.Length / numprocs)
for(j= 0 to numprocs)
{
CreateThread(WorkerThread,WorkItem)
}
WaitForMultipleObjects(threadHandle[])
The work done by each (worker) thread is not homogeneous.Therefore in
1 batch (of n) if thread 1 spends 1000 s doing work and rest of the 3
threads only 1 s , above design is inefficient,becaue after 1 sec
other 3 processors are idling. Besides there is no pooling - 1000
distinct threads are being created
How do I use the NT thread pool (I am not familiar enough- hence the
long winded question) and QueueUserWorkitem to achieve the above. The
following constraints should hold
The main thread requires that all worker items are processed before
it can proceed.So I would think that a waitall like construct above
is required
I want to create as many threads as processors (ie not 1000 threads
at a time)
Also I dont want to create 1000 distinct events, pass to the worker
thread, and wait on all events using the QueueUserWorkitem API or
otherwise
Exisitng code is in C++.Prefer C++ because I dont know c#
I suspect that the above is a very common pattern and was looking for
input from you folks.
I'm not a C++ programmer, so I'll give you some half-way pseudo code for it
tcount = 0
maxproc = 4
while queue_item = queue.get_next() # depends on implementation of queue
# may well be:
# for i=0; i<queue.length; i++
while tcount == maxproc
wait 0.1 seconds # or some other interval that isn't as cpu intensive
# as continously running the loop
tcount += 1 # must be atomic (reading the value and writing the new
# one must happen consecutively without interruption from
# other threads). I think ++tcount would handle that in cpp.
new thread(worker, queue_item)
function worker(item)
# ...do stuff with item here...
tcount -= 1 # must be atomic