Skip to content

Commit 1e6abe7

Browse files
authored
Merge pull request #1079 from nevkontakte/event-starvation
Prevent event loop starvation by always scheduled goroutines.
2 parents f9e992d + 2676098 commit 1e6abe7

File tree

3 files changed

+69
-3
lines changed

3 files changed

+69
-3
lines changed

compiler/prelude/goroutines.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,28 @@ var $go = function(fun, args) {
159159
160160
var $scheduled = [];
161161
var $runScheduled = function() {
162+
// For nested setTimeout calls browsers enforce 4ms minimum delay. We minimize
163+
// the effect of this penalty by queueing the timer preemptively before we run
164+
// the goroutines, and later cancelling it if it turns out unneeded. See:
165+
// https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#nested_timeouts
166+
var nextRun = setTimeout($runScheduled);
162167
try {
168+
var start = Date.now();
163169
var r;
164170
while ((r = $scheduled.shift()) !== undefined) {
165171
r();
172+
// We need to interrupt this loop in order to allow the event loop to
173+
// process timers, IO, etc. However, invoking scheduling through
174+
// setTimeout is ~1000 times more expensive, so we amortize this cost by
175+
// looping until the 4ms minimal delay has elapsed (assuming there are
176+
// scheduled goroutines to run), and then yield to the event loop.
177+
var elapsed = Date.now() - start;
178+
if (elapsed > 4 || elapsed < 0) { break; }
166179
}
167180
} finally {
168-
if ($scheduled.length > 0) {
169-
setTimeout($runScheduled, 0);
181+
if ($scheduled.length == 0) {
182+
// Cancel scheduling pass if there's nothing to run.
183+
clearTimeout(nextRun);
170184
}
171185
}
172186
};

0 commit comments

Comments
 (0)