Skip to content

Fix timeout handling in hl_condition_timed_wait - #1008

Open
Simn wants to merge 5 commits into
masterfrom
claude/jolly-wozniak-93trgh
Open

Simn wants to merge 5 commits into
masterfrom
claude/jolly-wozniak-93trgh

Conversation

@Simn

@Simn Simn commented Nov 1, 2026

Copy link
Copy Markdown
Member

hl_condition_timed_wait will back the new sys.thread.Condition.wait(?timeout:Float):Bool in HaxeFoundation/haxe#13109. Its result must be false exactly when the timeout expired. This PR fixes three problems:

  • Windows: the function always returned true and ignored the result of SleepConditionVariableCS. It now returns false when the wait fails with ERROR_TIMEOUT.
  • Clamping: timeouts are clamped to [0, 1e9] seconds, and NaN counts as 0. Before, a negative timeout cast to DWORD became an almost infinite wait on Windows. On POSIX, a timeout above INT_MAX seconds overflowed the int conversion, so the wait timed out immediately.
  • POSIX deadline: now computed with 64-bit integers.

The !HL_THREADS branch and the function signature are unchanged.

Testing

  • make libhl.so builds with no warnings in thread.c.
  • I ran a small C program against libhl.so on Linux, before and after the change:
Case Before After
0.1 s, no signal false after 0.1 s false after 0.1 s
0, -1, NaN, -1e300 false at once false at once
1.9999999 s false after 2.0 s false after 2.0 s
1e12 s, signalled after 0.1 s false at once (overflow) true after 0.1 s

Generated by Claude Code

- Windows: return false when SleepConditionVariableCS times out instead
  of always returning true.
- Clamp timeouts to [0, 1e9] seconds, treating NaN as 0. A negative
  timeout used to become an almost infinite wait on Windows, and on POSIX
  timeouts above INT_MAX seconds overflowed and timed out immediately.
- Compute the POSIX deadline with 64-bit integers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va4FXDYWEo4mt53psuM5hu
Simn pushed a commit to HaxeFoundation/haxe that referenced this pull request Nov 1, 2026
hl uses the existing `condition_timed_wait` native and clamps the timeout
itself, because older runtimes don't. CI builds the HashLink branch with
the native fixes until HaxeFoundation/hashlink#1008 is merged.

neko uses the new `cond_timed_wait` primitive when it is available
(HaxeFoundation/neko#336), and otherwise polls with growing sleeps,
using a counter that `signal` and `broadcast` increment to detect
notifications.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va4FXDYWEo4mt53psuM5hu

Simn commented Nov 1, 2026

Copy link
Copy Markdown
Member Author

haxe-test-linux (linux, x86_64) fails, but the cause isn't this PR's change. The job stops in its apt-get install step, before any HashLink code runs:

E: Package 'libturbojpeg' has no installation candidate
E: Unable to locate package libmbedtls14t64

The job ran on the ubuntu-26.04 runner image (Version: 20261025.179.1), which ubuntu-latest now resolves to. Those package names are from Ubuntu 24.04. The same job passed on master on 30 October (run 41069619334), before that switch. A re-run would fail the same way.

I didn't find an existing fix, so I've left it out of this PR. There are two options, and both are workflow changes:

  • update the runtime package names in build.yml for 26.04, or
  • pin haxe-test-linux (and build-linux, so the binaries are linked against the same libraries) to ubuntu-24.04.

Generated by Claude Code

@Simn

Simn commented Nov 2, 2026

Copy link
Copy Markdown
Member Author

#1009 has been merged, please update the branch (if you're still watching...)!

Simn commented Nov 2, 2026

Copy link
Copy Markdown
Member Author

Merged master (with #1009) into the branch in 1a74abf. The merge only brings in the build.yml runner pin, so haxe-test-linux (linux, x86_64) should run on ubuntu-24.04 again.


Generated by Claude Code

Simn commented Nov 2, 2026

Copy link
Copy Markdown
Member Author

Also, I am always watching. 👀


Generated by Claude Code

Simn commented Nov 2, 2026

Copy link
Copy Markdown
Member Author

The pull_request run for 1a74abf is fully green, including haxe-test-linux (linux, x86_64) on the pinned ubuntu-24.04 runner.

In the push run of the same commit, build-windows (windows, 64, cmake-clang-cl) and build-windows (windows, 64, vs2026) failed while downloading SDL, before anything was compiled: curl: (35) Recv failure: Connection was reset and Failed to connect to www.libsdl.org:443. Those two jobs passed in the pull_request run, so it's a network problem rather than this change. I've re-run the failed jobs once.


Generated by Claude Code

SleepConditionVariableCS takes a DWORD of milliseconds, so waits of more
than about 49.7 days are capped. A capped wait that runs out had not
reached the requested timeout, so return true like a spurious wakeup
instead of false, which means that the timeout expired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va4FXDYWEo4mt53psuM5hu
@Simn

Simn commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Please merge development again!

Simn commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Merged master again in 7fb2e50, including #1012, so the Windows jobs now get SDL from GitHub. The merge had no conflicts and doesn't touch src/std/thread.c. libhl.so still builds on Linux, and the timed-wait checks (timeout, non-positive/NaN, 1e12 s woken by a signal) behave as before.


Generated by Claude Code

@yuxiaomao

Copy link
Copy Markdown
Collaborator

The current change seems good to me.

Claude analysis point out 2 additional points which seems valid for me too, please check:

  • Any Windows failure other than ERROR_TIMEOUT returns true (counted as “woken”), while the POSIX path returns false for any error.
  • Same bug int overflow bug in two other functions. The same broken timeout code is still in hl_semaphore_try_acquire (lines 248-278) and hl_lock_wait (lines 778, 797-806).

@Simn

Simn commented Nov 4, 2026

Copy link
Copy Markdown
Member Author
  • Any Windows failure other than ERROR_TIMEOUT returns true (counted as “woken”), while the POSIX path returns false for any error.

I'll check that, might be legit.

  • Same bug int overflow bug in two other functions. The same broken timeout code is still in hl_semaphore_try_acquire (lines 248-278) and hl_lock_wait (lines 778, 797-806).

Yes but I don't want to deal with this here. Let's make one function that is implemented properly and then use it as a reference for the others as a follow-up.

The Windows timed wait returned true for every error other than
ERROR_TIMEOUT, while the POSIX path returns false for any error. Such
errors are misuse that should not occur, and returning true would make a
predicate loop spin on a persistent error, so return false like on POSIX.
Only a capped wait that runs out still counts as a spurious wakeup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Va4FXDYWEo4mt53psuM5hu

Simn commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

Thanks for the review, @yuxiaomao. The first point was real, and it's fixed in 3876c8d:

return ok || (capped && err == ERROR_TIMEOUT);

Windows now returns false for every error except a capped wait running out, matching POSIX (pthread_cond_timedwait(...) == 0). The remaining errors (EINVAL/EPERM on POSIX, anything other than ERROR_TIMEOUT on Windows) mean misuse and shouldn't happen once the timeout is clamped. If one does persist, returning true would make a caller's predicate loop spin, while false ends the wait. The capped case (more than ~49.7 days) still returns true, because the requested time hasn't passed yet and a spurious wakeup is the honest answer. I made the same change in the matching Neko code (HaxeFoundation/neko#336). The Windows branch compiles with x86_64-w64-mingw32-gcc -fsyntax-only.

On the second point, I'm leaving hl_semaphore_try_acquire and hl_lock_wait as they are in this PR, as Simn said. Once this is merged, clamp_wait_timeout and the 64-bit deadline computation here can be reused for both in a follow-up.


Generated by Claude Code

@yuxiaomao

Copy link
Copy Markdown
Collaborator

Seems good to me, please feel free to merge.

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.

3 participants