Skip to content

Release runtime keepalive in emscripten_clear_timeout and make clear_timeout/clear_immediate idempotent - #27720

Merged
guybedford merged 6 commits into
emscripten-core:mainfrom
guybedford:timeout-keepalive
Sep 17, 2026
Merged

guybedford merged 6 commits into
emscripten-core:mainfrom
guybedford:timeout-keepalive

Conversation

@guybedford

@guybedford guybedford commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Resolves #23763.

This fixes a runtime keepalive leak in emscripten_clear_timeout and makes emscripten_clear_timeout / emscripten_clear_immediate safe to call for ids that have already fired.

emscripten_set_timeout goes through safeSetTimeout, which does runtimeKeepalivePush() and pops in the fire wrapper. emscripten_clear_timeout was aliased straight to clearTimeout, so clearing a pending timer leaked one keepalive forever: with EXIT_RUNTIME=1 the runtime never exits after main returns (no atexit handlers run, emscripten_runtime_keepalive_check() stays true). emscripten_clear_immediate had the mirror bug: it popped unconditionally, so clearing an already-fired immediate double-popped the counter.

  • safeSetTimeout now follows the setImmediateWrapped.mapping pattern: it returns an i32 index into safeSetTimeout.mapping holding the native handle, and the fire wrapper clears the slot before popping the keepalive. Ids start at 1 so, like setTimeout, they are never zero.
  • New $safeClearTimeout(id) pops and clears only if the slot is still live, otherwise it is a no-op. emscripten_clear_timeout aliases it, which also removes the AUDIO_WORKLET special case. SDL_PauseAudio uses it too instead of a raw clearTimeout, which had the same leak.
  • emClearImmediate (both the setImmediate and postMessage variants) reports whether the immediate was still pending, and emscripten_clear_immediate pops only in that case.

Tests: other.test_emscripten_clear_timeout_{fires,cleared,idempotent,immediate} cover clearing a pending timeout (runtime exits from main immediately with atexit running), clearing the same id twice and clearing an id after it fired while another pending timer keeps the runtime alive, the existing set-then-fire behaviour, and the same idempotency for immediates. Three of the four fail without the fix.

Made with AI assistance under my review

Comment thread src/lib/libeventloop.js
@guybedford
guybedford force-pushed the timeout-keepalive branch 2 times, most recently from 4a4a2ec to 88ad1ef Compare September 16, 2026 19:50
Comment thread ChangeLog.md Outdated
Comment thread test/test_other.py Outdated
@guybedford
guybedford merged commit d602fa6 into emscripten-core:main Sep 17, 2026
42 checks passed
@guybedford
guybedford deleted the timeout-keepalive branch September 17, 2026 00:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

emscripten_clear_timeout fails to clear/pop the keepalive counter.

2 participants