Skip to content

Locks follow one rule: LLCoros where a coroutine reaches, LLMutex where only threads do - #591

Merged
RyeMutt merged 1 commit into
developfrom
rye/lock-types
Oct 27, 2026
Merged

RyeMutt merged 1 commit into
developfrom
rye/lock-types

Conversation

@RyeMutt

@RyeMutt RyeMutt commented Oct 27, 2026

Copy link
Copy Markdown
Member

Every std::mutex, std::condition_variable and std::shared_mutex now takes the type its takers call for, instead of being picked site by site.

The rule

  • LLCoros::Mutex / LLCoros::ConditionVariable where main-thread code that a coroutine can call takes the lock: llcorehttp's request, reply and handle locks and HTTPStats; Script Studio's save history, recovery store, master index, job and analysis lanes and caches; text find and search; the syntax table names; parameter blocks and ALParamTypes; process launch; LLVOAvatarSelf's joint map. A coroutine that has to wait parks alone, and one that suspends while holding the lock no longer leaves the next coroutine waiting on its own thread.
  • LLMutex / LLCondition where only threads the code owns, or the main loop's own frame path, take it: the watchdog's timeout, LLPerfStats, the published joint numbers, the disabled GL work queue, the LSL reference compiler, test fixtures. LLMutex's assert catches a coroutine that reaches one.
  • std stays below the coroutine layer (LLSingleton, llerror and the log recorders, LLInstanceTracker, lockstatic, llthread's registry, llmutex itself) and in code that does not link llcommon (llwebrtc, dullahan).

ALSharedMutex

LLSharedMutex's writer-preferring lock, now a class of its own (alsharedmutex.h) that neither side may re-enter. LLSharedMutex is built on it, and LLStringTable takes it in place of std::shared_mutex. Under glibc, std::shared_mutex let a reader that arrived while a writer waited in beside the first reader.

Races fixed on the way

  • llprocess marked its job object made before it made it, so a second launch could read the handle unset or half configured. It is now made first, then marked with a release store that the unlocked check reads with acquire.
  • The LSL service handed out its builtin names by reference to lists it cleared and refilled under a lock the caller no longer held. It now hands out shared lists, made anew when the table changes.

Left on std

The Linux PipeWire volume catcher: its cleanup() locks the child node mutex again through ChildNode::destroy() while it walks the set that destroy() erases from. The recursive LLMutex would turn that hang into a walk over a changing set; it needs its own fix.

Testing

  • Windows, vs2026-os RelWithDebInfo with tests on: the llcommon, llcorehttp, alscript, llui and newview test binaries and the viewer built with no warnings; 308/308 of those tests pass, plus the Python-peer llcommon tests (llleap, llprocess). alsharedmutex_test and llmutex_test passed 25 and 10 runs in random order.
  • alsharedmutex_test's late-reader case was run under glibc against std::shared_mutex (fails) and ALSharedMutex (passes).
  • The disabled GL work queue was compiled once with ENABLE_GL_WORK_QUEUE set.
  • The viewer was run in world.

Not built here: macOS, Linux, and lscript/allslreference.cpp (needs bison and flex).

🤖 Generated with Claude Code

…re only threads do

std::mutex, std::condition_variable and std::shared_mutex were picked site
by site. Each lock now takes the type its takers call for:

- A lock that main-thread code a coroutine can call takes -- llcorehttp's
  request, reply and handle locks and its stats, Script Studio's save
  history, recovery store, master index, job and analysis lanes and caches,
  text find and search, the syntax table names, parameter blocks, process
  launch, the avatar's joint map -- is an LLCoros::Mutex, with
  LLCoros::ConditionVariable where it is waited on. A coroutine that has to
  wait parks alone, and one that suspends holding it no longer leaves the
  next coroutine on the main thread waiting on its own thread.
- A lock taken only by threads the code owns, or by the main loop's own
  frame path -- the watchdog's timeout, perf stats, the published joint
  numbers, the disabled GL work queue, the LSL reference compiler, the test
  fixtures -- is an LLMutex or LLCondition, whose assert catches a
  coroutine that reaches it.
- std stays below the coroutine layer (LLSingleton, llerror and the log
  recorders, LLInstanceTracker, lockstatic, llthread's registry, llmutex
  itself) and in code that does not link llcommon (llwebrtc, dullahan).

ALSharedMutex is LLSharedMutex's writer-preferring lock on its own, which
neither side may re-enter; LLSharedMutex is built on it. LLStringTable takes
it in place of std::shared_mutex, which under glibc let a reader that came
while a writer waited in beside the first.

Two races met on the way are gone. llprocess marked its job object made
before it made it, so a second launch could read the handle unset or half
configured; it is now made first, then marked with a release store that the
unlocked check reads with acquire. The LSL service handed out its builtin
names by reference to lists it cleared and refilled under a lock the caller
no longer held; it hands out shared lists, made anew when the table changes.

The Linux volume catcher keeps std::mutex: its cleanup() locks the child
node mutex again through ChildNode::destroy() while it walks the set that
destroy() erases from, which the recursive LLMutex would turn from a hang
into a walk over a changing set.

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

coderabbitai Bot commented Oct 27, 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 9 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: e2ab01e4-7d60-48f8-ab19-93f17eec9fe3
Commits

Reviewing files that changed from the base of the PR and between c32de66 and 35fc15e.

Files selected for processing: 38
  • indra/alscript/lsl/allslservice.cpp
  • indra/alscript/luau/alluauconfigscript.cpp
  • indra/alscript/luau/alluauexports.cpp
  • indra/alscript/masters/almasterindex.cpp
  • indra/alscript/preprocessor/alscriptjoblane.h
  • indra/llcommon/CMakeLists.txt
  • indra/llcommon/alparamtype.cpp
  • indra/llcommon/alrecoverystore.cpp
  • indra/llcommon/alsavehistory.cpp
  • indra/llcommon/alsavehistory.h
  • indra/llcommon/alsharedmutex.h
  • indra/llcommon/llinitparam.cpp
  • indra/llcommon/llmutex.h
  • indra/llcommon/llprocess.cpp
  • indra/llcommon/llstringtable.cpp
  • indra/llcommon/llstringtable.h
  • indra/llcommon/llwatchdog.cpp
  • indra/llcommon/llwatchdog.h
  • indra/llcommon/tests/alserialworker_test.cpp
  • indra/llcommon/tests/alsharedmutex_test.cpp
  • indra/llcommon/tests/llmainthreadtask_test.cpp
  • indra/llcorehttp/_mutex.h
  • indra/llcorehttp/httpstats.cpp
  • indra/llcorehttp/httpstats.h
  • indra/llrender/llvertexbuffer.cpp
  • indra/llui/code/alsyntaxgrammar.cpp
  • indra/llui/text/altextfind.cpp
  • indra/llui/text/altextsearch.cpp
  • indra/lscript/allslreference.cpp
  • indra/newview/alscriptanalysis.cpp
  • indra/newview/alscriptanalysislane.cpp
  • indra/newview/alscriptanalysislane.h
  • indra/newview/llperfstats.cpp
  • indra/newview/llperfstats.h
  • indra/newview/llskinningutil.cpp
  • indra/newview/llvoavatarself.cpp
  • indra/newview/llvoavatarself.h
  • indra/newview/tests/alscriptanalysislane_test.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 27, 2026 10:20
@RyeMutt
RyeMutt merged commit 78b0204 into develop Oct 27, 2026
23 checks passed
@RyeMutt
RyeMutt deleted the rye/lock-types branch October 27, 2026 19:51
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