The wait for Konsole and its shell polls ps, each with a 30 s timeout, and
any failed poll ended the whole wait as a failure. On a busy single-thread page one
ps took over 30 s with Konsole and its shell already up, and the test failed
about 96 s into a 300 s wait. Now such a poll is not yet, and the number of them
goes into the result's times.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
The background download was one request from the first frame not held to
the bundle's end. It added that whole span to its total again on every
retry and after every pause (a hidden tab), so the page showed hundreds of
MB. It also fetched again any frame that a read, or an earlier visit, had
already brought in after that point.
Now each request is a window of up to 4 MiB over a run of frames that are
neither held nor in flight. What is held is checked again before every
window. A frame cut in the middle is taken up from its next byte. A read's
frame the download has not reached yet still goes out at once in its own
request. The total is the compressed bytes of the frames this visit did not
hold when the download began. Done is the part of them held by now, however
it came. A frame whose store fails (the origin's quota) is held packed for
the visit instead of being fetched again.
Test: page/lazy-rest-ahead logs every byte the server sends and checks it
against the bundle: a pause, a cut window, a read near the end at 10%, a
cut first visit then a second, a cache that refuses every frame.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A gexcc-win program checks that CreateFiber and CreateFiberEx refuse a
native start routine with ERROR_NOT_SUPPORTED and that SwitchToFiber
called from native code ends the process with a message. fibers.c also
runs as an x86-64 program, so guest fibers are checked on both sides.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A locked access across two 8-byte words ran under the global split lock
and stored its value with a plain store. The lock keeps such accesses
atomic against each other, but a store of the value's bytes undid any
aligned atomic another thread made in between to a field overlapping
them. That field may be native code's own, such as kernel32's
InterlockedIncrement on the counter beside.
Both copies now commit through one compare-exchange per word: cgen's
gex_locked_misaligned in the header, and tier-1's emit_misaligned_atomic.
- The first word is written when it is unchanged since the read; if it
changed, the access starts again.
- The second word retries with whatever the bytes around the value now
hold.
- If the value's own bytes in the second word changed, the access puts
the first word's bytes back while they still hold what it wrote, and
starts again.
Every split access therefore reads and writes the value atomically
against aligned atomics on either word as well.
wow64/splitlock gains a 32-bit "overlap" case.
- One thread makes 1000000 locked increments, in its own code, of a
counter at 6 mod 8.
- The other thread calls InterlockedIncrement, the whole time, on the
aligned counter at 8, which overlaps the first counter's upper half.
- The case passes only when both counts are exact.
- Natively, the old code lost 20310 of the other thread's 327824 rounds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
aconv.c, a library of every argument and result shape, built as an LP64
wasm library (gen.py's adapters for its exports, the GexA64Icall pass's
entry thunks for the pointers it hands out) and as the arm64 library; an
arm64 program checks each through both paths, and a callback of its own,
against the same functions compiled in: translated and replaced.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
Guest x86-64 code calling a native DLL's functions through their table
indices (the entry path) and an export, with guest-to-guest and
native-to-native references, natively and on the page: each kind's ns
per call in the result's times. A timed test, for bench.sh.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
wine64/guestnative and wine64-page/guestnative run fixtures/wine64/fibcb.c
beside convdll: a callback that switches to another fiber and back, and
callbacks nested across two fibers, 2000 times each; then the two calls
returning in the other order (not last in, first out), which may end
the process but must not hang (nt's `code: null`: any exit).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A guest call from native Wine that names no argument bytes counted as
left only with the stack more than NATIVE_UNWOUND (512 bytes) above its
frame, and gexrt64 asked gex to leave it (NATIVE_LEFT) only past the
same margin. A jump out of the callback to the guest frame that called
the native function lands closer than that when the native frames
between are small, so each such jump held one more call-out open: the
page's host stack ran out within 3000 jumps, and natively the call-outs
nested hundreds deep.
A call-out whose stack the TEB names (callout_stack_top) is now left by
any stack pointer above its frame on that stack, on both hosts, and
gexrt64 asks at any such entry (Wine pin). The return to the pad still
matches within NATIVE_UNWOUND. cbthrow passes on the page as well;
100000 jumps natively and on the page keep at most a few call-outs open.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A call-out's caller counted as unwound past when the guest's stack
pointer was above its frame. A fiber has a stack of its own: while a
callback's call-out was open and another fiber ran on a stack above it,
a safepoint (or gexrt's leave check, or a change of mode on the page)
abandoned the call-out, and the callback's return later reached native
Wine's return pad with no call-out catching it ("gex: BUG: a guest
return reached native Wine's return pad").
Each call-out now records the top of the stack it was made on, its
thread's TEB StackBase then (a fiber switch changes it), and only a
stack pointer between its frame and that top says the caller has been
left: x86::callout_stack_top, the native host's Callout::left, and on
the page gex_callout_stack_top, #left and gex_callout_guard's new top.
A call-out whose TEB names no stack holding its frame keeps the old
rule.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
64-bit Wine's gexrt kept a call-out's record on the stack just above the
callee's frame. Guest code jumping out of a callback to a frame less than
NATIVE_UNWOUND above it reused that stack, and the next entry read the
record from it: two such jumps in a row hung the thread. The records are
now in an array the Unix side allocates with each TEB, so wine64's
guestnative cbthrow (4000 jumps out, one and two native calls deep) passes
natively. On the page it stays a known failure, now a host stack overflow:
each such jump leaves its call-out running, one more host level per jump,
and a Worker's stack holds fewer than 4000.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
wine64/guestnative (and wine64-page/guestnative on the native PE core):
convdll.c, a native DLL built with gexcc-win, called from MinGW programs
through the pointers it hands out (its entry path) and its exports:
- convmatrix: every Win64 shape against the same functions compiled as x86;
- lateload: the DLL loaded while a thread spins with no system call, called
from that thread, unloaded, loaded and called again;
- cbthrow: a guest callback jumping out over the native frames, many times
(a known failure today: gexrt's call-out records on the stack);
- comvt: Wine's own native functions through COM and C++ vtables and
exports, also as i386 (wow64/comvt).
winerun's nt() takes `known`, for a run whose failure is a known one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A process's exit writes a generation of the kept root when anything is
waiting (persist::exited, core/fs-persist-exit), so a dd that ended had
already sent its blocks to the store: "stays in the cache" saw them there,
and sync, syncfs and sys.sync() found nothing left to write. The writes
the cache checks look at (the first one, the ones before sync, syncfs and
sys.sync(), and phase 2's write-back age) now come from bash's own printf
in a shell that runs for the whole phase. fsync, fdatasync, the 4 MiB
write, the failing store and the full store keep their own processes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
The run is also the prefix's first start (wineboot --init, its services,
their drivers' units). On one core with no units built that alone takes
89 s, so a loaded gate host ran past the 120 s the test allowed. The
reservation itself is refused at once.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
The lifter implements all of them now and matches a real CPU on
x86-programs' sse4, so gex claims x86-64-v2 and programs' IFUNCs and
feature checks may pick those code paths. glibc_math.c's note said the
C rounding variants were chosen; the provider binds the resolvers, so
it replaces whichever variant would be chosen.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
phminposuw, dpps, dppd, mpsadbw, crc32 (all widths), the four
pcmpXstrX with every immediate (formats, aggregations, polarities,
index and mask forms, the flags) and pclmulqdq were SIGILL. They are
lifted now, in sse/sse4.rs. dpps and dppd sum in the hardware's order
and keep the NaN an x86 add keeps (the first operand's, quieted),
spelled out so the function tier's C gives the same NaN. pcmpestrX
take their lengths from RAX and RDX under REX.W, EAX and EDX
otherwise.
test/fixtures/x86-programs/sse4.c runs SSSE3, SSE4.1, SSE4.2 and
PCLMULQDQ over pseudo-random inputs and every immediate, and compares
a hash per group with the one a real CPU gave, so the native and page
legs check it with no dependency on the host's CPU (programs.mjs's
SELF). The native legs take GEX_TEST_ONLY as the page legs do.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A `-import` export of a DLL the module only delay-imports (imagehlp's
SymSetOptions, dbghelp's) was bound to gexp__missing: gex binds a DLL's
exports when its image maps, and the owner's module, marked delayed, was
not installed by then. Nothing would load it later either, since the
export's callers never go through the module's delay thunks; a program's
crash handler calling it stopped the process ("an export of a replaced
library that its provider does not implement"). pelink now has such an
owner load with the module, as for a delay import it binds directly.
Test: wow64/reexport (64-bit and 32-bit, natively).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
When a process ends having left the tree changed or a block dirty since the
last generation, the kept root writes one before its parent hears of the
exit. An installer's files were kept only after the write-back's age or a
sync; a page reloaded or closed just after the installer ended lost them.
As on Windows, a program's files are on disk once it has exited.
Tests: persist's an_exit_keeps_what_was_written (ignored set), and
core/fs-persist-exit: an installer-like process writes a large file and
small ones and exits, the page reloads at once with its own flush disabled
and no tick due, and the next load has every byte (it fails without this).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
The browser compositor tells each client's keyboard repeat_info rate 0 and
passes each of the browser's own repeat keydowns on as a press. A client's
repeat timer ran from the press it saw, so when the page's main thread was
busy and handed the release on late, the client repeated a key the user
had already let go of (a command typed into cmd just after a reload came
out garbled). gexc's compositor and the X server's hostwl keep 25/600.
Tests: windows (rt-wl is told rate 0; a key held through a 900 ms
main-thread stall is one press; a held key gives one press per browser
repeat), x11-input (the same through Xwayland, the release after the
repeats), kde (typed into Konsole: a command runs, the stall gives one key,
a held key repeats).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
gex_window_placement_v1.grab and release_grab: while a placed surface's
client holds a grab, every pointer event goes to that surface wherever
the pointer is, other clients' surfaces included, as an X server grab
does. A Win32 menu loop under Wine takes one (winewayland's SetCapture,
as winex11's XGrabPointer), so a click on another program's window or on
the desktop reaches the loop and closes the menu; before, the click went
to the other program and the menu stayed open over it.
The grab rides in the surface record's flags (bit 2). The scene
compositor, the element layout and the native compositor route to the
topmost grabbing surface. windows/desktop-layer checks it with a third
rt-wl that grabs: a press over the desktop and over the other window
reaches it, outside it, until it is gone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A program's read of a lazy file in the rest bundle (BUNDLE#OFFSET) was
looked up without its offset, so the page said "Loading rest.bin" for
every such file. The offset is now part of the lookup.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
The page names its rest bundle to the lazy-file owner as a path on its
origin; a read names it by its whole URL. The owner kept two indexes, and a
read missed the frames held from the download while their store was in
flight. The bundles the page names are made whole first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
An ejected page starts the lazy-file owner itself and hands it to the
System it runs the program in. The System made another one anyway and gave
every Worker that one, so the page's owner (whose cache the rest bundle is
downloaded ahead into) read nothing, the reads went to a second cache, and
each frame was fetched again the first time a program read it. A lazyCache
in the host preset is now the owner.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
The background download of a rest bundle starts once the pieces (or the streamed archive) are in, not
once the start-up is over, and for a bundle with a hash in a page cache that holds it is one request at
low priority from the first frame not held yet to the end, each frame stored as it completes: a later
visit downloads none of it, and one cut short (or paused while the page is hidden) is taken up from
where it stopped. A read of a frame the download has not reached still goes out at once in its own
ranged request. The page's progress carries it as `rest` ({done, total, complete}) for a page to show.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A skeleton's .wasmref names the module as the link wrote it (zed.exe.wasm);
an installer may give the files other case (Zed.exe, Zed.exe.wasm), and
the exact lookup on a case-sensitive filesystem then failed with ENOENT
when the program started. Both hosts take the exact name first, then the
directory's entry that differs from it only in case.
Test: modcase (gexcc-win, natively and on the page), a program and its
module installed as ModCase.exe and ModCase.exe.wasm.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
The system font set's Tahoma asked for bold has the bold face first,
and a set of a font file in memory (CreateInMemoryFontFileLoader,
AddFontFile), as programs that ship their own fonts build, finds it by
its family. bigdata's main takes argc and argv, as the CRT's call has
it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
wasmtime compiles a position-independent module's active element segment
(at global.get __table_base) into its startup code, one table store per
slot; a program's million slots took register allocation hours. A
segment of over 16384 slots is now made passive and filled by one
table.init at the head of the start function, in memory, for wasmtime's
compile only: the cache keys and the shipped module are unchanged (V8
fills an active segment lazily, and a table.init eagerly). Measured, the
rewrite compiles a 64k-slot segment in 1.2 s against 4.0 s.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A 64-bit module's data went in the PE data window above the images,
which keeps a data export's RVA from its skeleton positive and under
4 GiB. A module that exports no data (a gexcc-win program's) has no
such RVA, and its data may be larger than the window (16 MiB in the
compact space). It now maps below 4 GiB as an i386 module's does, as
natively. bigdata: a program with 24 MiB of data, in the page pe list.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A gexcc-win program has no core beside it, so its module installs as the
process's core, before ntdll's skeleton maps; the core installing then
replaced it, and image_of no longer found it: GetModuleHandleExW's
FROM_ADDRESS on the program's own data or functions failed (Zed's window
creation). It now stays with the modules of their own.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
wasm-ld's __wasm_apply_data_relocs has one store for each address in a
module's data, and a large program's passes the 7654321-byte limit a
function body has in V8 and wasmparser, so the module fails to load. A
straight-line function of stores over 1 MiB is split into functions of
its statements, appended after the others, which it calls in order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
The ICALL pass's signatures are link-time input: winebuild reads them from
the objects, pelink from the .r.o and the defining DLL's side module.
wasm-ld concatenated every object's into the module, and wasm-opt kept
them, though gex never reads them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
With no archs and no --enable-win64, Wine's configure builds the tools
-m32, and a large program's link runs winebuild out of its 4 GiB. The
tools tree's key includes the flavour, so the change rebuilds it once.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
--stub-imports (gexcc-win's link of a program): each function the program
imports that no module exports gets a definition in the program's module
that raises EXCEPTION_WINE_STUB with the DLL whose .spec lists it, as
Wine's stub entry points do, and the link lists them as warnings. The
program runs; a call ends it with Wine's unimplemented-function error.
The core and the tree's DLLs stay checked.
The core's gap stubs stay as they are: on wasm64 pelink reads the linked
module's imports with wasm-objdump, which cannot read a memory64 module,
so it found none.
Test: wine64/gexccwin's missimp.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
An exception handler jumps back to main without unwinding, its stack is
written over, and a second exception is raised and handled; then the same
from a fiber and the thread between switches. Fails until an entry into
64-bit Wine drops the exception frames it pushed on its own stack.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A critical section, an SRW lock and an init-once at addresses that are not
a multiple of their fields' size, contended by two threads in Wine's native
wasm code, and (32-bit) translated locked increments racing kernel32's
InterlockedIncrement on one counter. 64-bit and 32-bit, natively and on the
page; fails until Wine's interlocked operations take the split path at
misaligned addresses.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_853378b774d645b4ac78b16d9af91a31
A window system's own desktop window -- Wine's virtual desktop, which draws
the background and the desktop's icons and takes the clicks on them -- needs
to be shown, and shown under every other window, placed or not: in the
normal layer a window sent to the bottom (place_below 0xffffffff) would go
under it.
gex_window_placement_v1 version 3 adds the layer "desktop" (3). The server
keeps it only for a client that bound version 3; an older client's layer is
clamped as before. Every compositor stacks it lowest: the native screen
(drawn before the windows nobody placed, and hit-tested under the rest), the
scene compositor and the page's element layout. The scene compositor's
keyboard fallback now takes the top of the stacking, not of the raw order.
rt-wl places itself with "place X Y LAYER [bottom]" and says where each press
landed; windows/desktop-layer (scene) and windows/desktop-layer-single (the
element layout) check that the window sent to the bottom stays over the
desktop and that presses reach each, and a native screen test the same.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_e0aa53aa95eb4b86910519b4d14812a5
The lazy files' owner fetches every frame of the site's rest bundles it does not hold yet, one at a
time at fetch priority low, into the store reads look in (the page's cache for a bundle with a hash,
the kept frames otherwise). A read's own fetch goes first: none of the background's starts while one
is in flight, and a frame both want is one request. Paused while the page is hidden; none on a
save-data or 2G connection; ?restahead=0 turns it off.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_e0aa53aa95eb4b86910519b4d14812a5