Repository navigation
feat(computers): agent-env's libfaketime for the computer's clock, gated on x86_64 and arm64 (FD-5474) - #619
Open
earakely-scale wants to merge 5 commits into
Open
earakely-scale wants to merge 5 commits into
earakely-scale wants to merge 5 commits into
Conversation
…ted on x86_64 and arm64 (FD-5474) src/agent_env/computers/clock is the library every computer's session programs will load to read universe time: upstream libfaketime 0.9.12 from its sha256-pinned tarball with agent-env's changes, as a small libfaketime.patch and two engine files it includes (agentenv_clock_page.c, agentenv_clock_waits.c; GPL-2.0-only, as libfaketime is), built by build.sh, with the page writer the follower uses (clockpage.py) and the library's default settings (clock.conf). Clock reads and waits for a set realtime clock time follow the universe; monotonic and boot clocks and every relative wait stay real; only the session user is faked; settings are read once at load. A clock read now copies the page before reading the real time and keeps the pair only if the page is still the one copied: a reader that waited out a write paired a time from before it with the page it wrote, which stepped CLOCK_REALTIME back by up to ~280 ns at a rate change. tst/clock (make clock-test, .github/workflows/clock.yml on x86_64 and arm64) builds it and stock 0.9.12 in an Ubuntu 24.04 image and gates it: every wait API in C, Python, Java and Node at several rates, a rate change, a set_time back and 30 jumps, condvar and semaphore stress, a stalled writer, torn reads, who is faked, the marker, the environment race and Chromium launched directly 60 times (stock 0.9.12 as the positive control), in under 30 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s by file, a dead page writer (FD-5474) Greptile's review of the clock library: - a condvar wait cancelled mid-wait gives up its table entry (cleanup handler); - sem_timedwait and the universe clock_nanosleep are cancellation points again, and a cancelled semaphore wait leaves the waiter count as it found it; - without futex_waitv (before Linux 5.16, gVisor, a seccomp filter), realtime futex and semaphore waits wait on the word in slices and plan again from the page between them, instead of a deadline translated once or not at all; - a timerfd's record holds its descriptors, kept through dup, dup2, dup3, fcntl, close and close_range; - clockpage.write takes up a page a dead writer left with seq odd; - clock.conf and the README say settings are file-only while the file exists. The gates check every wait's return code, and new gates cover cancellation, timerfd duplicates, a dead writer, and the waits again in containers that refuse futex_waitv. The patch's headers are a/ and b/ again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eep a timerfd's record, Chromium variants side by side (FD-5474) - Without futex_waitv, a slice's end is the only reason to read the page again; any other futex error (EINVAL for an unaligned word) is the caller's, and futex_waitv is taken as missing only on ENOSYS or a seccomp EPERM. - dup2 and dup3 that replace a timerfd, and close_range that closes one, make the call and its bookkeeping under the table lock, so a failed call keeps the record. - The Chromium gate runs its 60 launches with this library and stock's 10 in containers side by side: CI's amd64 runner took 32.6 s in sequence, over the 30 s budget. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s write (FD-5474) The page changes, and waits wake, before the write's last step (the clock file); on CI's amd64 runner that step once took 55 ms, so waits a jump had ended correctly read as 55 ms early against the write's end. A wait a jump or a change ends must now end no earlier than its write started and within its bound of the write's end. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s 30 s on CI's amd64 runner (FD-5474) 60 launches alone took 31.4 s there on one run. 40 clean launches are still conclusive: at stock's one crash in five, a run would come out clean about once in 7,500. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of FD-5420, the computer's own clock following the gateway's universe clock. This is FD-5474: the library every computer's session programs will load to read universe time. Every program on a computer reads its own clock, so it can't ask the gateway's
get_timethe way an MCP server does. Instead, agent-env's build of libfaketime answers those programs' clock reads and clock-time waits from a page a follower process keeps in step with the gateway. Wiring the library into the base computer (/etc/ld.so.preload, the follower, supervisord) is FD-5476.What it is
src/agent_env/computers/clock/:libfaketime.patchsrc/, applied withpatch -p1agentenv_clock_page.clibfaketime.cagentenv_clock_waits.cCOPYINGbuild.shlibfaketimeMT.so.1from upstream's sha256-pinned tarball and the files aboveclockpage.pyclock.confWhat the library does:
CLOCK_REALTIME,_COARSE,_ALARM, TAI,gettimeofday,time) and waits for a set realtime clock time (clock_nanosleep TIMER_ABSTIME, realtime futex waits,sem_timedwaitandsem_clockwait, realtime condvars, absolute realtime timerfds and POSIX timers). Each wait is planned in real time and planned again whenever the page changes.futex_waitv(before 5.16, or gVisor), realtime futex and semaphore waits read the page between 100 ms slices instead.dup,dup2,dup3,fcntl,closeandclose_rangeare interposed), not by its number.AGENTENV_REAL_TIME=1stays real, and its children are faked./etc/agentenv/clock.conf, or from the environment where there's no such file. While the file exists, the environment changes none of them, so a session program can't point the library at a page of its own. Stock libfaketime'sgetenvraced programs' ownsetenv; that crashed Chromium on about one launch in five and hangs it outright in headless mode.The patch and the engine files are the prototype's library, moved out of the two Python scripts that applied it as string replacements (about 900 lines of C inside a Python string). Their preprocessed source was byte-identical to the prototype's before the fix below.
A bug the gates found, fixed here
The gates found it on arm64: with the follower stalled 5 ms in a write,
CLOCK_REALTIMEstepped back up to 280 ns at a rate change, 3 to 7 times per 2,000 writes.ft_snap_at). The real time is ordered before that check: a dependent load anddmb ishldon aarch64,lfenceon x86_64.Greptile's first review, fixed here
All seven findings were real; each has a gate now.
sem_timedwaitused raw futex calls. It now checks for cancellation first, cancels asynchronously for the system call alone, and a cleanup handler takes the thread back off the waiter count.clock_nanosleep's universe sleep does the same.futex_waitv: the fallback translated a deadline once, or not at all, and stopped watching the page. It now waits on the caller's word in slices of at most 100 ms and plans again from the page between them. Modal runs sandboxes on gVisor, so this path is live there.dupandclose, the helper re-armed a closed number or another timer. Records now hold the timer's descriptors.seqodd, and every later write stayed odd.clockpage.writenow goes on from the next even value and skips the continuity rule for that write.clock.confsaid the environment could override the file. It can't, on purpose, and the docs now say so.Greptile's second review (4/5) found two more, also fixed:
dup2,dup3orclose_rangethat failed still dropped the target timer's record. Calls that replace or close a timerfd now run with their bookkeeping under the table lock, so a failed one keeps the record.futex_waitv, a futex error such as EINVAL for an unaligned word was retried as a page wake, spinning a core. Such errors now go back to the caller.futex_waitvis taken as missing only on ENOSYS, or EPERM from a seccomp filter.The patch's headers are back to
a/andb/without timestamps; a regeneration on macOS had leftpa/andpb/in them.Where it lives
It's at
computers/clock/, beside the bundle's other shared recipes (disk/,vm_bundle/), not atartifacts/ubuntu-24.04/clock/as the issue said. Inside the base computer's folder it would change the base's build context, and with it the image's tag, though nothing loads the library yet. FD-5476 brings it into the base.Licensing
The patch and the two engine files are derived from libfaketime, so they carry
GPL-2.0-onlyand upstream'sCOPYINGsits beside them. Everything else in the folder and the repo stays Apache-2.0, and the package's license metadata is unchanged.THIRD_PARTY_NOTICES.mdgets a libfaketime section. I also changed its opening sentence, which said no third-party source is incorporated in the repository; that's no longer true for these three files. That sentence is legal wording, so please check the change. Whether the wheel's license metadata should also list GPL-2.0 for these files is a question for legal.Tests
tst/clock(make clock-test,.github/workflows/clock.ymlonubuntu-24.04andubuntu-24.04-arm, not a required check):browsersbundle's pin, read fromchromium.py) and upstream's tarball are fetched once into a cache, checked by sha256, and kept between CI runs.The gates:
futex_waitv: the waits, rate-change, jump, cancellation and semaphore-stress gates run a second time in containers whose seccomp profile answersfutex_waitvwith ENOSYS, as gVisor and kernels before 5.16 do. A probe checks that it does.sem_timedwaitandclock_nanosleepgo at once, and the semaphore keeps no waiter. 600 threads are cancelled inpthread_cond_timedwait, more than the table holds. A condvar wait after them still ends at a jump past its deadline. A realtime futex wait on an unaligned word fails with EINVAL at once.dup,dup2,dup3,fcntl(F_DUPFD_CLOEXEC)andclose_range. A timerfd armed an hour ahead is duplicated and closed, and its number goes to a duplicate of a monotonic timerfd. A 2 h jump fires the duplicate at the jump and leaves the monotonic one alone. A last case makesdup2,dup3andclose_rangecalls on the timerfd fail, and it still fires at the jump.sem_timedwaitposts, with the page changing under them, lose nothing; every handoff is under 2 ms.seqodd is taken up by the next write, and session programs read it.CLOCK_REALTIME: 0 torn reads in about 319 million on arm64.setenvracing clock reads and timed waits, 3 runs of 10 s each: no crash.Results:
clock.ymlincluded. That job passes all 28 tests on amd64 (10 min) and arm64 (9 min 45 s). Before 7690e0d, amd64 failed once on a jump round's measurement (fixed in e193801) and once on the Chromium time (fixed in 7690e0d).Docs
The docs site should cover the computer's clock: what follows universe time and what stays real, who is faked,
AGENTENV_REAL_TIME, andclock.conf. That page belongs with FD-5476, when computers load the library.🤖 Generated with Claude Code


Confidence Score: 5/5
The changes since the previous review appear safe to merge.Summary
Adds the computer clock library, its shared clock page writer, and Docker-based gates for x86_64 and arm64. Loading the library into computers remains a separate change.
Reviews (8) · Last reviewed commit: "test(computers): 40 Chrome launches with..." · Reviewed by Greptile