gluon is an esync/fsync/NTSync-class synchronization backend for Wine on
macOS (Apple Silicon). It routes NT sync primitives — events, semaphores,
mutexes — through shared memory + os_sync_wait_on_address instead of
a wineserver RPC, and falls back to wineserver for everything it doesn't
own. Turn it on with WINEGLUON=1.
Every NT wait/signal in stock Wine is an RPC the single-threaded wineserver must service. Under sync-heavy load that pins a CPU core and serializes the whole prefix. gluon moves those ops into shared memory, so they never reach the server.
Real AAA benchmark — Unigine Superposition 1.1 (DirectX 11), 30 s steady-state render window:
| WINEGLUON | wineserver CPU | as % of a core |
|---|---|---|
| off | 10.32 CPU-s | ~34% |
| on | 0.04 CPU-s | ~0.1% |
712,000 sync waits over ~85 s were handled client-side; with gluon off
those are all wineserver RPCs. Superposition renders through
D3D11 → wined3d's Vulkan backend → MoltenVK → Metal — no DXVK needed.
Sync-bound micro-benchmark (job-system frame loop):
| workers | wineserver CPU off → on | throughput off → on |
|---|---|---|
| 2 | 65% → 2.5% | 17k → 690k fps (40×) |
| 8 | 66% → 0.0% | 5.1k → 28k fps (5.5×) |
| 16 | 66% → 0.2% | 2.5k → 18k fps (7×) |
Uncontended op cost (the hot path), standalone PoC on the M5: set_event 4 ns · signaled wait 4 ns · sem acquire+release 8 ns · mutex acquire+release 9 ns — vs ~2.8 µs for the socket RPC gluon replaces.
GPU-bound fps itself is unchanged (sync isn't the frame limiter — the honest control); the win is the wineserver-CPU headroom, exactly what esync/fsync deliver. Full methodology and every raw number: docs/BENCHMARKS.md.
one shared-memory region per Wine prefix (/wine-<inode>-gluon)
┌───────────────────────────────────────────────────────────────┐
│ header magic │
│ channels[8192] per thread: { seq (sleep word), sleeping, │
│ apc_count, owned-mutex list } │
│ nodes[8192*64] intrusive wait-list nodes (one set / channel) │
│ objects[65536] { type, state, extra, waiter-list head, ... } │
└───────────────────────────────────────────────────────────────┘
wineserver (server/gluon.c) ntdll client (dlls/ntdll/unix/gluon.c)
───────────────────────────── ──────────────────────────────────────
owns naming, handles, lifetime, hot path, no server round trip:
slot + channel allocation, - create/open → server assigns a slot,
death cleanup (abandonment walk returns its shm index with the handle
at kill_thread), and the wake - set/reset/release → atomics on the slot
bridge for server-side waits - wait → try-acquire (lock-free), else
register on each object's waiter list,
recheck, sleep on the channel's seq via
os_sync_wait_on_address; signalers poke
the channel (skipping the syscall if the
waiter is still spinning)
Key properties (all validated — see below):
- Uncontended ops make zero syscalls. That is where the CPU savings come from.
- Server owns lifetime. The NT handle is identity; the server holds the slot until the last handle closes, so the client cache needs no refcount and no generation check — the PoC's per-op pinning is dropped at the Wine boundary.
- Honest fallback. Any handle gluon doesn't own (files, processes,
threads, message queues, keyed events, …) makes the whole wait fall
back to
server_select, with a+gluonTRACE. The single-threaded server can still wait on gluon objects via a wake bridge. - NT semantics parity. wait-all (all-or-nothing with rollback), mutex
abandonment (
WAIT_ABANDONEDon owner death, incl.kill -9across processes), alertable waits / APCs, and NT status codes. - Graceful table exhaustion. When the 64k-slot table fills, creates transparently fall back to normal server objects — no spurious out-of-resources failure.
- winetest conformance:
kernel32:sync,ntdll:sync,ntdll:omshow zero gluon-only failures vs the server baseline (getting there fixed four real bugs — see wine-tests/README.md §2 and patch 0005). - kill -9 abandonment end-to-end: a process holding a named mutex is
kill -9'd, another process's waiter getsWAIT_ABANDONED— including recursive-ownership and wait-all variants, and an off-mode control (wine-tests/run.sh). wine64 notepadandwinecfgrun cleanly underWINEGLUON=1, behavior identical with it off.- Standalone PoC: correctness suite + a 10M-op randomized stress test, ThreadSanitizer-clean, on macOS arm64 and Linux.
patches/ the release. 0000 = macOS-26/Xcode-26 build prep for the
CrossOver 22.1.1 tree; 0001–0007 = the gluon feature as a
logically-ordered series (LGPL-2.1, matching Wine). See
patches/README.md for the apply-and-build recipe.
poc/ standalone C11 reference implementation of the core
(gluon.h/.c) + its tests and benchmark. MIT-licensed.
docs/ BUILD.md — build Wine + the graphics stack on Apple Silicon
INTEGRATION.md — the esync→gluon design map (hook sites,
protocol, fallback matrix, death cleanup)
BENCHMARKS.md — full results + methodology
PLAN.md — the phased project plan
wine-tests/ the PE test programs and harness scripts (abandonment,
conformance diff, sync-load + GPU benchmarks).
patches/(the Wine backend): LGPL-2.1-or-later, matching Wine. Every new file carries the LGPL header; modified files keep Wine's.poc/(the standalone core): MIT (poc/LICENSE).
# 1. reproduce the buildable baseline and apply gluon (see patches/README.md)
# 2. build wine64 (see docs/BUILD.md §3)
# 3. run something:
WINEGLUON=1 ./loader/wine64 notepad
# 4. see the effect:
WINEGLUON=1 WINEDEBUG=+gluon ./loader/wine64 your.exe 2>&1 | grep -c 'waiting for'Requires macOS 14.4+ (for os_sync_wait_on_address); gluon compiles out
on other platforms. Built and measured on macOS 26 / Apple M5.
Issues and test reports are welcome — especially from M1 / M2 / M3
Macs. Everything here was built and measured on an M5; confirming the
wineserver-CPU win (and shaking out any wake-channel / os_sync timing
differences) on earlier Apple Silicon would be genuinely useful. Please
include your macOS version, chip, WINEDEBUG=+gluon fallback counts, and
the workload.
Roadmap, roughly in priority order:
- Rebase onto modern Wine with new-style wow64 (
--enable-archs=i386,x86_64). This tree is win64-only, so 32-bit titles (e.g. Unigine Heaven) can't run at all. wow64 unlocks the large back catalogue of 32-bit games and is the single biggest capability gain. - Server-bound wait acceleration (queue / thread / process handles).
These currently fall back to
server_selectby design (INTEGRATION.md D3); routing them through gluon's shared-memory channels would move the remaining steady-state waits — and the message-loop path — off the server too. - Port to current CrossOver / Kegworks trees. The patches target CrossOver 22.1.1 / Wine 7.7; forward-porting to the Wine base those ship today would make gluon usable in the stacks most people actually run, alongside their D3DMetal/DXVK graphics translation.
See docs/INTEGRATION.md for the design map and the open decisions each of these touches.