Skip to content

Pool workers sleep until woken, and UUIDs are random v4 without a lock - #596

Merged
RyeMutt merged 2 commits into
developfrom
rye/pool-scheduler-uuid
Oct 28, 2026
Merged

RyeMutt merged 2 commits into
developfrom
rye/pool-scheduler-uuid

Conversation

@RyeMutt

@RyeMutt RyeMutt commented Oct 28, 2026

Copy link
Copy Markdown
Member

Two follow-ups to the lock-type sweep (#591), one commit each.

Pool workers sleep until work or a deadline wakes them

On Windows every pool thread ran sleepy_robin, whose wait was Sleep(1) and whose wake did nothing. An idle worker woke every tick of the system timer to look for work, and picked work up, or came back from a fiber's timed wait, only on a tick. The viewer's window asks for 1 ms ticks, so each idle pool thread woke about a thousand times a second. Windows 11 gives a process whose window is hidden the full 15.6 ms tick, whatever it asked for.

sleepy_robin now waits on an auto-reset event, which a fiber made ready from another thread sets. Where a fiber's timed wait sets a deadline, it also waits on a high-resolution waitable timer, which the tick does not round. If either handle can't be created it uses round_robin's own wait and wake, so no path sleeps a tick.

Measured in threadpool_test at Windows' default tick:

before after
cycles an idle worker runs in 250 ms 758,090 0
median overrun of a 2 ms timed wait 13.4 ms 0.4 ms

Conflict ahead: #586 (rye/aldb) has its own event-based sleepy_robin, whose deadlines go through millisecond WaitForSingleObject timeouts that the tick rounds, and its own tests/threadpool_test.cpp. Whichever lands second merges the two; this scheduler is the one to keep.

Generated UUIDs are random v4, made on any thread without a lock

LLUUID::generate made a version 1 UUID (the time, a clock sequence and the network card's address) and handed out its MD5. The time and clock sequence lived in statics no lock guarded, so threads generating at once made the same UUID: eight threads making 25,000 each produced only about 140,000 distinct UUIDs out of 200,000, on every run. Its one lock was an LLMutex on a rare branch, which asserted if a coroutine reached it.

A UUID is now 122 random bits with RFC 9562's version 4 and variant, from the system's cryptographic generator (BCryptGenRandom, getrandom, arc4random_buf) through a buffer each thread keeps. uuid_time_t, cmpTime, getSystemTime, getCurrentTime and LLUUID::mMutex are gone. getNodeID stays for the machine ID and hashed unique ID, and bcrypt joins the Windows system libraries.

combine and the name-based generate(string) keep MD5. The server uses combine to make an upload's asset ID, and generate(string)'s callers name files and keys with it.

Testing

🤖 Generated with Claude Code

RyeMutt and others added 2 commits October 27, 2026 22:56
…at a time

Every pool thread on Windows ran sleepy_robin, whose wait was a Sleep(1)
and whose wake did nothing: an idle worker woke every tick of the system
timer to look for work, and picked work up, or came back from a fiber's
timed wait, only at a tick. The viewer's window asks for 1 ms ticks, so
each idle pool thread woke about a thousand times a second; Windows 11
gives a process whose window is hidden the whole 15.6 ms tick whatever it
asked for.

sleepy_robin now waits on an auto-reset event, which a fiber made ready
from another thread sets, and where a fiber's timed wait sets a deadline,
on a high resolution waitable timer, which the tick does not round. Short
of either handle it uses round_robin's own wait and wake, so no path
sleeps a tick. Only a timer that will not be set falls back to a wait the
tick rounds, which a wake still ends at once.

Measured in threadpool_test at Windows' default tick, before and after:
an idle worker ran 758,090 cycles in a quarter second, now 0; a 2 ms
timed wait on a worker overran by a median 13.4 ms, now 0.4 ms.

rye/aldb has an event-based sleepy_robin of its own, whose deadlines go
through millisecond WaitForSingleObject timeouts the tick rounds, and its
own tests/threadpool_test.cpp: whichever lands second merges the two, and
this scheduler is the one to keep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…read without a lock

LLUUID::generate made a version 1 UUID -- the time, a clock sequence and
the network card's address -- and handed out its MD5. The time and the
clock sequence lived in statics no lock guarded, so threads generating at
once read the same tick and made the same UUID: eight threads making
25,000 each made about 140,000 distinct of 200,000, every run. Its one
lock, an LLMutex on the branch where the clock had not moved, asserted if
a coroutine reached it, and was made lazily on whichever thread got there
first.

A UUID is now 122 random bits with RFC 9562's version 4 and variant, from
the system's cryptographic generator (BCryptGenRandom, getrandom,
arc4random_buf) through a buffer each thread keeps: nothing shared between
threads, and a call into the system for every sixteen UUIDs. uuid_time_t,
cmpTime, getSystemTime, getCurrentTime and LLUUID::mMutex are gone; the
machine ID and the hashed unique ID still read getNodeID. bcrypt joins the
Windows system libraries.

combine and the name-based generate(string) keep their MD5: combine is
what the server makes an upload's asset ID with, and every caller of
generate(string) names files or keys from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 48 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: bd02341e-2cca-4236-ad7f-0a82d9029749
Commits

Reviewing files that changed from the base of the PR and between e9d2331 and 9ebf45f.

Files selected for processing: 7
  • indra/cmake/Linking.cmake
  • indra/llcommon/CMakeLists.txt
  • indra/llcommon/lluuid.cpp
  • indra/llcommon/lluuid.h
  • indra/llcommon/tests/lluuid_test.cpp
  • indra/llcommon/tests/threadpool_test.cpp
  • indra/llcommon/threadpool.cpp
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@RyeMutt
RyeMutt marked this pull request as ready for review October 28, 2026 02:59
@RyeMutt
RyeMutt merged commit 9acfd01 into develop Oct 28, 2026
17 checks passed
@RyeMutt
RyeMutt deleted the rye/pool-scheduler-uuid branch October 28, 2026 03:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant