EventLoop: formalize driver contract and add public swapDriver API#12998
Open
kevinresol wants to merge 1 commit into
Open
EventLoop: formalize driver contract and add public swapDriver API#12998kevinresol wants to merge 1 commit into
swapDriver API#12998kevinresol wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Goal
#12492 deliberately kept native loop integration private: EventLoop gained a structural hook (
nativeLoop) but no supported way to install or swap a waiter from user or library code. That was the right tradeoff for inverting the HL dependency at the time.More targets now ship or plan libuv bindings (Lua, eval stdlib, C++ via linc_uv and similar). This PR closes that gap by formalizing the driver contract and exposing a safe public API (
EventLoopDriver,swapDriver, factory/ctor hooks) to plug in a loop driver without@:privateAccessor target-specific internals.Summary
nativeLoopsidecar with a public, mandatoryEventLoopDriverthat owns waiting (wait(previouslyrun) /wake/close/hasExternalWork(previouslyisAlive())).EventLoopa publicswapDriverAPI (plusDEFAULT_DRIVER_FACTORY/ ctor injection) so native waiters such as libuv can be installed explicitly and safely.UvEventLoopDriverandLoop.getFromEventLoop→swapDriver, removing thenativeLoopprivate access.Blocking/
maxBlock/wakepoller details for native loops are covered in #12993; this PR is about the abstraction and public swap surface.Rationale
After #12492 inverted the dependency so EventLoop no longer imports
hl.uv, native integration still looked like an internal bolt-on:NativeEventLoopwas assigned with@:privateAccess loop.nativeLoop = ….Lock/Sys.sleepon the loop; the native side was optionally polled fromloopOnce.That made UV (and any future native loop) a special case rather than a first-class waiting strategy. Concretely, this PR:
__init__/mainconstruction) and later install UV whengetFromEventLoop(or user code) asks for it.EntryPointremains the platform pump (who callsloop()/loopOnce()). Thread create APIs are unchanged; custom drivers are installed withswapDriveron that thread’s loop.Design
Public API
haxe.EventLoopDriverHaxeEventLoopDriverEventLoop.DEFAULT_DRIVER_FACTORYnew EventLoop(?driver)omits a drivernew EventLoop(?driver)swapDriver(driver, ?onSwapped)getDriver()/getPendingDriver()hl.uv.UvEventLoopDriverhl.uv.Loop.getFromEventLoopLoop+swapDriverinstallswapDriverrulesthreadunset) and not insidewait/loopOnce.applyPendingSwap(start of eachloop()iteration / end ofloopOnce), andwake()the current driver so a blocked wait can progress.close()s any superseded pending driver and drops its callback.close()old, so concurrentwakeupnever targets a just-closed driver.onSwappedalways runs on the loop thread.loop()cannot idle-exit between e.g. socket creation and driver publish (important becausegetFromEventLoopis often called withinLoop == true, so install is deferred).UV attach
getFromEventLoopstill returns the concretehl.uv.Loopsynchronously (required by AsyncSocket / Tcp / Fs). It creates aUvEventLoopDriver(isDefaultfor main’sdefault_loop), callsswapDriver, and reuses an already current or pending UV driver (idempotent). Closing a default-loop driver does notuv_loop_closethe process-global loop.Implementation notes
NativeEventLoop/nativeLoopfromEventLoop.loop()always goes throughdriver.wait(...);loopOnceruns Haxe callbacks only (no blocking wait — preserves JS/Flash EntryPoint).dispose()closes pending + current drivers and leaves a closed non-null driver so later cross-threadwakeupcannot NPE.Timerno longer references removedcreateWithEventLoop/runWithEventLoop.For how native
wait(maxBlock)/wakeinteract with Haxe deadlines and cross-thread enqueue, see #12993.